< prev index next >

src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m

Print this page




 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 
 199 static inline void
 200 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
 201 {
 202 #if __LITTLE_ENDIAN__
 203     *(dst + 2) = 0xFF - (p >> 24 & 0xFF);
 204     *(dst + 1) = 0xFF - (p >> 16 & 0xFF);
 205     *(dst) = 0xFF - (p >> 8 & 0xFF);
 206 #else
 207     *(dst) = 0xFF - (p >> 16 & 0xFF);
 208     *(dst + 1) = 0xFF - (p >> 8 & 0xFF);
 209     *(dst + 2) = 0xFF - (p & 0xFF);
 210 #endif
 211 }
 212 
 213 static void
 214 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 215 {
 216     UInt32 *src = (UInt32 *)canvas->image->data;
 217     size_t srcRowWidth = canvas->image->width;
 218 
 219     UInt8 *dest = (UInt8 *)info->image;
 220     size_t destRowWidth = info->width;
 221 
 222     size_t height = info->height;
 223 
 224     size_t y;


 225     for (y = 0; y < height; y++) {
 226         size_t destRow = y * destRowWidth * 3;
 227         size_t srcRow = y * srcRowWidth;
 228 
 229         size_t x;
 230         for (x = 0; x < destRowWidth; x++) {
 231             // size_t x3 = x * 3;
 232             // UInt32 p = src[srcRow + x];
 233             // dest[destRow + x3] = 0xFF - (p >> 16 & 0xFF);
 234             // dest[destRow + x3 + 1] = 0xFF - (p >> 8 & 0xFF);
 235             // dest[destRow + x3 + 2] = 0xFF - (p & 0xFF);
 236             CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
 237                                          dest + destRow + x * 3);
 238         }
 239     }
 240 }
 241 
 242 //static void CGGI_copyImageFromCanvasToAlphaInfo
 243 //(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 244 //{
 245 //    vImage_Buffer infoBuffer;
 246 //    infoBuffer.data = info->image;
 247 //    infoBuffer.width = info->width;
 248 //    infoBuffer.height = info->height;
 249 //    infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
 250 //
 251 //    UInt8 scrapPixel[info->width * info->height];
 252 //    vImage_Buffer scrapBuffer;
 253 //    scrapBuffer.data = &scrapPixel;
 254 //    scrapBuffer.width = info->width;
 255 //    scrapBuffer.height = info->height;
 256 //    scrapBuffer.rowBytes = info->width;
 257 //
 258 //    vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
 259 //        &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
 260 //}
 261 
 262 static inline UInt8
 263 CGGI_ConvertPixelToGreyBit(UInt32 p)
 264 {
 265 #ifdef __LITTLE_ENDIAN__
 266     return 0xFF - ((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3;
 267 #else
 268     return 0xFF - ((p >> 16 & 0xFF) + (p >> 8 & 0xFF) + (p & 0xFF)) / 3;
 269 #endif
 270 }
 271 
 272 static void
 273 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 274 {
 275     UInt32 *src = (UInt32 *)canvas->image->data;
 276     size_t srcRowWidth = canvas->image->width;
 277 
 278     UInt8 *dest = (UInt8 *)info->image;
 279     size_t destRowWidth = info->width;
 280 
 281     size_t height = info->height;
 282 
 283     size_t y;


 284     for (y = 0; y < height; y++) {
 285         size_t destRow = y * destRowWidth;
 286         size_t srcRow = y * srcRowWidth;
 287 
 288         size_t x;
 289         for (x = 0; x < destRowWidth; x++) {
 290             UInt32 p = src[srcRow + x];
 291             dest[destRow + x] = CGGI_ConvertPixelToGreyBit(p);
 292         }
 293     }
 294 }
 295 
 296 
 297 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
 298 
 299 typedef struct CGGI_GlyphInfoDescriptor {
 300     size_t pixelSize;
 301     void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
 302 } CGGI_GlyphInfoDescriptor;
 303 
 304 typedef struct CGGI_RenderingMode {
 305     CGGI_GlyphInfoDescriptor *glyphDescriptor;
 306     JRSFontRenderingStyle cgFontMode;
 307 } CGGI_RenderingMode;
 308 
 309 static CGGI_GlyphInfoDescriptor grey =
 310     { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
 311 static CGGI_GlyphInfoDescriptor rgb =
 312     { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
 313 
 314 static inline CGGI_RenderingMode
 315 CGGI_GetRenderingMode(const AWTStrike *strike)
 316 {
 317     CGGI_RenderingMode mode;
 318     mode.cgFontMode = strike->fStyle;

 319 
 320     switch (strike->fAAStyle) {
 321     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
 322     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
 323     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
 324     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
 325     default:
 326         mode.glyphDescriptor = &grey;
 327         break;
 328     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
 329     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
 330     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
 331     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
 332         mode.glyphDescriptor = &rgb;
 333         break;











 334     }
 335 
 336     return mode;
 337 }
 338 
 339 
 340 #pragma mark --- Canvas Managment ---
 341 
 342 /*
 343  * Creates a new canvas of a fixed size, and initializes the CGContext as
 344  * an 32-bit ARGB BitmapContext with some generic RGB color space.
 345  */
 346 static inline void
 347 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
 348                 const vImagePixelCount width, const vImagePixelCount height)

 349 {
 350     // our canvas is *always* 4-byte ARGB
 351     size_t bytesPerRow = width * sizeof(UInt32);
 352     size_t byteCount = bytesPerRow * height;
 353 
 354     canvas->image = malloc(sizeof(vImage_Buffer));
 355     canvas->image->width = width;
 356     canvas->image->height = height;
 357     canvas->image->rowBytes = bytesPerRow;
 358 
 359     canvas->image->data = (void *)calloc(byteCount, sizeof(UInt32));
 360     if (canvas->image->data == NULL) {
 361         [[NSException exceptionWithName:NSMallocException
 362             reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
 363     }
 364 
 365     CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);





 366     canvas->context = CGBitmapContextCreate(canvas->image->data,
 367                                             width, height, 8, bytesPerRow,
 368                                             colorSpace,
 369                                             kCGImageAlphaPremultipliedFirst);
 370 

 371     CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);

 372     CGContextSetFontSize(canvas->context, 1);
 373     CGContextSaveGState(canvas->context);
 374 
 375     CGColorSpaceRelease(colorSpace);
 376 }
 377 
 378 /*
 379  * Releases the BitmapContext and the associated memory backing it.
 380  */
 381 static inline void
 382 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
 383 {
 384     if (canvas->context != NULL) {
 385         CGContextRelease(canvas->context);
 386     }
 387 
 388     if (canvas->image != NULL) {
 389         if (canvas->image->data != NULL) {
 390             free(canvas->image->data);
 391         }
 392         free(canvas->image);
 393     }
 394 }
 395 
 396 /*
 397  * This is the slack space that is preallocated for the global GlyphCanvas
 398  * when it needs to be expanded. It has been set somewhat liberally to
 399  * avoid re-upsizing frequently.
 400  */
 401 #define CGGI_GLYPH_CANVAS_SLACK 2.5
 402 
 403 /*
 404  * Quick and easy inline to check if this canvas is big enough.
 405  */
 406 static inline void
 407 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vImagePixelCount height, const JRSFontRenderingStyle style)


 408 {
 409     if (canvas->image != NULL &&
 410         width  < canvas->image->width &&
 411         height < canvas->image->height)
 412     {
 413         return;
 414     }
 415 
 416     // if we don't have enough space to strike the largest glyph in the
 417     // run, resize the canvas
 418     CGGI_FreeCanvas(canvas);
 419     CGGI_InitCanvas(canvas,
 420                     width * CGGI_GLYPH_CANVAS_SLACK,
 421                     height * CGGI_GLYPH_CANVAS_SLACK);
 422     JRSFontSetRenderingStyleOnContext(canvas->context, style);

 423 }
 424 
 425 /*
 426  * Clear the canvas by blitting white only into the region of interest
 427  * (the rect which we will copy out of once the glyph is struck).
 428  */
 429 static inline void
 430 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 431 {
 432     vImage_Buffer canvasRectToClear;
 433     canvasRectToClear.data = canvas->image->data;
 434     canvasRectToClear.height = info->height;
 435     canvasRectToClear.width = info->width;
 436     // use the row stride of the canvas, not the info
 437     canvasRectToClear.rowBytes = canvas->image->rowBytes;
 438 
 439     // clean the canvas
 440 #ifdef CGGI_DEBUG
 441     Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
 442 #else
 443     Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
 444 #endif
 445 

 446     vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
 447 }
 448 
 449 
 450 #pragma mark --- GlyphInfo Creation & Copy Functions ---
 451 
 452 /*
 453  * Creates a GlyphInfo with exactly the correct size image and measurements.
 454  */
 455 #define CGGI_GLYPH_BBOX_PADDING 2.0f
 456 static inline GlyphInfo *
 457 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
 458                             const AWTStrike *strike,
 459                             const CGGI_RenderingMode *mode)
 460 {
 461     size_t pixelSize = mode->glyphDescriptor->pixelSize;
 462 
 463     // adjust the bounding box to be 1px bigger on each side than what
 464     // CGFont-whatever suggests - because it gives a bounding box that
 465     // is too tight


 560         glyph = glyphTmp[0];
 561     } else {
 562         UTF16Char charRef;
 563         charRef = (UTF16Char) uniChar; // truncate.
 564         fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
 565     }
 566 
 567     CGAffineTransform tx = strike->fTx;
 568     JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
 569 
 570     CGRect bbox;
 571     JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
 572 
 573     CGSize advance;
 574     CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
 575 
 576     // create the Sun2D GlyphInfo we are going to strike into
 577     GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
 578 
 579     // fix the context size, just in case the substituted character is unexpectedly large
 580     CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode);
 581 
 582     // align the transform for the real CoreText strike
 583     CGContextSetTextMatrix(canvas->context, strike->fAltTx);
 584 
 585     const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
 586     CGContextSetFont(canvas->context, cgFallback);
 587     CFRelease(cgFallback);
 588 
 589     // clean the canvas - align, strike, and copy the glyph from the canvas into the info
 590     CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
 591 
 592     // restore the state of the world
 593     CGContextRestoreGState(canvas->context);
 594 
 595     CFRelease(fallback);
 596 #ifdef CGGI_DEBUG
 597     DUMP_GLYPHINFO(info);
 598 #endif
 599 
 600 #ifdef CGGI_DEBUG_DUMP


 636         if (info != NULL) {
 637             CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
 638         } else {
 639             info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
 640             glyphInfos[i] = ptr_to_jlong(info);
 641         }
 642 #ifdef CGGI_DEBUG
 643         DUMP_GLYPHINFO(info);
 644 #endif
 645 
 646 #ifdef CGGI_DEBUG_DUMP
 647         DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 648 #endif
 649     }
 650 #ifdef CGGI_DEBUG_DUMP
 651     DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 652     PRINT_CGSTATES_INFO(canvas->context);
 653 #endif
 654 }
 655 
 656 static NSString *threadLocalCanvasKey =
 657     @"Java CoreGraphics Text Renderer Cached Canvas";



 658 
 659 /*
 660  * This is the maximum length and height times the above slack squared
 661  * to determine if we go with the global canvas, or malloc one on the spot.
 662  */
 663 #define CGGI_GLYPH_CANVAS_MAX 100
 664 
 665 /*
 666  * Based on the space needed to strike the largest character in the run,
 667  * either use the global shared canvas, or make one up on the spot, strike
 668  * the glyphs, and destroy it.
 669  */
 670 static inline void
 671 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
 672                          const CGGI_RenderingMode *mode,
 673                          const UniChar uniChars[], const CGGlyph glyphs[],
 674                          const size_t maxWidth, const size_t maxHeight,
 675                          const CFIndex len)
 676 {
 677     if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
 678         CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
 679     {
 680         CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
 681         CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight);
 682         CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
 683                                                 mode, glyphInfos, uniChars,
 684                                                 glyphs, len);
 685         CGGI_FreeCanvas(tmpCanvas);
 686 
 687         [tmpCanvas release];
 688         return;
 689     }
 690 
 691     NSMutableDictionary *threadDict =
 692         [[NSThread currentThread] threadDictionary];
 693     CGGI_GlyphCanvas *canvas = [threadDict objectForKey:threadLocalCanvasKey];




 694     if (canvas == nil) {
 695         canvas = [[CGGI_GlyphCanvas alloc] init];
 696         [threadDict setObject:canvas forKey:threadLocalCanvasKey];
 697     }
 698 
 699     CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode->cgFontMode);
 700     CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
 701                                             glyphInfos, uniChars, glyphs, len);
 702 }
 703 
 704 /*
 705  * Finds the advances and bounding boxes of the characters in the run,
 706  * cycles through all the bounds and calculates the maximum canvas space
 707  * required by the largest glyph.
 708  *
 709  * Creates a GlyphInfo struct with a malloc that also encapsulates the
 710  * image the struct points to.  This is done to meet memory layout
 711  * expectations in the Sun text rasterizer memory managment code.
 712  * The image immediately follows the struct physically in memory.
 713  */
 714 static inline void
 715 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
 716                       const CGGI_RenderingMode *mode,
 717                       const UniChar uniChars[], const CGGlyph glyphs[],
 718                       CGSize advances[], CGRect bboxes[], const CFIndex len)
 719 {




 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         const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA");
 207         if (pGammaEnv != NULL) {
 208             reverseGamma = atol(pGammaEnv);
 209         }
 210         
 211         if (reverseGamma < 100 || reverseGamma > 250) {
 212             reverseGamma = 180;
 213         }
 214         
 215         gamma = 100.0 / reverseGamma;
 216         for (int i = 0; i < 256; i++) {
 217             double x = ((double)i) / 255.0;
 218             reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma));
 219         }
 220     }
 221     return reverseGammaLut;
 222 }
 223 
 224 static inline void
 225 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
 226 {
 227     UInt8* lut = getReverseGammaLut();
 228     
 229     *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)];  // red
 230     *(dst + 1) = lut[0xFF - (p >>  8 & 0xFF)];  // green
 231     *(dst + 2) = lut[0xFF - (p & 0xFF)];        // blue




 232 }
 233 
 234 static void
 235 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 236 {
 237     UInt32 *src = (UInt32 *)canvas->image->data;
 238     size_t srcRowWidth = canvas->image->width;
 239 
 240     UInt8 *dest = (UInt8 *)info->image;
 241     size_t destRowWidth = info->width;
 242 
 243     size_t height = info->height;
 244 
 245     size_t y;
 246     
 247     // fill empty glyph image with black-on-white glyph
 248     for (y = 0; y < height; y++) {
 249         size_t destRow = y * destRowWidth * 3;
 250         size_t srcRow = y * srcRowWidth;
 251 
 252         size_t x;
 253         for (x = 0; x < destRowWidth; x++) {





 254             CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
 255                                          dest + destRow + x * 3);
 256         }
 257     }
 258 }
 259 
 260 //static void CGGI_copyImageFromCanvasToAlphaInfo
 261 //(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 262 //{
 263 //    vImage_Buffer infoBuffer;
 264 //    infoBuffer.data = info->image;
 265 //    infoBuffer.width = info->width;
 266 //    infoBuffer.height = info->height;
 267 //    infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
 268 //
 269 //    UInt8 scrapPixel[info->width * info->height];
 270 //    vImage_Buffer scrapBuffer;
 271 //    scrapBuffer.data = &scrapPixel;
 272 //    scrapBuffer.width = info->width;
 273 //    scrapBuffer.height = info->height;
 274 //    scrapBuffer.rowBytes = info->width;
 275 //
 276 //    vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
 277 //        &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
 278 //}
 279 
 280 static inline UInt8
 281 CGGI_ConvertBWPixelToByteGray(UInt32 p)
 282 {
 283     return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3);




 284 }
 285 
 286 static void
 287 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 288 {
 289     UInt32 *src = (UInt32 *)canvas->image->data;
 290     size_t srcRowWidth = canvas->image->width;
 291 
 292     UInt8 *dest = (UInt8 *)info->image;
 293     size_t destRowWidth = info->width;
 294 
 295     size_t height = info->height;
 296 
 297     size_t y;
 298     
 299     // fill empty glyph image with black-on-white glyph
 300     for (y = 0; y < height; y++) {
 301         size_t destRow = y * destRowWidth;
 302         size_t srcRow = y * srcRowWidth;

 303         size_t x;
 304         for (x = 0; x < destRowWidth; x++) {
 305             UInt32 p = src[srcRow + x];
 306             dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
 307         }
 308     }
 309 }
 310 
 311 
 312 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
 313 
 314 typedef struct CGGI_GlyphInfoDescriptor {
 315     size_t pixelSize;
 316     void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
 317 } CGGI_GlyphInfoDescriptor;
 318 
 319 typedef struct CGGI_RenderingMode {
 320     CGGI_GlyphInfoDescriptor *glyphDescriptor;
 321     JRSFontRenderingStyle cgFontMode;
 322 } CGGI_RenderingMode;
 323 
 324 static CGGI_GlyphInfoDescriptor grey =
 325     { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
 326 static CGGI_GlyphInfoDescriptor rgb =
 327     { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
 328 
 329 static inline CGGI_RenderingMode
 330 CGGI_GetRenderingMode(const AWTStrike *strike)
 331 {
 332     CGGI_RenderingMode mode;
 333     mode.cgFontMode = strike->fStyle;
 334     NSException *e = nil;
 335 
 336     switch (strike->fAAStyle) {

 337     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
 338     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:


 339         mode.glyphDescriptor = &grey;
 340         break;
 341     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
 342     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
 343     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
 344     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
 345         mode.glyphDescriptor = &rgb;
 346         break;
 347     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
 348     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
 349     default:
 350         /* we expect that text antialiasing hint has been already
 351          * evaluated. Report an error if we get 'unevaluated' hint here.
 352          */
 353         e = [NSException
 354                 exceptionWithName:@"IllegalArgumentException"
 355                 reason:@"Invalid hint value"
 356                 userInfo:nil];
 357         @throw e;
 358     }
 359 
 360     return mode;
 361 }
 362 
 363 
 364 #pragma mark --- Canvas Managment ---
 365 
 366 /*
 367  * Creates a new canvas of a fixed size, and initializes the CGContext as
 368  * an 32-bit ARGB BitmapContext with some generic RGB color space.
 369  */
 370 static inline void
 371 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
 372                 const vImagePixelCount width, const vImagePixelCount height,
 373                 const CGGI_RenderingMode* mode)
 374 {
 375     // our canvas is *always* 4-byte ARGB
 376     size_t bytesPerRow = width * sizeof(UInt32);
 377     size_t byteCount = bytesPerRow * height;
 378 
 379     canvas->image = malloc(sizeof(vImage_Buffer));
 380     canvas->image->width = width;
 381     canvas->image->height = height;
 382     canvas->image->rowBytes = bytesPerRow;
 383 
 384     canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
 385     if (canvas->image->data == NULL) {
 386         [[NSException exceptionWithName:NSMallocException
 387             reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
 388     }
 389 
 390     uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
 391     if (mode->glyphDescriptor == &rgb) {
 392         bmpInfo |= kCGBitmapByteOrder32Host;
 393     }
 394 
 395     CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
 396     canvas->context = CGBitmapContextCreate(canvas->image->data,
 397                                             width, height, 8, bytesPerRow,
 398                                             colorSpace,
 399                                             bmpInfo);
 400 
 401     // set foreground color
 402     CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
 403     
 404     CGContextSetFontSize(canvas->context, 1);
 405     CGContextSaveGState(canvas->context);
 406 
 407     CGColorSpaceRelease(colorSpace);
 408 }
 409 
 410 /*
 411  * Releases the BitmapContext and the associated memory backing it.
 412  */
 413 static inline void
 414 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
 415 {
 416     if (canvas->context != NULL) {
 417         CGContextRelease(canvas->context);
 418     }
 419 
 420     if (canvas->image != NULL) {
 421         if (canvas->image->data != NULL) {
 422             free(canvas->image->data);
 423         }
 424         free(canvas->image);
 425     }
 426 }
 427 
 428 /*
 429  * This is the slack space that is preallocated for the global GlyphCanvas
 430  * when it needs to be expanded. It has been set somewhat liberally to
 431  * avoid re-upsizing frequently.
 432  */
 433 #define CGGI_GLYPH_CANVAS_SLACK 2.5
 434 
 435 /*
 436  * Quick and easy inline to check if this canvas is big enough.
 437  */
 438 static inline void
 439 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
 440         const vImagePixelCount height,
 441         const CGGI_RenderingMode* mode)
 442 {
 443     if (canvas->image != NULL &&
 444         width  < canvas->image->width &&
 445         height < canvas->image->height)
 446     {
 447         return;
 448     }
 449 
 450     // if we don't have enough space to strike the largest glyph in the
 451     // run, resize the canvas
 452     CGGI_FreeCanvas(canvas);
 453     CGGI_InitCanvas(canvas,
 454                     width * CGGI_GLYPH_CANVAS_SLACK,
 455                     height * CGGI_GLYPH_CANVAS_SLACK,
 456                     mode);
 457     JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
 458 }
 459 
 460 /*
 461  * Clear the canvas by blitting white only into the region of interest
 462  * (the rect which we will copy out of once the glyph is struck).
 463  */
 464 static inline void
 465 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 466 {
 467     vImage_Buffer canvasRectToClear;
 468     canvasRectToClear.data = canvas->image->data;
 469     canvasRectToClear.height = info->height;
 470     canvasRectToClear.width = info->width;
 471     // use the row stride of the canvas, not the info
 472     canvasRectToClear.rowBytes = canvas->image->rowBytes;
 473 
 474     // clean the canvas
 475 #ifdef CGGI_DEBUG
 476     Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
 477 #else
 478     Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
 479 #endif
 480 
 481     // clear canvas background and set foreground color
 482     vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
 483 }
 484 
 485 
 486 #pragma mark --- GlyphInfo Creation & Copy Functions ---
 487 
 488 /*
 489  * Creates a GlyphInfo with exactly the correct size image and measurements.
 490  */
 491 #define CGGI_GLYPH_BBOX_PADDING 2.0f
 492 static inline GlyphInfo *
 493 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
 494                             const AWTStrike *strike,
 495                             const CGGI_RenderingMode *mode)
 496 {
 497     size_t pixelSize = mode->glyphDescriptor->pixelSize;
 498 
 499     // adjust the bounding box to be 1px bigger on each side than what
 500     // CGFont-whatever suggests - because it gives a bounding box that
 501     // is too tight


 596         glyph = glyphTmp[0];
 597     } else {
 598         UTF16Char charRef;
 599         charRef = (UTF16Char) uniChar; // truncate.
 600         fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
 601     }
 602 
 603     CGAffineTransform tx = strike->fTx;
 604     JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
 605 
 606     CGRect bbox;
 607     JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
 608 
 609     CGSize advance;
 610     CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
 611 
 612     // create the Sun2D GlyphInfo we are going to strike into
 613     GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
 614 
 615     // fix the context size, just in case the substituted character is unexpectedly large
 616     CGGI_SizeCanvas(canvas, info->width, info->height, mode);
 617 
 618     // align the transform for the real CoreText strike
 619     CGContextSetTextMatrix(canvas->context, strike->fAltTx);
 620 
 621     const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
 622     CGContextSetFont(canvas->context, cgFallback);
 623     CFRelease(cgFallback);
 624 
 625     // clean the canvas - align, strike, and copy the glyph from the canvas into the info
 626     CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
 627 
 628     // restore the state of the world
 629     CGContextRestoreGState(canvas->context);
 630 
 631     CFRelease(fallback);
 632 #ifdef CGGI_DEBUG
 633     DUMP_GLYPHINFO(info);
 634 #endif
 635 
 636 #ifdef CGGI_DEBUG_DUMP


 672         if (info != NULL) {
 673             CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
 674         } else {
 675             info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
 676             glyphInfos[i] = ptr_to_jlong(info);
 677         }
 678 #ifdef CGGI_DEBUG
 679         DUMP_GLYPHINFO(info);
 680 #endif
 681 
 682 #ifdef CGGI_DEBUG_DUMP
 683         DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 684 #endif
 685     }
 686 #ifdef CGGI_DEBUG_DUMP
 687     DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 688     PRINT_CGSTATES_INFO(canvas->context);
 689 #endif
 690 }
 691 
 692 static NSString *threadLocalAACanvasKey =
 693     @"Java CoreGraphics Text Renderer Cached Canvas for AA";
 694 
 695 static NSString *threadLocalLCDCanvasKey =
 696     @"Java CoreGraphics Text Renderer Cached Canvas for LCD";
 697 
 698 /*
 699  * This is the maximum length and height times the above slack squared
 700  * to determine if we go with the global canvas, or malloc one on the spot.
 701  */
 702 #define CGGI_GLYPH_CANVAS_MAX 100
 703 
 704 /*
 705  * Based on the space needed to strike the largest character in the run,
 706  * either use the global shared canvas, or make one up on the spot, strike
 707  * the glyphs, and destroy it.
 708  */
 709 static inline void
 710 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
 711                          const CGGI_RenderingMode *mode,
 712                          const UniChar uniChars[], const CGGlyph glyphs[],
 713                          const size_t maxWidth, const size_t maxHeight,
 714                          const CFIndex len)
 715 {
 716     if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
 717         CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
 718     {
 719         CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
 720         CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
 721         CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
 722                 mode, glyphInfos, uniChars,
 723                 glyphs, len);
 724         CGGI_FreeCanvas(tmpCanvas);
 725 
 726         [tmpCanvas release];
 727         return;
 728     }

 729     NSMutableDictionary *threadDict =
 730         [[NSThread currentThread] threadDictionary];
 731 
 732     NSString* theKey = (mode->glyphDescriptor == &rgb) ?
 733         threadLocalLCDCanvasKey : threadLocalAACanvasKey;
 734     
 735     CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
 736     if (canvas == nil) {
 737         canvas = [[CGGI_GlyphCanvas alloc] init];
 738         [threadDict setObject:canvas forKey:theKey];
 739     }
 740 
 741     CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
 742     CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
 743                                             glyphInfos, uniChars, glyphs, len);
 744 }
 745 
 746 /*
 747  * Finds the advances and bounding boxes of the characters in the run,
 748  * cycles through all the bounds and calculates the maximum canvas space
 749  * required by the largest glyph.
 750  *
 751  * Creates a GlyphInfo struct with a malloc that also encapsulates the
 752  * image the struct points to.  This is done to meet memory layout
 753  * expectations in the Sun text rasterizer memory managment code.
 754  * The image immediately follows the struct physically in memory.
 755  */
 756 static inline void
 757 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
 758                       const CGGI_RenderingMode *mode,
 759                       const UniChar uniChars[], const CGGlyph glyphs[],
 760                       CGSize advances[], CGRect bboxes[], const CFIndex len)
 761 {


< prev index next >