rev 54098 : 8260616: Removing remaining JNF dependencies in the java.desktop module
8259729: Missed JNFInstanceOf -> IsInstanceOf conversion

   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 #import <Accelerate/Accelerate.h> // for vImage_Buffer
  27 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  28 

  29 #import "CGGlyphImages.h"
  30 #import "CoreTextSupport.h"
  31 #import "fontscalerdefs.h" // contains the definition of GlyphInfo struct
  32 
  33 #import "sun_awt_SunHints.h"
  34 
  35 //#define USE_IMAGE_ALIGNED_MEMORY 1
  36 //#define CGGI_DEBUG 1
  37 //#define CGGI_DEBUG_DUMP 1
  38 //#define CGGI_DEBUG_HIT_COUNT 1
  39 
  40 #define PRINT_TX(x) \
  41     NSLog(@"(%f, %f, %f, %f, %f, %f)", x.a, x.b, x.c, x.d, x.tx, x.ty);
  42 
  43 /*
  44  * The GlyphCanvas is a global shared CGContext that characters are struck into.
  45  * For each character, the glyph is struck, copied into a GlyphInfo struct, and
  46  * the canvas is cleared for the next glyph.
  47  *
  48  * If the necessary canvas is too large, the shared one will not be used and a
  49  * temporary one will be provided.
  50  */
  51 @interface CGGI_GlyphCanvas : NSObject {
  52 @public
  53     CGContextRef context;
  54     vImage_Buffer *image;
  55 }
  56 @end;
  57 
  58 @implementation CGGI_GlyphCanvas
  59 @end
  60 
  61 
  62 #pragma mark --- Debugging Helpers ---
  63 
  64 /*
  65  * These debug functions are only compiled when CGGI_DEBUG is activated.
  66  * They will print out a full UInt8 canvas and any pixels struck (assuming
  67  * the canvas is not too big).
  68  *
  69  * As another debug feature, the entire canvas will be filled with a light
  70  * alpha value so it is easy to see where the glyph painting regions are
  71  * at runtime.
  72  */
  73 
  74 #ifdef CGGI_DEBUG_DUMP
  75 static void
  76 DUMP_PIXELS(const char msg[], const UInt8 pixels[],
  77             const size_t bytesPerPixel, const int width, const int height)
  78 {
  79     printf("| %s: (%d, %d)\n", msg, width, height);
  80 
  81     if (width > 80 || height > 80) {
  82         printf("| too big\n");
  83         return;
  84     }
  85 
  86     size_t i, j = 0, k, size = width * height;
  87     for (i = 0; i < size; i++) {
  88         for (k = 0; k < bytesPerPixel; k++) {
  89             if (pixels[i * bytesPerPixel + k] > 0x80) j++;
  90         }
  91     }
  92 
  93     if (j == 0) {
  94         printf("| empty\n");
  95         return;
  96     }
  97 
  98     printf("|_");
  99     int x, y;
 100     for (x = 0; x < width; x++) {
 101         printf("__");
 102     }
 103     printf("_\n");
 104 
 105     for (y = 0; y < height; y++) {
 106         printf("| ");
 107         for (x = 0; x < width; x++) {
 108             int p = 0;
 109             for(k = 0; k < bytesPerPixel; k++) {
 110                 p += pixels[(y * width + x) * bytesPerPixel + k];
 111             }
 112 
 113             if (p < 0x80) {
 114                 printf("  ");
 115             } else {
 116                 printf("[]");
 117             }
 118         }
 119         printf(" |\n");
 120     }
 121 }
 122 
 123 static void
 124 DUMP_IMG_PIXELS(const char msg[], const vImage_Buffer *image)
 125 {
 126     const void *pixels = image->data;
 127     const size_t pixelSize = image->rowBytes / image->width;
 128     const size_t width = image->width;
 129     const size_t height = image->height;
 130 
 131     DUMP_PIXELS(msg, pixels, pixelSize, width, height);
 132 }
 133 
 134 static void
 135 PRINT_CGSTATES_INFO(const CGContextRef cgRef)
 136 {
 137     // TODO(cpc): lots of SPI use in this method; remove/rewrite?
 138 #if 0
 139     CGRect clip = CGContextGetClipBoundingBox(cgRef);
 140     fprintf(stderr, "    clip: ((%f, %f), (%f, %f))\n",
 141             clip.origin.x, clip.origin.y, clip.size.width, clip.size.height);
 142 
 143     CGAffineTransform ctm = CGContextGetCTM(cgRef);
 144     fprintf(stderr, "    ctm: (%f, %f, %f, %f, %f, %f)\n",
 145             ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);
 146 
 147     CGAffineTransform txtTx = CGContextGetTextMatrix(cgRef);
 148     fprintf(stderr, "    txtTx: (%f, %f, %f, %f, %f, %f)\n",
 149             txtTx.a, txtTx.b, txtTx.c, txtTx.d, txtTx.tx, txtTx.ty);
 150 
 151     if (CGContextIsPathEmpty(cgRef) == 0) {
 152         CGPoint pathpoint = CGContextGetPathCurrentPoint(cgRef);
 153         CGRect pathbbox = CGContextGetPathBoundingBox(cgRef);
 154         fprintf(stderr, "    [pathpoint: (%f, %f)] [pathbbox: ((%f, %f), (%f, %f))]\n",
 155                 pathpoint.x, pathpoint.y, pathbbox.origin.x, pathbbox.origin.y,
 156                 pathbbox.size.width, pathbbox.size.width);
 157     }
 158 
 159     CGFloat linewidth = CGContextGetLineWidth(cgRef);
 160     CGLineCap linecap = CGContextGetLineCap(cgRef);
 161     CGLineJoin linejoin = CGContextGetLineJoin(cgRef);
 162     CGFloat miterlimit = CGContextGetMiterLimit(cgRef);
 163     size_t dashcount = CGContextGetLineDashCount(cgRef);
 164     fprintf(stderr, "    [linewidth: %f] [linecap: %d] [linejoin: %d] [miterlimit: %f] [dashcount: %lu]\n",
 165             linewidth, linecap, linejoin, miterlimit, (unsigned long)dashcount);
 166 
 167     CGFloat smoothness = CGContextGetSmoothness(cgRef);
 168     bool antialias = CGContextGetShouldAntialias(cgRef);
 169     bool smoothfont = CGContextGetShouldSmoothFonts(cgRef);
 170     JRSFontRenderingStyle fRendMode = CGContextGetFontRenderingMode(cgRef);
 171     fprintf(stderr, "    [smoothness: %f] [antialias: %d] [smoothfont: %d] [fontrenderingmode: %d]\n",
 172             smoothness, antialias, smoothfont, fRendMode);
 173 #endif
 174 }
 175 #endif
 176 
 177 #ifdef CGGI_DEBUG
 178 
 179 static void
 180 DUMP_GLYPHINFO(const GlyphInfo *info)
 181 {
 182     printf("size: (%d, %d) pixelSize: %d\n",
 183            info->width, info->height, info->rowBytes / info->width);
 184     printf("adv: (%f, %f) top: (%f, %f)\n",
 185            info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);
 186 
 187 #ifdef CGGI_DEBUG_DUMP
 188     DUMP_PIXELS("Glyph Info Struct",
 189                 info->image, info->rowBytes / info->width,
 190                 info->width, info->height);
 191 #endif
 192 }
 193 
 194 #endif
 195 
 196 
 197 #pragma mark --- Font Rendering Mode Descriptors ---
 198 static Int32 reverseGamma = 0;
 199 
 200 static UInt8 reverseGammaLut[256] = { 0 };
 201 
 202 static inline UInt8* getReverseGammaLut() {
 203     if (reverseGamma == 0) {
 204         // initialize gamma lut
 205         double gamma;
 206         int i;
 207         const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA");
 208         if (pGammaEnv != NULL) {
 209             reverseGamma = atol(pGammaEnv);
 210         }
 211 
 212         if (reverseGamma < 100 || reverseGamma > 250) {
 213             reverseGamma = 180;
 214         }
 215 
 216         gamma = 100.0 / reverseGamma;
 217         for (i = 0; i < 256; i++) {
 218             double x = ((double)i) / 255.0;
 219             reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma));
 220         }
 221     }
 222     return reverseGammaLut;
 223 }
 224 
 225 static inline void
 226 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
 227 {
 228     UInt8* lut = getReverseGammaLut();
 229 
 230     *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)];  // red
 231     *(dst + 1) = lut[0xFF - (p >>  8 & 0xFF)];  // green
 232     *(dst + 2) = lut[0xFF - (p & 0xFF)];        // blue
 233 }
 234 
 235 static void
 236 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 237 {
 238     UInt32 *src = (UInt32 *)canvas->image->data;
 239     size_t srcRowWidth = canvas->image->width;
 240 
 241     UInt8 *dest = (UInt8 *)info->image;
 242     size_t destRowWidth = info->width;
 243 
 244     size_t height = info->height;
 245 
 246     size_t y;
 247 
 248     // fill empty glyph image with black-on-white glyph
 249     for (y = 0; y < height; y++) {
 250         size_t destRow = y * destRowWidth * 3;
 251         size_t srcRow = y * srcRowWidth;
 252 
 253         size_t x;
 254         for (x = 0; x < destRowWidth; x++) {
 255             CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
 256                                          dest + destRow + x * 3);
 257         }
 258     }
 259 }
 260 
 261 //static void CGGI_copyImageFromCanvasToAlphaInfo
 262 //(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 263 //{
 264 //    vImage_Buffer infoBuffer;
 265 //    infoBuffer.data = info->image;
 266 //    infoBuffer.width = info->width;
 267 //    infoBuffer.height = info->height;
 268 //    infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
 269 //
 270 //    UInt8 scrapPixel[info->width * info->height];
 271 //    vImage_Buffer scrapBuffer;
 272 //    scrapBuffer.data = &scrapPixel;
 273 //    scrapBuffer.width = info->width;
 274 //    scrapBuffer.height = info->height;
 275 //    scrapBuffer.rowBytes = info->width;
 276 //
 277 //    vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
 278 //        &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
 279 //}
 280 
 281 static inline UInt8
 282 CGGI_ConvertBWPixelToByteGray(UInt32 p)
 283 {
 284     return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3);
 285 }
 286 
 287 static void
 288 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 289 {
 290     UInt32 *src = (UInt32 *)canvas->image->data;
 291     size_t srcRowWidth = canvas->image->width;
 292 
 293     UInt8 *dest = (UInt8 *)info->image;
 294     size_t destRowWidth = info->width;
 295 
 296     size_t height = info->height;
 297 
 298     size_t y;
 299 
 300     // fill empty glyph image with black-on-white glyph
 301     for (y = 0; y < height; y++) {
 302         size_t destRow = y * destRowWidth;
 303         size_t srcRow = y * srcRowWidth;
 304         size_t x;
 305         for (x = 0; x < destRowWidth; x++) {
 306             UInt32 p = src[srcRow + x];
 307             dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
 308         }
 309     }
 310 }
 311 
 312 
 313 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
 314 
 315 typedef struct CGGI_GlyphInfoDescriptor {
 316     size_t pixelSize;
 317     void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
 318 } CGGI_GlyphInfoDescriptor;
 319 
 320 typedef struct CGGI_RenderingMode {
 321     CGGI_GlyphInfoDescriptor *glyphDescriptor;
 322     JRSFontRenderingStyle cgFontMode;
 323 } CGGI_RenderingMode;
 324 
 325 static CGGI_GlyphInfoDescriptor grey =
 326     { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
 327 static CGGI_GlyphInfoDescriptor rgb =
 328     { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
 329 
 330 static inline CGGI_RenderingMode
 331 CGGI_GetRenderingMode(const AWTStrike *strike)
 332 {
 333     CGGI_RenderingMode mode;
 334     mode.cgFontMode = strike->fStyle;
 335     NSException *e = nil;
 336 
 337     switch (strike->fAAStyle) {
 338     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
 339     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
 340         mode.glyphDescriptor = &grey;
 341         break;
 342     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
 343     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
 344     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
 345     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
 346         mode.glyphDescriptor = &rgb;
 347         break;
 348     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
 349     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
 350     default:
 351         /* we expect that text antialiasing hint has been already
 352          * evaluated. Report an error if we get 'unevaluated' hint here.
 353          */
 354         e = [NSException
 355                 exceptionWithName:@"IllegalArgumentException"
 356                 reason:@"Invalid hint value"
 357                 userInfo:nil];
 358         @throw e;
 359     }
 360 
 361     return mode;
 362 }
 363 
 364 
 365 #pragma mark --- Canvas Managment ---
 366 
 367 /*
 368  * Creates a new canvas of a fixed size, and initializes the CGContext as
 369  * an 32-bit ARGB BitmapContext with some generic RGB color space.
 370  */
 371 static inline void
 372 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
 373                 const vImagePixelCount width, const vImagePixelCount height,
 374                 const CGGI_RenderingMode* mode)
 375 {
 376     // our canvas is *always* 4-byte ARGB
 377     size_t bytesPerRow = width * sizeof(UInt32);
 378     size_t byteCount = bytesPerRow * height;
 379 
 380     canvas->image = malloc(sizeof(vImage_Buffer));
 381     canvas->image->width = width;
 382     canvas->image->height = height;
 383     canvas->image->rowBytes = bytesPerRow;
 384 
 385     canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
 386     if (canvas->image->data == NULL) {
 387         [[NSException exceptionWithName:NSMallocException
 388             reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
 389     }
 390 
 391     uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
 392     if (mode->glyphDescriptor == &rgb) {
 393         bmpInfo |= kCGBitmapByteOrder32Host;
 394     }
 395 
 396     CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
 397     canvas->context = CGBitmapContextCreate(canvas->image->data,
 398                                             width, height, 8, bytesPerRow,
 399                                             colorSpace,
 400                                             bmpInfo);
 401 
 402     // set foreground color
 403     CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
 404 
 405     CGContextSetFontSize(canvas->context, 1);
 406     CGContextSaveGState(canvas->context);
 407 
 408     CGColorSpaceRelease(colorSpace);
 409 }
 410 
 411 /*
 412  * Releases the BitmapContext and the associated memory backing it.
 413  */
 414 static inline void
 415 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
 416 {
 417     if (canvas->context != NULL) {
 418         CGContextRelease(canvas->context);
 419     }
 420 
 421     if (canvas->image != NULL) {
 422         if (canvas->image->data != NULL) {
 423             free(canvas->image->data);
 424         }
 425         free(canvas->image);
 426     }
 427 }
 428 
 429 /*
 430  * This is the slack space that is preallocated for the global GlyphCanvas
 431  * when it needs to be expanded. It has been set somewhat liberally to
 432  * avoid re-upsizing frequently.
 433  */
 434 #define CGGI_GLYPH_CANVAS_SLACK 2.5
 435 
 436 /*
 437  * Quick and easy inline to check if this canvas is big enough.
 438  */
 439 static inline void
 440 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
 441         const vImagePixelCount height,
 442         const CGGI_RenderingMode* mode)
 443 {
 444     if (canvas->image != NULL &&
 445         width  < canvas->image->width &&
 446         height < canvas->image->height)
 447     {
 448         return;
 449     }
 450 
 451     // if we don't have enough space to strike the largest glyph in the
 452     // run, resize the canvas
 453     CGGI_FreeCanvas(canvas);
 454     CGGI_InitCanvas(canvas,
 455                     width * CGGI_GLYPH_CANVAS_SLACK,
 456                     height * CGGI_GLYPH_CANVAS_SLACK,
 457                     mode);
 458     JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
 459 }
 460 
 461 /*
 462  * Clear the canvas by blitting white only into the region of interest
 463  * (the rect which we will copy out of once the glyph is struck).
 464  */
 465 static inline void
 466 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 467 {
 468     vImage_Buffer canvasRectToClear;
 469     canvasRectToClear.data = canvas->image->data;
 470     canvasRectToClear.height = info->height;
 471     canvasRectToClear.width = info->width;
 472     // use the row stride of the canvas, not the info
 473     canvasRectToClear.rowBytes = canvas->image->rowBytes;
 474 
 475     // clean the canvas
 476 #ifdef CGGI_DEBUG
 477     Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
 478 #else
 479     Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
 480 #endif
 481 
 482     // clear canvas background and set foreground color
 483     vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
 484 }
 485 
 486 
 487 #pragma mark --- GlyphInfo Creation & Copy Functions ---
 488 
 489 /*
 490  * Creates a GlyphInfo with exactly the correct size image and measurements.
 491  */
 492 #define CGGI_GLYPH_BBOX_PADDING 2.0f
 493 static inline GlyphInfo *
 494 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
 495                             const AWTStrike *strike,
 496                             const CGGI_RenderingMode *mode)
 497 {
 498     size_t pixelSize = mode->glyphDescriptor->pixelSize;
 499 
 500     // adjust the bounding box to be 1px bigger on each side than what
 501     // CGFont-whatever suggests - because it gives a bounding box that
 502     // is too tight
 503     bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;
 504     bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f;
 505     bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;
 506     bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;
 507 
 508     vImagePixelCount width = ceilf(bbox.size.width);
 509     vImagePixelCount height = ceilf(bbox.size.height);
 510 
 511     // if the glyph is larger than 1MB, don't even try...
 512     // the GlyphVector path should have taken over by now
 513     // and zero pixels is ok
 514     if (width * height > 1024 * 1024) {
 515         width = 1;
 516         height = 1;
 517     }
 518     advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
 519     if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
 520         advance.width = round(advance.width);
 521         advance.height = round(advance.height);
 522     }
 523     advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
 524 
 525 #ifdef USE_IMAGE_ALIGNED_MEMORY
 526     // create separate memory
 527     GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));
 528     void *image = (void *)malloc(height * width * pixelSize);
 529 #else
 530     // create a GlyphInfo struct fused to the image it points to
 531     GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +
 532                                                height * width * pixelSize);
 533 #endif
 534 
 535     glyphInfo->advanceX = advance.width;
 536     glyphInfo->advanceY = advance.height;
 537     glyphInfo->topLeftX = round(bbox.origin.x);
 538     glyphInfo->topLeftY = round(bbox.origin.y);
 539     glyphInfo->width = width;
 540     glyphInfo->height = height;
 541     glyphInfo->rowBytes = width * pixelSize;
 542     glyphInfo->cellInfo = NULL;
 543 
 544 #ifdef USE_IMAGE_ALIGNED_MEMORY
 545     glyphInfo->image = image;
 546 #else
 547     glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);
 548 #endif
 549 
 550     return glyphInfo;
 551 }
 552 
 553 
 554 #pragma mark --- Glyph Striking onto Canvas ---
 555 
 556 /*
 557  * Clears the canvas, strikes the glyph with CoreGraphics, and then
 558  * copies the struck pixels into the GlyphInfo image.
 559  */
 560 static inline void
 561 CGGI_CreateImageForGlyph
 562     (CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
 563      GlyphInfo *info, const CGGI_RenderingMode *mode)
 564 {
 565     if (isnan(info->topLeftX) || isnan(info->topLeftY)) {
 566         // Explicitly set glyphInfo width/height to be 0 to ensure
 567         // zero length glyph image is copied into GlyphInfo from canvas
 568         info->width = 0;
 569         info->height = 0;
 570 
 571         // copy the "empty" glyph from the canvas into the info
 572         (*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
 573         return;
 574     }
 575 
 576     // clean the canvas
 577     CGGI_ClearCanvas(canvas, info);
 578 
 579     // strike the glyph in the upper right corner
 580     CGContextShowGlyphsAtPoint(canvas->context,
 581                                -info->topLeftX,
 582                                canvas->image->height + info->topLeftY,
 583                                &glyph, 1);
 584     // copy the glyph from the canvas into the info
 585     (*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
 586 }
 587 
 588 /*
 589  * CoreText path...
 590  */
 591 static inline GlyphInfo *
 592 CGGI_CreateImageForUnicode
 593     (CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
 594      const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar)
 595 {
 596     // save the state of the world
 597     CGContextSaveGState(canvas->context);
 598 
 599     // get the glyph, measure it using CG
 600     CGGlyph glyph;
 601     CTFontRef fallback;
 602     if (uniChar > 0xFFFF) {
 603         UTF16Char charRef[2];
 604         CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
 605         CGGlyph glyphTmp[2];
 606         fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
 607         glyph = glyphTmp[0];
 608     } else {
 609         UTF16Char charRef;
 610         charRef = (UTF16Char) uniChar; // truncate.
 611         fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
 612     }
 613 
 614     CGAffineTransform tx = strike->fTx;
 615     JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
 616 
 617     CGRect bbox;
 618     JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
 619 
 620     CGSize advance;
 621     CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
 622 
 623     // create the Sun2D GlyphInfo we are going to strike into
 624     GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
 625 
 626     // fix the context size, just in case the substituted character is unexpectedly large
 627     CGGI_SizeCanvas(canvas, info->width, info->height, mode);
 628 
 629     // align the transform for the real CoreText strike
 630     CGContextSetTextMatrix(canvas->context, strike->fAltTx);
 631 
 632     const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
 633     CGContextSetFont(canvas->context, cgFallback);
 634     CFRelease(cgFallback);
 635 
 636     // clean the canvas - align, strike, and copy the glyph from the canvas into the info
 637     CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
 638 
 639     // restore the state of the world
 640     CGContextRestoreGState(canvas->context);
 641 
 642     CFRelease(fallback);
 643 #ifdef CGGI_DEBUG
 644     DUMP_GLYPHINFO(info);
 645 #endif
 646 
 647 #ifdef CGGI_DEBUG_DUMP
 648     DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 649 #if 0
 650     PRINT_CGSTATES_INFO(NULL);
 651 #endif
 652 #endif
 653 
 654     return info;
 655 }
 656 
 657 
 658 #pragma mark --- GlyphInfo Filling and Canvas Managment ---
 659 
 660 /*
 661  * Sets all the per-run properties for the canvas, and then iterates through
 662  * the character run, and creates images in the GlyphInfo structs.
 663  *
 664  * Not inlined because it would create two copies in the function below
 665  */
 666 static void
 667 CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
 668                                         const AWTStrike *strike,
 669                                         const CGGI_RenderingMode *mode,
 670                                         jlong glyphInfos[],
 671                                         const UnicodeScalarValue uniChars[],
 672                                         const CGGlyph glyphs[],
 673                                         const CFIndex len)
 674 {
 675     CGContextSetTextMatrix(canvas->context, strike->fAltTx);
 676 
 677     CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
 678     JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
 679 
 680     CFIndex i;
 681     for (i = 0; i < len; i++) {
 682         GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
 683         if (info != NULL) {
 684             CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
 685         } else {
 686             info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
 687             glyphInfos[i] = ptr_to_jlong(info);
 688         }
 689 #ifdef CGGI_DEBUG
 690         DUMP_GLYPHINFO(info);
 691 #endif
 692 
 693 #ifdef CGGI_DEBUG_DUMP
 694         DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 695 #endif
 696     }
 697 #ifdef CGGI_DEBUG_DUMP
 698     DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 699     PRINT_CGSTATES_INFO(canvas->context);
 700 #endif
 701 }
 702 
 703 static NSString *threadLocalAACanvasKey =
 704     @"Java CoreGraphics Text Renderer Cached Canvas for AA";
 705 
 706 static NSString *threadLocalLCDCanvasKey =
 707     @"Java CoreGraphics Text Renderer Cached Canvas for LCD";
 708 
 709 /*
 710  * This is the maximum length and height times the above slack squared
 711  * to determine if we go with the global canvas, or malloc one on the spot.
 712  */
 713 #define CGGI_GLYPH_CANVAS_MAX 100
 714 
 715 /*
 716  * Based on the space needed to strike the largest character in the run,
 717  * either use the global shared canvas, or make one up on the spot, strike
 718  * the glyphs, and destroy it.
 719  */
 720 static inline void
 721 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
 722                          const CGGI_RenderingMode *mode,
 723                          const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
 724                          const size_t maxWidth, const size_t maxHeight,
 725                          const CFIndex len)
 726 {
 727     if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
 728         CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
 729     {
 730         CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
 731         CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
 732         CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
 733                 mode, glyphInfos, uniChars,
 734                 glyphs, len);
 735         CGGI_FreeCanvas(tmpCanvas);
 736 
 737         [tmpCanvas release];
 738         return;
 739     }
 740     NSMutableDictionary *threadDict =
 741         [[NSThread currentThread] threadDictionary];
 742 
 743     NSString* theKey = (mode->glyphDescriptor == &rgb) ?
 744         threadLocalLCDCanvasKey : threadLocalAACanvasKey;
 745 
 746     CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
 747     if (canvas == nil) {
 748         canvas = [[CGGI_GlyphCanvas alloc] init];
 749         [threadDict setObject:canvas forKey:theKey];
 750     }
 751 
 752     CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
 753     CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
 754                                             glyphInfos, uniChars, glyphs, len);
 755 }
 756 
 757 /*
 758  * Finds the advances and bounding boxes of the characters in the run,
 759  * cycles through all the bounds and calculates the maximum canvas space
 760  * required by the largest glyph.
 761  *
 762  * Creates a GlyphInfo struct with a malloc that also encapsulates the
 763  * image the struct points to.  This is done to meet memory layout
 764  * expectations in the Sun text rasterizer memory managment code.
 765  * The image immediately follows the struct physically in memory.
 766  */
 767 static inline void
 768 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
 769                       const CGGI_RenderingMode *mode,
 770                       const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
 771                       CGSize advances[], CGRect bboxes[], const CFIndex len)
 772 {
 773     AWTFont *font = strike->fAWTFont;
 774     CGAffineTransform tx = strike->fTx;
 775     JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
 776 
 777     JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
 778     CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
 779 
 780     size_t maxWidth = 1;
 781     size_t maxHeight = 1;
 782 
 783     CFIndex i;
 784     for (i = 0; i < len; i++)
 785     {
 786         if (uniChars[i] != 0)
 787         {
 788             glyphInfos[i] = 0L;
 789             continue; // will be handled later
 790         }
 791 
 792         CGSize advance = advances[i];
 793         CGRect bbox = bboxes[i];
 794 
 795         GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
 796 
 797         if (maxWidth < glyphInfo->width)   maxWidth = glyphInfo->width;
 798         if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
 799 
 800         glyphInfos[i] = ptr_to_jlong(glyphInfo);
 801     }
 802 
 803     CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,
 804                              glyphs, maxWidth, maxHeight, len);
 805 }
 806 
 807 
 808 #pragma mark --- Temporary Buffer Allocations and Initialization ---
 809 
 810 /*
 811  * This stage separates the already valid glyph codes from the unicode values
 812  * that need special handling - the rawGlyphCodes array is no longer used
 813  * after this stage.
 814  */
 815 static void
 816 CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,
 817                                         const AWTStrike *strike,
 818                                         const CGGI_RenderingMode *mode,
 819                                         jint rawGlyphCodes[],
 820                                         UnicodeScalarValue uniChars[], CGGlyph glyphs[],
 821                                         CGSize advances[], CGRect bboxes[],
 822                                         const CFIndex len)
 823 {
 824     CFIndex i;
 825     for (i = 0; i < len; i++) {
 826         jint code = rawGlyphCodes[i];
 827         if (code < 0) {
 828             glyphs[i] = 0;
 829             uniChars[i] = -code;
 830         } else {
 831             glyphs[i] = code;
 832             uniChars[i] = 0;
 833         }
 834     }
 835 
 836     CGGI_CreateGlyphInfos(glyphInfos, strike, mode,
 837                           uniChars, glyphs, advances, bboxes, len);
 838 
 839 #ifdef CGGI_DEBUG_HIT_COUNT
 840     static size_t hitCount = 0;
 841     hitCount++;
 842     printf("%d\n", (int)hitCount);
 843 #endif
 844 }
 845 
 846 /*
 847  * Conditionally stack allocates buffers for glyphs, bounding boxes,
 848  * and advances.  Unfortunately to use CG or CT in bulk runs (which is
 849  * faster than calling them per character), we have to copy into and out
 850  * of these buffers. Still a net win though.
 851  */
 852 void
 853 CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
 854                                 const AWTStrike *strike,
 855                                 jint rawGlyphCodes[], const CFIndex len)
 856 {
 857     const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);
 858 
 859     if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {
 860         CGRect bboxes[len];
 861         CGSize advances[len];
 862         CGGlyph glyphs[len];
 863         UnicodeScalarValue uniChars[len];
 864 
 865         CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
 866                                                 rawGlyphCodes, uniChars, glyphs,
 867                                                 advances, bboxes, len);
 868 
 869         return;
 870     }
 871 
 872     // just do one malloc, and carve it up for all the buffers
 873     void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *
 874                           sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len);
 875     if (buffer == NULL) {
 876         [[NSException exceptionWithName:NSMallocException
 877             reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];
 878     }
 879 
 880     CGRect *bboxes = (CGRect *)(buffer);
 881     CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);
 882     CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);
 883     UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len);
 884 
 885     CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
 886                                             rawGlyphCodes, uniChars, glyphs,
 887                                             advances, bboxes, len);
 888 
 889     free(buffer);
 890 }
--- EOF ---