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