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 }