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 }