1 /* 2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 // Native side of the Quartz text pipe, paints on Quartz Surface Datas. 27 // Interesting Docs : /Developer/Documentation/Cocoa/TasksAndConcepts/ProgrammingTopics/FontHandling/FontHandling.html 28 29 #import "sun_awt_SunHints.h" 30 #import "sun_lwawt_macosx_CTextPipe.h" 31 #import "sun_java2d_OSXSurfaceData.h" 32 33 #import <JavaNativeFoundation/JavaNativeFoundation.h> 34 35 #import "CoreTextSupport.h" 36 #import "QuartzSurfaceData.h" 37 #include "AWTStrike.h" 38 39 static const CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 }; 40 41 42 #pragma mark --- CoreText Support --- 43 44 45 // Translates a Unicode into a CGGlyph/CTFontRef pair 46 // Returns the substituted font, and places the appropriate glyph into "glyphRef" 47 CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForUnicode 48 (const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) { 49 CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, charRef, count); 50 if (fallback == NULL) 51 { 52 // use the original font if we somehow got duped into trying to fallback something we can't 53 fallback = (CTFontRef)font->fFont; 54 CFRetain(fallback); 55 } 56 57 CTFontGetGlyphsForCharacters(fallback, charRef, glyphRef, count); 58 return fallback; 59 } 60 61 // Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair 62 // Returns the substituted font, and places the appropriate glyph into "glyph" 63 CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForJavaGlyphCode 64 (const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef) 65 { 66 // negative glyph codes are really unicodes, which were placed there by the mapper 67 // to indicate we should use CoreText to substitute the character 68 if (glyphCode >= 0) 69 { 70 *glyphRef = glyphCode; 71 CFRetain(font->fFont); 72 return (CTFontRef)font->fFont; 73 } 74 75 UTF16Char character = -glyphCode; 76 return JavaCT_CopyCTFallbackFontAndGlyphForUnicode(font, &character, glyphRef, 1); 77 } 78 79 // Breakup a 32 bit unicode value into the component surrogate pairs 80 void JavaCT_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) { 81 int value = uniChar - 0x10000; 82 UTF16Char low_surrogate = (value & 0x3FF) | LO_SURROGATE_START; 83 UTF16Char high_surrogate = (((int)(value & 0xFFC00)) >> 10) | HI_SURROGATE_START; 84 charRef[0] = high_surrogate; 85 charRef[1] = low_surrogate; 86 } 87 88 89 90 /* 91 * Callback for CoreText which uses the CoreTextProviderStruct to feed CT UniChars 92 * We only use it for one-off lines, and don't attempt to fragment our strings 93 */ 94 const UniChar *Java_CTProvider 95 (CFIndex stringIndex, CFIndex *charCount, CFDictionaryRef *attributes, void *refCon) 96 { 97 // if we have a zero length string we can just return NULL for the string 98 // or if the index anything other than 0 we are not using core text 99 // correctly since we only have one run. 100 if (stringIndex != 0) 101 { 102 return NULL; 103 } 104 105 CTS_ProviderStruct *ctps = (CTS_ProviderStruct *)refCon; 106 *charCount = ctps->length; 107 *attributes = ctps->attributes; 108 return ctps->unicodes; 109 } 110 111 112 /* 113 * Gets a Dictionary filled with common details we want to use for CoreText when we are interacting 114 * with it from Java. 115 */ 116 static NSDictionary* ctsDictionaryFor(const NSFont *font, BOOL useFractionalMetrics) 117 { 118 NSNumber *gZeroNumber = [NSNumber numberWithInt:0]; 119 NSNumber *gOneNumber = [NSNumber numberWithInt:1]; 120 121 return [NSDictionary dictionaryWithObjectsAndKeys: 122 font, NSFontAttributeName, 123 gOneNumber, (id)kCTForegroundColorFromContextAttributeName, 124 useFractionalMetrics ? gZeroNumber : gOneNumber, @"CTIntegerMetrics", // force integer hack in CoreText to help with Java's integer assumptions 125 gZeroNumber, NSLigatureAttributeName, 126 gZeroNumber, NSKernAttributeName, 127 nil]; 128 } 129 130 // Itterates though each glyph, and if a transform is present for that glyph, apply it to the CGContext, and strike the glyph. 131 // If there is no per-glyph transform, just strike the glyph. Advances must also be transformed on-the-spot as well. 132 void JavaCT_DrawGlyphVector 133 (const QuartzSDOps *qsdo, const AWTStrike *strike, const BOOL useSubstituion, const int uniChars[], const CGGlyph glyphs[], CGSize advances[], const jint g_gvTXIndicesAsInts[], const jdouble g_gvTransformsAsDoubles[], const CFIndex length) 134 { 135 CGPoint pt = { 0, 0 }; 136 137 // get our baseline transform and font 138 CGContextRef cgRef = qsdo->cgRef; 139 CGAffineTransform ctmText = CGContextGetTextMatrix(cgRef); 140 141 BOOL saved = false; 142 143 CGAffineTransform invTx = CGAffineTransformInvert(strike->fTx); 144 145 NSInteger i; 146 for (i = 0; i < length; i++) 147 { 148 CGGlyph glyph = glyphs[i]; 149 int uniChar = uniChars[i]; 150 // if we found a unichar instead of a glyph code, get the fallback font, 151 // find the glyph code for the fallback font, and set the font on the current context 152 if (uniChar != 0) 153 { 154 CTFontRef fallback; 155 if (uniChar > 0xFFFF) { 156 UTF16Char charRef[2]; 157 JavaCT_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef); 158 CGGlyph glyphTmp[2]; 159 fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2); 160 glyph = glyphTmp[0]; 161 } else { 162 const UTF16Char u = uniChar; 163 fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, &u, (CGGlyph *)&glyph, 1); 164 } 165 if (fallback) { 166 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); 167 CFRelease(fallback); 168 169 if (cgFallback) { 170 if (!saved) { 171 CGContextSaveGState(cgRef); 172 saved = true; 173 } 174 CGContextSetFont(cgRef, cgFallback); 175 CFRelease(cgFallback); 176 } 177 } 178 } else { 179 if (saved) { 180 CGContextRestoreGState(cgRef); 181 saved = false; 182 } 183 } 184 185 // if we have per-glyph transformations 186 int tin = (g_gvTXIndicesAsInts == NULL) ? -1 : (g_gvTXIndicesAsInts[i] - 1) * 6; 187 if (tin < 0) 188 { 189 CGContextShowGlyphsAtPoint(cgRef, pt.x, pt.y, &glyph, 1); 190 } 191 else 192 { 193 CGAffineTransform tx = CGAffineTransformMake( 194 (CGFloat)g_gvTransformsAsDoubles[tin + 0], (CGFloat)g_gvTransformsAsDoubles[tin + 2], 195 (CGFloat)g_gvTransformsAsDoubles[tin + 1], (CGFloat)g_gvTransformsAsDoubles[tin + 3], 196 0, 0); 197 198 CGPoint txOffset = { (CGFloat)g_gvTransformsAsDoubles[tin + 4], (CGFloat)g_gvTransformsAsDoubles[tin + 5] }; 199 200 txOffset = CGPointApplyAffineTransform(txOffset, invTx); 201 202 // apply the transform, strike the glyph, can change the transform back 203 CGContextSetTextMatrix(cgRef, CGAffineTransformConcat(ctmText, tx)); 204 CGContextShowGlyphsAtPoint(cgRef, txOffset.x + pt.x, txOffset.y + pt.y, &glyph, 1); 205 CGContextSetTextMatrix(cgRef, ctmText); 206 207 // transform the measured advance for this strike 208 advances[i] = CGSizeApplyAffineTransform(advances[i], tx); 209 advances[i].width += txOffset.x; 210 advances[i].height += txOffset.y; 211 } 212 213 // move our next x,y 214 pt.x += advances[i].width; 215 pt.y += advances[i].height; 216 217 } 218 // reset the font on the context after striking a unicode with CoreText 219 if (saved) { 220 CGContextRestoreGState(cgRef); 221 } 222 } 223 224 // Using the Quartz Surface Data context, draw a hot-substituted character run 225 void JavaCT_DrawTextUsingQSD(JNIEnv *env, const QuartzSDOps *qsdo, const AWTStrike *strike, const jchar *chars, const jsize length) 226 { 227 CGContextRef cgRef = qsdo->cgRef; 228 229 AWTFont *awtFont = strike->fAWTFont; 230 CGFloat ptSize = strike->fSize; 231 CGAffineTransform tx = strike->fFontTx; 232 233 NSFont *nsFont = [NSFont fontWithName:[awtFont->fFont fontName] size:ptSize]; 234 235 if (ptSize != 0) { 236 CGFloat invScale = 1 / ptSize; 237 tx = CGAffineTransformConcat(tx, CGAffineTransformMakeScale(invScale, invScale)); 238 CGContextConcatCTM(cgRef, tx); 239 } 240 241 CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity); // resets the damage from CoreText 242 243 NSString *string = [NSString stringWithCharacters:chars length:length]; 244 /* 245 The calls below were used previously but for unknown reason did not 246 render using the right font (see bug 7183516) when attribString is not 247 initialized with font dictionary attributes. It seems that "options" 248 in CTTypesetterCreateWithAttributedStringAndOptions which contains the 249 font dictionary is ignored. 250 251 NSAttributedString *attribString = [[NSAttributedString alloc] initWithString:string]; 252 253 CTTypesetterRef typeSetterRef = CTTypesetterCreateWithAttributedStringAndOptions((CFAttributedStringRef) attribString, (CFDictionaryRef) ctsDictionaryFor(nsFont, JRSFontStyleUsesFractionalMetrics(strike->fStyle))); 254 */ 255 NSAttributedString *attribString = [[NSAttributedString alloc] 256 initWithString:string 257 attributes:ctsDictionaryFor(nsFont, JRSFontStyleUsesFractionalMetrics(strike->fStyle))]; 258 259 CTTypesetterRef typeSetterRef = CTTypesetterCreateWithAttributedString((CFAttributedStringRef) attribString); 260 261 CFRange range = {0, length}; 262 CTLineRef lineRef = CTTypesetterCreateLine(typeSetterRef, range); 263 264 CTLineDraw(lineRef, cgRef); 265 266 [attribString release]; 267 CFRelease(lineRef); 268 CFRelease(typeSetterRef); 269 } 270 271 272 /*---------------------- 273 DrawTextContext is the funnel for all of our CoreText drawing. 274 All three JNI apis call through this method. 275 ----------------------*/ 276 static void DrawTextContext 277 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, const jchar *chars, const jsize length, const jdouble x, const jdouble y) 278 { 279 if (length == 0) 280 { 281 return; 282 } 283 284 qsdo->BeginSurface(env, qsdo, SD_Text); 285 if (qsdo->cgRef == NULL) 286 { 287 qsdo->FinishSurface(env, qsdo); 288 return; 289 } 290 291 CGContextRef cgRef = qsdo->cgRef; 292 293 294 CGContextSaveGState(cgRef); 295 JRSFontSetRenderingStyleOnContext(cgRef, strike->fStyle); 296 297 // we want to translate before we transform (scale or rotate) <rdar://4042541> (vm) 298 CGContextTranslateCTM(cgRef, x, y); 299 300 AWTFont *awtfont = strike->fAWTFont; //(AWTFont *)(qsdo->fontInfo.awtfont); 301 NSCharacterSet *charSet = [awtfont->fFont coveredCharacterSet]; 302 303 JavaCT_DrawTextUsingQSD(env, qsdo, strike, chars, length); // Draw with CoreText 304 305 CGContextRestoreGState(cgRef); 306 307 qsdo->FinishSurface(env, qsdo); 308 } 309 310 #pragma mark --- Glyph Vector Pipeline --- 311 312 /*----------------------------------- 313 Glyph Vector Pipeline 314 315 doDrawGlyphs() has been separated into several pipelined functions to increase performance, 316 and improve accountability for JNI resources, malloc'd memory, and error handling. 317 318 Each stage of the pipeline is responsible for doing only one major thing, like allocating buffers, 319 aquiring transform arrays from JNI, filling buffers, or striking glyphs. All resources or memory 320 acquired at a given stage, must be released in that stage. Any error that occurs (like a failed malloc) 321 is to be handled in the stage it occurs in, and is to return immediatly after freeing it's resources. 322 323 -----------------------------------*/ 324 325 static JNF_CLASS_CACHE(jc_StandardGlyphVector, "sun/font/StandardGlyphVector"); 326 327 // Checks the GlyphVector Java object for any transforms that were applied to individual characters. If none are present, 328 // strike the glyphs immediately in Core Graphics. Otherwise, obtain the arrays, and defer to above. 329 static inline void doDrawGlyphsPipe_checkForPerGlyphTransforms 330 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, BOOL useSubstituion, int *uniChars, CGGlyph *glyphs, CGSize *advances, size_t length) 331 { 332 // if we have no character substitution, and no per-glyph transformations - strike now! 333 static JNF_MEMBER_CACHE(jm_StandardGlyphVector_gti, jc_StandardGlyphVector, "gti", "Lsun/font/StandardGlyphVector$GlyphTransformInfo;"); 334 jobject gti = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_gti); 335 if (gti == 0) 336 { 337 if (useSubstituion) 338 { 339 // quasi-simple case, substitution, but no per-glyph transforms 340 JavaCT_DrawGlyphVector(qsdo, strike, TRUE, uniChars, glyphs, advances, NULL, NULL, length); 341 } 342 else 343 { 344 // fast path, straight to CG without per-glyph transforms 345 CGContextShowGlyphsWithAdvances(qsdo->cgRef, glyphs, advances, length); 346 } 347 return; 348 } 349 350 static JNF_CLASS_CACHE(jc_StandardGlyphVector_GlyphTransformInfo, "sun/font/StandardGlyphVector$GlyphTransformInfo"); 351 static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_transforms, jc_StandardGlyphVector_GlyphTransformInfo, "transforms", "[D"); 352 jdoubleArray g_gtiTransformsArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_transforms); //(*env)->GetObjectField(env, gti, g_gtiTransforms); 353 if (g_gtiTransformsArray == NULL) { 354 return; 355 } 356 jdouble *g_gvTransformsAsDoubles = (*env)->GetPrimitiveArrayCritical(env, g_gtiTransformsArray, NULL); 357 if (g_gvTransformsAsDoubles == NULL) { 358 (*env)->DeleteLocalRef(env, g_gtiTransformsArray); 359 return; 360 } 361 362 static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_indices, jc_StandardGlyphVector_GlyphTransformInfo, "indices", "[I"); 363 jintArray g_gtiTXIndicesArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_indices); 364 jint *g_gvTXIndicesAsInts = (*env)->GetPrimitiveArrayCritical(env, g_gtiTXIndicesArray, NULL); 365 if (g_gvTXIndicesAsInts == NULL) { 366 (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT); 367 (*env)->DeleteLocalRef(env, g_gtiTransformsArray); 368 (*env)->DeleteLocalRef(env, g_gtiTXIndicesArray); 369 return; 370 } 371 // slowest case, we have per-glyph transforms, and possibly glyph substitution as well 372 JavaCT_DrawGlyphVector(qsdo, strike, useSubstituion, uniChars, glyphs, advances, g_gvTXIndicesAsInts, g_gvTransformsAsDoubles, length); 373 374 (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT); 375 (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTXIndicesArray, g_gvTXIndicesAsInts, JNI_ABORT); 376 377 (*env)->DeleteLocalRef(env, g_gtiTransformsArray); 378 (*env)->DeleteLocalRef(env, g_gtiTXIndicesArray); 379 } 380 381 // Retrieves advances for translated unicodes 382 // Uses "glyphs" as a temporary buffer for the glyph-to-unicode translation 383 void JavaCT_GetAdvancesForUnichars 384 (const NSFont *font, const int uniChars[], CGGlyph glyphs[], const size_t length, CGSize advances[]) 385 { 386 // cycle over each spot, and if we discovered a unicode to substitute, we have to calculate the advance for it 387 size_t i; 388 for (i = 0; i < length; i++) 389 { 390 UniChar uniChar = uniChars[i]; 391 if (uniChar == 0) continue; 392 393 CGGlyph glyph = 0; 394 const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font, &uniChar, 1); 395 if (fallback) { 396 CTFontGetGlyphsForCharacters(fallback, &uniChar, &glyph, 1); 397 CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &(advances[i]), 1); 398 CFRelease(fallback); 399 } 400 401 glyphs[i] = glyph; 402 } 403 } 404 405 // Fills the glyph buffer with glyphs from the GlyphVector object. Also checks to see if the glyph's positions have been 406 // already caculated from GlyphVector, or we simply ask Core Graphics to make some advances for us. Pre-calculated positions 407 // are translated into advances, since CG only understands advances. 408 static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers 409 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, int *uniChars, CGSize *advances, size_t length, jintArray glyphsArray) 410 { 411 // fill the glyph buffer 412 jint *glyphsAsInts = (*env)->GetPrimitiveArrayCritical(env, glyphsArray, NULL); 413 if (glyphsAsInts == NULL) { 414 return; 415 } 416 417 // if a glyph code from Java is negative, that means it is really a unicode value 418 // which we can use in CoreText to strike the character in another font 419 size_t i; 420 BOOL complex = NO; 421 for (i = 0; i < length; i++) 422 { 423 jint code = glyphsAsInts[i]; 424 if (code < 0) 425 { 426 complex = YES; 427 uniChars[i] = -code; 428 glyphs[i] = 0; 429 } 430 else 431 { 432 uniChars[i] = 0; 433 glyphs[i] = code; 434 } 435 } 436 437 (*env)->ReleasePrimitiveArrayCritical(env, glyphsArray, glyphsAsInts, JNI_ABORT); 438 439 // fill the advance buffer 440 static JNF_MEMBER_CACHE(jm_StandardGlyphVector_positions, jc_StandardGlyphVector, "positions", "[F"); 441 jfloatArray posArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_positions); 442 jfloat *positions = NULL; 443 if (posArray != NULL) { 444 // in this case, the positions have already been pre-calculated for us on the Java side 445 positions = (*env)->GetPrimitiveArrayCritical(env, posArray, NULL); 446 if (positions == NULL) { 447 (*env)->DeleteLocalRef(env, posArray); 448 } 449 } 450 if (positions != NULL) { 451 CGPoint prev; 452 prev.x = positions[0]; 453 prev.y = positions[1]; 454 455 // <rdar://problem/4294061> take the first point, and move the context to that location 456 CGContextTranslateCTM(qsdo->cgRef, prev.x, prev.y); 457 458 CGAffineTransform invTx = CGAffineTransformInvert(strike->fFontTx); 459 460 // for each position, figure out the advance (since CG won't take positions directly) 461 size_t i; 462 for (i = 0; i < length - 1; i++) 463 { 464 size_t i2 = (i+1) * 2; 465 CGPoint pt; 466 pt.x = positions[i2]; 467 pt.y = positions[i2+1]; 468 pt = CGPointApplyAffineTransform(pt, invTx); 469 advances[i].width = pt.x - prev.x; 470 advances[i].height = -(pt.y - prev.y); // negative to translate to device space 471 prev.x = pt.x; 472 prev.y = pt.y; 473 } 474 475 (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, JNI_ABORT); 476 (*env)->DeleteLocalRef(env, posArray); 477 } 478 else 479 { 480 // in this case, we have to go and calculate the positions ourselves 481 // there were no pre-calculated positions from the glyph buffer on the Java side 482 AWTFont *awtFont = strike->fAWTFont; 483 CTFontGetAdvancesForGlyphs((CTFontRef)awtFont->fFont, kCTFontDefaultOrientation, glyphs, advances, length); 484 485 if (complex) 486 { 487 JavaCT_GetAdvancesForUnichars(awtFont->fFont, uniChars, glyphs, length, advances); 488 } 489 } 490 491 // continue on to the next stage of the pipe 492 doDrawGlyphsPipe_checkForPerGlyphTransforms(env, qsdo, strike, gVector, complex, uniChars, glyphs, advances, length); 493 } 494 495 // Obtains the glyph array to determine the number of glyphs we are dealing with. If we are dealing a large number of glyphs, 496 // we malloc a buffer to hold the glyphs and their advances, otherwise we use stack allocated buffers. 497 static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc 498 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector) 499 { 500 static JNF_MEMBER_CACHE(jm_StandardGlyphVector_glyphs, jc_StandardGlyphVector, "glyphs", "[I"); 501 jintArray glyphsArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_glyphs); 502 jsize length = (*env)->GetArrayLength(env, glyphsArray); 503 504 if (length == 0) 505 { 506 // nothing to draw 507 (*env)->DeleteLocalRef(env, glyphsArray); 508 return; 509 } 510 511 if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) 512 { 513 // if we are small enough, fit everything onto the stack 514 CGGlyph glyphs[length]; 515 int uniChars[length]; 516 CGSize advances[length]; 517 doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray); 518 } 519 else 520 { 521 // otherwise, we should malloc and free buffers for this large run 522 CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * length); 523 int *uniChars = (int *)malloc(sizeof(int) * length); 524 CGSize *advances = (CGSize *)malloc(sizeof(CGSize) * length); 525 526 if (glyphs == NULL || uniChars == NULL || advances == NULL) 527 { 528 (*env)->DeleteLocalRef(env, glyphsArray); 529 [NSException raise:NSMallocException format:@"%s-%s:%d", __FILE__, __FUNCTION__, __LINE__]; 530 if (glyphs) 531 { 532 free(glyphs); 533 } 534 if (uniChars) 535 { 536 free(uniChars); 537 } 538 if (advances) 539 { 540 free(advances); 541 } 542 return; 543 } 544 545 doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray); 546 547 free(glyphs); 548 free(uniChars); 549 free(advances); 550 } 551 552 (*env)->DeleteLocalRef(env, glyphsArray); 553 } 554 555 // Setup and save the state of the CGContext, and apply any java.awt.Font transforms to the context. 556 static inline void doDrawGlyphsPipe_applyFontTransforms 557 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, const jfloat x, const jfloat y) 558 { 559 CGContextRef cgRef = qsdo->cgRef; 560 CGContextSetFontSize(cgRef, 1.0); 561 CGContextSetFont(cgRef, strike->fAWTFont->fNativeCGFont); 562 CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity); 563 564 CGAffineTransform tx = strike->fFontTx; 565 tx.tx += x; 566 tx.ty += y; 567 CGContextConcatCTM(cgRef, tx); 568 569 doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc(env, qsdo, strike, gVector); 570 } 571 572 573 #pragma mark --- CTextPipe JNI --- 574 575 576 /* 577 * Class: sun_lwawt_macosx_CTextPipe 578 * Method: doDrawString 579 * Signature: (Lsun/java2d/SurfaceData;JLjava/lang/String;DD)V 580 */ 581 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawString 582 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jstring str, jdouble x, jdouble y) 583 { 584 QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); 585 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 586 587 JNF_COCOA_ENTER(env); 588 589 jsize len = (*env)->GetStringLength(env, str); 590 591 if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation <rdar://problem/4285041> 592 { 593 jchar unichars[len]; 594 (*env)->GetStringRegion(env, str, 0, len, unichars); 595 JNF_CHECK_AND_RETHROW_EXCEPTION(env); 596 597 // Draw the text context 598 DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y); 599 } 600 else 601 { 602 // Get string to draw and the length 603 const jchar *unichars = JNFGetStringUTF16UniChars(env, str); 604 605 // Draw the text context 606 DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y); 607 608 JNFReleaseStringUTF16UniChars(env, str, unichars); 609 } 610 611 JNF_COCOA_RENDERER_EXIT(env); 612 } 613 614 615 /* 616 * Class: sun_lwawt_macosx_CTextPipe 617 * Method: doUnicodes 618 * Signature: (Lsun/java2d/SurfaceData;J[CIIFF)V 619 */ 620 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doUnicodes 621 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jcharArray unicodes, jint offset, jint length, jfloat x, jfloat y) 622 { 623 QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); 624 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 625 626 JNF_COCOA_ENTER(env); 627 628 // Setup the text context 629 if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation 630 { 631 jchar copyUnichars[length]; 632 (*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars); 633 JNF_CHECK_AND_RETHROW_EXCEPTION(env); 634 DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y); 635 } 636 else 637 { 638 jchar *copyUnichars = malloc(length * sizeof(jchar)); 639 if (!copyUnichars) { 640 [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory to create the glyphs for string drawing"]; 641 } 642 643 @try { 644 (*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars); 645 JNF_CHECK_AND_RETHROW_EXCEPTION(env); 646 DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y); 647 } @finally { 648 free(copyUnichars); 649 } 650 } 651 652 JNF_COCOA_RENDERER_EXIT(env); 653 } 654 655 /* 656 * Class: sun_lwawt_macosx_CTextPipe 657 * Method: doOneUnicode 658 * Signature: (Lsun/java2d/SurfaceData;JCFF)V 659 */ 660 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doOneUnicode 661 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jchar aUnicode, jfloat x, jfloat y) 662 { 663 QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); 664 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 665 666 JNF_COCOA_ENTER(env); 667 668 DrawTextContext(env, qsdo, awtStrike, &aUnicode, 1, x, y); 669 670 JNF_COCOA_RENDERER_EXIT(env); 671 } 672 673 /* 674 * Class: sun_lwawt_macosx_CTextPipe 675 * Method: doDrawGlyphs 676 * Signature: (Lsun/java2d/SurfaceData;JLjava/awt/font/GlyphVector;FF)V 677 */ 678 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawGlyphs 679 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jobject gVector, jfloat x, jfloat y) 680 { 681 QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata); 682 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 683 684 JNF_COCOA_ENTER(env); 685 686 qsdo->BeginSurface(env, qsdo, SD_Text); 687 if (qsdo->cgRef == NULL) 688 { 689 qsdo->FinishSurface(env, qsdo); 690 return; 691 } 692 693 CGContextSaveGState(qsdo->cgRef); 694 JRSFontSetRenderingStyleOnContext(qsdo->cgRef, JRSFontGetRenderingStyleForHints(sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON, sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON)); 695 696 doDrawGlyphsPipe_applyFontTransforms(env, qsdo, awtStrike, gVector, x, y); 697 698 CGContextRestoreGState(qsdo->cgRef); 699 700 qsdo->FinishSurface(env, qsdo); 701 702 JNF_COCOA_RENDERER_EXIT(env); 703 }