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     NSInteger 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     if (g_gtiTransformsArray == NULL) {
 359         return;
 360     } 
 361     jdouble *g_gvTransformsAsDoubles = (*env)->GetPrimitiveArrayCritical(env, g_gtiTransformsArray, NULL);
 362     if (g_gvTransformsAsDoubles == NULL) {
 363         (*env)->DeleteLocalRef(env, g_gtiTransformsArray);
 364         return;
 365     } 
 366 
 367     static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_indices, jc_StandardGlyphVector_GlyphTransformInfo, "indices", "[I");
 368     jintArray g_gtiTXIndicesArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_indices);
 369     jint *g_gvTXIndicesAsInts = (*env)->GetPrimitiveArrayCritical(env, g_gtiTXIndicesArray, NULL);
 370     if (g_gvTXIndicesAsInts == NULL) {
 371         (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT);
 372         (*env)->DeleteLocalRef(env, g_gtiTransformsArray);
 373         (*env)->DeleteLocalRef(env, g_gtiTXIndicesArray);
 374         return;
 375     }
 376     // slowest case, we have per-glyph transforms, and possibly glyph substitution as well
 377     JavaCT_DrawGlyphVector(qsdo, strike, useSubstituion, uniChars, glyphs, advances, g_gvTXIndicesAsInts, g_gvTransformsAsDoubles, length);
 378 
 379     (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT);


 380     (*env)->ReleasePrimitiveArrayCritical(env, g_gtiTXIndicesArray, g_gvTXIndicesAsInts, JNI_ABORT);
 381 
 382     (*env)->DeleteLocalRef(env, g_gtiTransformsArray);
 383     (*env)->DeleteLocalRef(env, g_gtiTXIndicesArray);
 384 }
 385 
 386 // Retrieves advances for translated unicodes
 387 // Uses "glyphs" as a temporary buffer for the glyph-to-unicode translation
 388 void JavaCT_GetAdvancesForUnichars
 389 (const NSFont *font, const int uniChars[], CGGlyph glyphs[], const size_t length, CGSize advances[])
 390 {
 391     // cycle over each spot, and if we discovered a unicode to substitute, we have to calculate the advance for it
 392     size_t i;
 393     for (i = 0; i < length; i++)
 394     {
 395         UniChar uniChar = uniChars[i];
 396         if (uniChar == 0) continue;
 397 
 398         CGGlyph glyph = 0;
 399         const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font, &uniChar, 1);
 400         if (fallback) {
 401             CTFontGetGlyphsForCharacters(fallback, &uniChar, &glyph, 1);
 402             CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &(advances[i]), 1);
 403             CFRelease(fallback);
 404         }
 405 
 406         glyphs[i] = glyph;
 407     }
 408 }
 409 
 410 // Fills the glyph buffer with glyphs from the GlyphVector object. Also checks to see if the glyph's positions have been
 411 // already caculated from GlyphVector, or we simply ask Core Graphics to make some advances for us. Pre-calculated positions
 412 // are translated into advances, since CG only understands advances.
 413 static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers
 414 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, int *uniChars, CGSize *advances, size_t length, jintArray glyphsArray)
 415 {
 416     // fill the glyph buffer
 417     jint *glyphsAsInts = (*env)->GetPrimitiveArrayCritical(env, glyphsArray, NULL);
 418     if (glyphsAsInts == NULL) {
 419         return;
 420     }
 421 
 422     // if a glyph code from Java is negative, that means it is really a unicode value
 423     // which we can use in CoreText to strike the character in another font
 424     size_t i;
 425     BOOL complex = NO;
 426     for (i = 0; i < length; i++)
 427     {
 428         jint code = glyphsAsInts[i];
 429         if (code < 0)
 430         {
 431             complex = YES;
 432             uniChars[i] = -code;
 433             glyphs[i] = 0;
 434         }
 435         else
 436         {
 437             uniChars[i] = 0;
 438             glyphs[i] = code;
 439         }
 440     }
 441 
 442     (*env)->ReleasePrimitiveArrayCritical(env, glyphsArray, glyphsAsInts, JNI_ABORT);
 443 
 444     // fill the advance buffer
 445     static JNF_MEMBER_CACHE(jm_StandardGlyphVector_positions, jc_StandardGlyphVector, "positions", "[F");
 446     jfloatArray posArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_positions);
 447     jfloat *positions = NULL;
 448     if (posArray != NULL) {
 449         // in this case, the positions have already been pre-calculated for us on the Java side
 450         positions = (*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
 451         if (positions == NULL) {
 452             (*env)->DeleteLocalRef(env, posArray);
 453         }
 454     }
 455     if (positions != NULL) {
 456         CGPoint prev;
 457         prev.x = positions[0];
 458         prev.y = positions[1];
 459 
 460         // <rdar://problem/4294061> take the first point, and move the context to that location
 461         CGContextTranslateCTM(qsdo->cgRef, prev.x, prev.y);
 462 
 463         CGAffineTransform invTx = CGAffineTransformInvert(strike->fFontTx);
 464 
 465         // for each position, figure out the advance (since CG won't take positions directly)
 466         size_t i;
 467         for (i = 0; i < length - 1; i++)
 468         {
 469             size_t i2 = (i+1) * 2;
 470             CGPoint pt;
 471             pt.x = positions[i2];
 472             pt.y = positions[i2+1];
 473             pt = CGPointApplyAffineTransform(pt, invTx);
 474             advances[i].width = pt.x - prev.x;
 475             advances[i].height = -(pt.y - prev.y); // negative to translate to device space
 476             prev.x = pt.x;
 477             prev.y = pt.y;
 478         }
 479 
 480         (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, JNI_ABORT);
 481         (*env)->DeleteLocalRef(env, posArray);
 482     }
 483     else
 484     {
 485         // in this case, we have to go and calculate the positions ourselves
 486         // there were no pre-calculated positions from the glyph buffer on the Java side
 487         AWTFont *awtFont = strike->fAWTFont;
 488         CTFontGetAdvancesForGlyphs((CTFontRef)awtFont->fFont, kCTFontDefaultOrientation, glyphs, advances, length);
 489 
 490         if (complex)
 491         {
 492             JavaCT_GetAdvancesForUnichars(awtFont->fFont, uniChars, glyphs, length, advances);
 493         }
 494     }
 495 
 496     // continue on to the next stage of the pipe
 497     doDrawGlyphsPipe_checkForPerGlyphTransforms(env, qsdo, strike, gVector, complex, uniChars, glyphs, advances, length);
 498 }
 499 
 500 // Obtains the glyph array to determine the number of glyphs we are dealing with. If we are dealing a large number of glyphs,
 501 // we malloc a buffer to hold the glyphs and their advances, otherwise we use stack allocated buffers.
 502 static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc
 503 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector)
 504 {
 505     static JNF_MEMBER_CACHE(jm_StandardGlyphVector_glyphs, jc_StandardGlyphVector, "glyphs", "[I");
 506     jintArray glyphsArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_glyphs);
 507     jsize length = (*env)->GetArrayLength(env, glyphsArray);
 508 
 509     if (length == 0)
 510     {
 511         // nothing to draw
 512         (*env)->DeleteLocalRef(env, glyphsArray);
 513         return;
 514     }
 515 
 516     if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE)
 517     {
 518         // if we are small enough, fit everything onto the stack
 519         CGGlyph glyphs[length];
 520         int uniChars[length];
 521         CGSize advances[length];
 522         doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray);
 523     }
 524     else
 525     {
 526         // otherwise, we should malloc and free buffers for this large run
 527         CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * length);
 528         int *uniChars = (int *)malloc(sizeof(int) * length);
 529         CGSize *advances = (CGSize *)malloc(sizeof(CGSize) * length);
 530 
 531         if (glyphs == NULL || uniChars == NULL || advances == NULL)
 532         {
 533             (*env)->DeleteLocalRef(env, glyphsArray);
 534             [NSException raise:NSMallocException format:@"%s-%s:%d", THIS_FILE, __FUNCTION__, __LINE__];
 535             if (glyphs)
 536             {
 537                 free(glyphs);
 538             }
 539             if (uniChars)
 540             {
 541                 free(uniChars);
 542             }
 543             if (advances)
 544             {
 545                 free(advances);
 546             }
 547             return;
 548         }
 549 
 550         doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray);
 551 
 552         free(glyphs);
 553         free(uniChars);
 554         free(advances);
 555     }
 556 
 557     (*env)->DeleteLocalRef(env, glyphsArray);
 558 }
 559 
 560 // Setup and save the state of the CGContext, and apply any java.awt.Font transforms to the context.
 561 static inline void doDrawGlyphsPipe_applyFontTransforms
 562 (JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, const jfloat x, const jfloat y)
 563 {
 564     CGContextRef cgRef = qsdo->cgRef;
 565     CGContextSetFontSize(cgRef, 1.0);
 566     CGContextSetFont(cgRef, strike->fAWTFont->fNativeCGFont);
 567     CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity);
 568 
 569     CGAffineTransform tx = strike->fFontTx;
 570     tx.tx += x;
 571     tx.ty += y;
 572     CGContextConcatCTM(cgRef, tx);
 573 
 574     doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc(env, qsdo, strike, gVector);
 575 }
 576 
 577 
 578 #pragma mark --- CTextPipe JNI ---
 579 
 580 
 581 /*
 582  * Class:     sun_lwawt_macosx_CTextPipe
 583  * Method:    doDrawString
 584  * Signature: (Lsun/java2d/SurfaceData;JLjava/lang/String;DD)V
 585  */
 586 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawString
 587 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jstring str, jdouble x, jdouble y)
 588 {
 589     QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
 590     AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
 591 
 592 JNF_COCOA_ENTER(env);
 593 
 594     jsize len = (*env)->GetStringLength(env, str);
 595 
 596     if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation <rdar://problem/4285041>
 597     {
 598         jchar unichars[len];
 599         (*env)->GetStringRegion(env, str, 0, len, unichars);
 600         JNF_CHECK_AND_RETHROW_EXCEPTION(env);
 601 
 602         // Draw the text context
 603         DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y);
 604     }
 605     else
 606     {
 607         // Get string to draw and the length
 608         const jchar *unichars = JNFGetStringUTF16UniChars(env, str);
 609 
 610         // Draw the text context
 611         DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y);
 612 
 613         JNFReleaseStringUTF16UniChars(env, str, unichars);
 614     }
 615 
 616 JNF_COCOA_RENDERER_EXIT(env);
 617 }
 618 
 619 
 620 /*
 621  * Class:     sun_lwawt_macosx_CTextPipe
 622  * Method:    doUnicodes
 623  * Signature: (Lsun/java2d/SurfaceData;J[CIIFF)V
 624  */
 625 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doUnicodes
 626 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jcharArray unicodes, jint offset, jint length, jfloat x, jfloat y)
 627 {
 628     QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
 629     AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
 630 
 631 JNF_COCOA_ENTER(env);
 632 
 633     // Setup the text context
 634     if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation
 635     {
 636         jchar copyUnichars[length];
 637         (*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars);
 638         JNF_CHECK_AND_RETHROW_EXCEPTION(env);
 639         DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y);
 640     }
 641     else
 642     {
 643         jchar *copyUnichars = malloc(length * sizeof(jchar));
 644         if (!copyUnichars) {
 645             [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory to create the glyphs for string drawing"];
 646         }
 647 
 648         @try {
 649             (*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars);
 650             JNF_CHECK_AND_RETHROW_EXCEPTION(env);
 651             DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y);
 652         } @finally {
 653             free(copyUnichars);
 654         }
 655     }
 656 
 657 JNF_COCOA_RENDERER_EXIT(env);
 658 }
 659 
 660 /*
 661  * Class:     sun_lwawt_macosx_CTextPipe
 662  * Method:    doOneUnicode
 663  * Signature: (Lsun/java2d/SurfaceData;JCFF)V
 664  */
 665 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doOneUnicode
 666 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jchar aUnicode, jfloat x, jfloat y)
 667 {
 668     QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
 669     AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
 670 
 671 JNF_COCOA_ENTER(env);
 672 
 673     DrawTextContext(env, qsdo, awtStrike, &aUnicode, 1, x, y);
 674 
 675 JNF_COCOA_RENDERER_EXIT(env);
 676 }
 677 
 678 /*
 679  * Class: sun_lwawt_macosx_CTextPipe
 680  * Method: doDrawGlyphs
 681  * Signature: (Lsun/java2d/SurfaceData;JLjava/awt/font/GlyphVector;FF)V
 682  */
 683 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawGlyphs
 684 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jobject gVector, jfloat x, jfloat y)
 685 {
 686     QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
 687     AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
 688 
 689 JNF_COCOA_ENTER(env);
 690 
 691     qsdo->BeginSurface(env, qsdo, SD_Text);
 692     if (qsdo->cgRef == NULL)
 693     {
 694         qsdo->FinishSurface(env, qsdo);
 695         return;
 696     }
 697 
 698     CGContextSaveGState(qsdo->cgRef);
 699     JRSFontSetRenderingStyleOnContext(qsdo->cgRef, JRSFontGetRenderingStyleForHints(sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON, sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON));
 700 
 701     doDrawGlyphsPipe_applyFontTransforms(env, qsdo, awtStrike, gVector, x, y);
 702 
 703     CGContextRestoreGState(qsdo->cgRef);
 704 
 705     qsdo->FinishSurface(env, qsdo);
 706 
 707 JNF_COCOA_RENDERER_EXIT(env);
 708 }
--- EOF ---