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