< prev index next >

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

Print this page




  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 {


 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
 466     bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;


 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 {
 720     AWTFont *font = strike->fAWTFont;
 721     CGAffineTransform tx = strike->fTx;




  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 typedef enum {
  52     BLACK_ON_WHITE_STAGE,
  53     WHITE_ON_BLACK_STAGE
  54 } CGGI_GlyphRenderingStage;
  55 
  56 @interface CGGI_GlyphCanvas : NSObject {
  57 @public
  58     CGContextRef context;
  59     vImage_Buffer *image;
  60     CGGI_GlyphRenderingStage stage;
  61 }
  62 @end;
  63 
  64 @implementation CGGI_GlyphCanvas
  65 - (id) init {
  66     if (self = [super init]) {
  67         context = NULL;
  68         image = NULL;
  69         stage = BLACK_ON_WHITE_STAGE;
  70     }
  71     return self;
  72 }
  73 @end
  74 
  75 
  76 #pragma mark --- Debugging Helpers ---
  77 
  78 /*
  79  * These debug functions are only compiled when CGGI_DEBUG is activated.
  80  * They will print out a full UInt8 canvas and any pixels struck (assuming
  81  * the canvas is not too big).
  82  *
  83  * As another debug feature, the entire canvas will be filled with a light
  84  * alpha value so it is easy to see where the glyph painting regions are
  85  * at runtime.
  86  */
  87 
  88 #ifdef CGGI_DEBUG_DUMP
  89 static void
  90 DUMP_PIXELS(const char msg[], const UInt8 pixels[],
  91             const size_t bytesPerPixel, const int width, const int height)
  92 {


 196     printf("size: (%d, %d) pixelSize: %d\n",
 197            info->width, info->height, info->rowBytes / info->width);
 198     printf("adv: (%f, %f) top: (%f, %f)\n",
 199            info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);
 200 
 201 #ifdef CGGI_DEBUG_DUMP
 202     DUMP_PIXELS("Glyph Info Struct",
 203                 info->image, info->rowBytes / info->width,
 204                 info->width, info->height);
 205 #endif
 206 }
 207 
 208 #endif
 209 
 210 
 211 #pragma mark --- Font Rendering Mode Descriptors ---
 212 
 213 static inline void
 214 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
 215 {
 216     *(dst + 0) = 0xFF - (p >> 16 & 0xFF);  // red
 217     *(dst + 1) = 0xFF - (p >>  8 & 0xFF);  // green
 218     *(dst + 2) = 0xFF - (p & 0xFF);        // blue






 219 }
 220 
 221 static void
 222 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 223 {
 224     UInt32 *src = (UInt32 *)canvas->image->data;
 225     size_t srcRowWidth = canvas->image->width;
 226 
 227     UInt8 *dest = (UInt8 *)info->image;
 228     size_t destRowWidth = info->width;
 229 
 230     size_t height = info->height;
 231 
 232     size_t y;
 233     switch (canvas->stage) {
 234         case BLACK_ON_WHITE_STAGE:
 235             // fill empty glyph image with black-on-white glyph
 236             for (y = 0; y < height; y++) {
 237                 size_t destRow = y * destRowWidth * 3;
 238                 size_t srcRow = y * srcRowWidth;
 239 
 240                 size_t x;
 241                 for (x = 0; x < destRowWidth; x++) {





 242                     CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
 243                             dest + destRow + x * 3);
 244                 }
 245             }
 246             break;
 247         case WHITE_ON_BLACK_STAGE:
 248             // merge black-on-white glyph (which is already in the glyph image)
 249             // with white-on-black glyph
 250             for (y = 0; y < height; y++) {
 251                 size_t destRow = y * destRowWidth * 3;
 252                 size_t srcRow = y * srcRowWidth;
 253 
 254                 size_t x;
 255                 for (x = 0; x < destRowWidth; x++) {
 256                     UInt8* pDst = dest + destRow + x * 3;
 257                     UInt32 srcPixel = src[srcRow + x];
 258 
 259                     UInt16 r = *(pDst + 0) + (0xff & (srcPixel >> 16));
 260                     *(pDst + 0) = (UInt8)(r >> 1);
 261                     UInt16 g = *(pDst + 1) + (0xff & (srcPixel >>  8));
 262                     *(pDst + 1) = (UInt8)(g >> 1);
 263                     UInt16 b = *(pDst + 2) + (0xff & (srcPixel      ));
 264                     *(pDst + 2) = (UInt8)(b >> 1);
 265                 }
 266             }
 267             break;
 268     }
 269 }
 270 
 271 //static void CGGI_copyImageFromCanvasToAlphaInfo
 272 //(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 273 //{
 274 //    vImage_Buffer infoBuffer;
 275 //    infoBuffer.data = info->image;
 276 //    infoBuffer.width = info->width;
 277 //    infoBuffer.height = info->height;
 278 //    infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
 279 //
 280 //    UInt8 scrapPixel[info->width * info->height];
 281 //    vImage_Buffer scrapBuffer;
 282 //    scrapBuffer.data = &scrapPixel;
 283 //    scrapBuffer.width = info->width;
 284 //    scrapBuffer.height = info->height;
 285 //    scrapBuffer.rowBytes = info->width;
 286 //
 287 //    vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
 288 //        &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
 289 //}
 290 
 291 static inline UInt8
 292 CGGI_ConvertWBPixelToByteGray(UInt32 p) {
 293     return ((p >> 16 & 0xFF) + (p >> 8 & 0xFF) + (p & 0xFF)) / 3;
 294 }
 295 
 296 static inline UInt8
 297 CGGI_ConvertBWPixelToByteGray(UInt32 p)
 298 {
 299     return 0xFF - CGGI_ConvertWBPixelToByteGray(p);




 300 }
 301 
 302 static void
 303 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 304 {
 305     UInt32 *src = (UInt32 *)canvas->image->data;
 306     size_t srcRowWidth = canvas->image->width;
 307 
 308     UInt8 *dest = (UInt8 *)info->image;
 309     size_t destRowWidth = info->width;
 310 
 311     size_t height = info->height;
 312 
 313     size_t y;
 314     switch (canvas->stage) {
 315         case BLACK_ON_WHITE_STAGE:
 316             // fill empty glyph image with black-on-white glyph
 317             for (y = 0; y < height; y++) {
 318                 size_t destRow = y * destRowWidth;
 319                 size_t srcRow = y * srcRowWidth;
 320                 size_t x;
 321                 for (x = 0; x < destRowWidth; x++) {
 322                     UInt32 p = src[srcRow + x];
 323                     dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
 324                 }
 325             }
 326             break;
 327         case WHITE_ON_BLACK_STAGE:
 328             // merge black-on-white glyph (which is already in the glyph image)
 329             // with white-on-black glyph
 330             for (y = 0; y < height; y++) {
 331                 size_t destRow = y * destRowWidth;
 332                 size_t srcRow = y * srcRowWidth;
 333 
 334                 size_t x;
 335                 for (x = 0; x < destRowWidth; x++) {
 336                     UInt32 p = src[srcRow + x];
 337                     UInt16 gray = dest[destRow + x] + CGGI_ConvertWBPixelToByteGray(p);
 338                     dest[destRow + x] = (UInt8)(gray >> 1);
 339                 }
 340             }
 341             break;
 342     }
 343 }
 344 
 345 
 346 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
 347 
 348 typedef struct CGGI_GlyphInfoDescriptor {
 349     size_t pixelSize;
 350     void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
 351 } CGGI_GlyphInfoDescriptor;
 352 
 353 typedef struct CGGI_RenderingMode {
 354     CGGI_GlyphInfoDescriptor *glyphDescriptor;
 355     JRSFontRenderingStyle cgFontMode;
 356 } CGGI_RenderingMode;
 357 
 358 static CGGI_GlyphInfoDescriptor grey =
 359     { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
 360 static CGGI_GlyphInfoDescriptor rgb =
 361     { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
 362 
 363 static inline CGGI_RenderingMode
 364 CGGI_GetRenderingMode(const AWTStrike *strike)
 365 {
 366     CGGI_RenderingMode mode;
 367     mode.cgFontMode = strike->fStyle;
 368     NSException *e = nil;
 369 
 370     switch (strike->fAAStyle) {

 371     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
 372     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:


 373         mode.glyphDescriptor = &grey;
 374         break;
 375     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
 376     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
 377     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
 378     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
 379         mode.glyphDescriptor = &rgb;
 380         break;
 381     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
 382     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
 383     default:
 384         e = [NSException
 385                 exceptionWithName:@"IllegalArgumentException"
 386                 reason:@"Invalid hint value"
 387                 userInfo:nil];
 388         @throw e;
 389     }
 390 
 391     return mode;
 392 }
 393 
 394 
 395 #pragma mark --- Canvas Managment ---
 396 
 397 /*
 398  * Creates a new canvas of a fixed size, and initializes the CGContext as
 399  * an 32-bit ARGB BitmapContext with some generic RGB color space.
 400  */
 401 static inline void
 402 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
 403                 const vImagePixelCount width, const vImagePixelCount height,
 404                 const CGGI_RenderingMode* mode)
 405 {
 406     // our canvas is *always* 4-byte ARGB
 407     size_t bytesPerRow = width * sizeof(UInt32);
 408     size_t byteCount = bytesPerRow * height;
 409 
 410     canvas->image = malloc(sizeof(vImage_Buffer));
 411     canvas->image->width = width;
 412     canvas->image->height = height;
 413     canvas->image->rowBytes = bytesPerRow;
 414 
 415     canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
 416     if (canvas->image->data == NULL) {
 417         [[NSException exceptionWithName:NSMallocException
 418             reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
 419     }
 420 
 421     canvas->stage = BLACK_ON_WHITE_STAGE;
 422 
 423     uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
 424     if (mode->glyphDescriptor == &rgb) {
 425         bmpInfo |= kCGBitmapByteOrder32Host;
 426     }
 427 
 428     CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
 429     canvas->context = CGBitmapContextCreate(canvas->image->data,
 430                                             width, height, 8, bytesPerRow,
 431                                             colorSpace,
 432                                             bmpInfo);
 433 
 434     // set foreground color
 435     CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
 436     
 437     CGContextSetFontSize(canvas->context, 1);
 438     CGContextSaveGState(canvas->context);
 439 
 440     CGColorSpaceRelease(colorSpace);
 441 }
 442 
 443 /*
 444  * Releases the BitmapContext and the associated memory backing it.
 445  */
 446 static inline void
 447 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
 448 {
 449     if (canvas->context != NULL) {
 450         CGContextRelease(canvas->context);
 451     }
 452 
 453     if (canvas->image != NULL) {
 454         if (canvas->image->data != NULL) {
 455             free(canvas->image->data);
 456         }
 457         free(canvas->image);
 458     }
 459 }
 460 
 461 /*
 462  * This is the slack space that is preallocated for the global GlyphCanvas
 463  * when it needs to be expanded. It has been set somewhat liberally to
 464  * avoid re-upsizing frequently.
 465  */
 466 #define CGGI_GLYPH_CANVAS_SLACK 2.5
 467 
 468 /*
 469  * Quick and easy inline to check if this canvas is big enough.
 470  */
 471 static inline void
 472 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
 473         const vImagePixelCount height,
 474         const CGGI_RenderingMode* mode)
 475 {
 476     if (canvas->image != NULL &&
 477         width  < canvas->image->width &&
 478         height < canvas->image->height)
 479     {
 480         return;
 481     }
 482 
 483     // if we don't have enough space to strike the largest glyph in the
 484     // run, resize the canvas
 485     CGGI_FreeCanvas(canvas);
 486     CGGI_InitCanvas(canvas,
 487                     width * CGGI_GLYPH_CANVAS_SLACK,
 488                     height * CGGI_GLYPH_CANVAS_SLACK,
 489                     mode);
 490     JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
 491 }
 492 
 493 /*
 494  * Clear the canvas by blitting white only into the region of interest
 495  * (the rect which we will copy out of once the glyph is struck).
 496  */
 497 static inline void
 498 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 499 {
 500     vImage_Buffer canvasRectToClear;
 501     canvasRectToClear.data = canvas->image->data;
 502     canvasRectToClear.height = info->height;
 503     canvasRectToClear.width = info->width;
 504     // use the row stride of the canvas, not the info
 505     canvasRectToClear.rowBytes = canvas->image->rowBytes;
 506 
 507     // clean the canvas
 508 #ifdef CGGI_DEBUG
 509     Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
 510     Pixel_8888 opaqueBlack = { 0x10, 0x10, 0x10, 0xF0 };
 511 #else
 512     Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
 513     Pixel_8888 opaqueBlack = { 0x00, 0x00, 0x00, 0xFF };
 514 #endif
 515 
 516     // clear canvas background and set foreground color
 517     switch(canvas->stage) {
 518         case BLACK_ON_WHITE_STAGE:
 519             vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
 520             CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
 521             break;
 522         case WHITE_ON_BLACK_STAGE:
 523             vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueBlack, kvImageNoFlags);
 524             CGContextSetRGBFillColor(canvas->context, 1.0f, 1.0f, 1.0f, 1.0f);
 525             break;
 526     }
 527     CGContextSaveGState(canvas->context);
 528 }
 529 
 530 
 531 #pragma mark --- GlyphInfo Creation & Copy Functions ---
 532 
 533 /*
 534  * Creates a GlyphInfo with exactly the correct size image and measurements.
 535  */
 536 #define CGGI_GLYPH_BBOX_PADDING 2.0f
 537 static inline GlyphInfo *
 538 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
 539                             const AWTStrike *strike,
 540                             const CGGI_RenderingMode *mode)
 541 {
 542     size_t pixelSize = mode->glyphDescriptor->pixelSize;
 543 
 544     // adjust the bounding box to be 1px bigger on each side than what
 545     // CGFont-whatever suggests - because it gives a bounding box that
 546     // is too tight
 547     bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;


 641         glyph = glyphTmp[0];
 642     } else {
 643         UTF16Char charRef;
 644         charRef = (UTF16Char) uniChar; // truncate.
 645         fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
 646     }
 647 
 648     CGAffineTransform tx = strike->fTx;
 649     JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
 650 
 651     CGRect bbox;
 652     JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
 653 
 654     CGSize advance;
 655     CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
 656 
 657     // create the Sun2D GlyphInfo we are going to strike into
 658     GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
 659 
 660     // fix the context size, just in case the substituted character is unexpectedly large
 661     CGGI_SizeCanvas(canvas, info->width, info->height, mode);
 662 
 663     // align the transform for the real CoreText strike
 664     CGContextSetTextMatrix(canvas->context, strike->fAltTx);
 665 
 666     const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
 667     CGContextSetFont(canvas->context, cgFallback);
 668     CFRelease(cgFallback);
 669 
 670     // clean the canvas - align, strike, and copy the glyph from the canvas into the info
 671     CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
 672 
 673     // restore the state of the world
 674     CGContextRestoreGState(canvas->context);
 675 
 676     CFRelease(fallback);
 677 #ifdef CGGI_DEBUG
 678     DUMP_GLYPHINFO(info);
 679 #endif
 680 
 681 #ifdef CGGI_DEBUG_DUMP


 717         if (info != NULL) {
 718             CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
 719         } else {
 720             info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
 721             glyphInfos[i] = ptr_to_jlong(info);
 722         }
 723 #ifdef CGGI_DEBUG
 724         DUMP_GLYPHINFO(info);
 725 #endif
 726 
 727 #ifdef CGGI_DEBUG_DUMP
 728         DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 729 #endif
 730     }
 731 #ifdef CGGI_DEBUG_DUMP
 732     DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
 733     PRINT_CGSTATES_INFO(canvas->context);
 734 #endif
 735 }
 736 
 737 static NSString *threadLocalAACanvasKey =
 738     @"Java CoreGraphics Text Renderer Cached Canvas for AA";
 739 
 740 static NSString *threadLocalLCDCanvasKey =
 741     @"Java CoreGraphics Text Renderer Cached Canvas for LCD";
 742 
 743 /*
 744  * This is the maximum length and height times the above slack squared
 745  * to determine if we go with the global canvas, or malloc one on the spot.
 746  */
 747 #define CGGI_GLYPH_CANVAS_MAX 100
 748 
 749 /*
 750  * Based on the space needed to strike the largest character in the run,
 751  * either use the global shared canvas, or make one up on the spot, strike
 752  * the glyphs, and destroy it.
 753  */
 754 static inline void
 755 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
 756                          const CGGI_RenderingMode *mode,
 757                          const UniChar uniChars[], const CGGlyph glyphs[],
 758                          const size_t maxWidth, const size_t maxHeight,
 759                          const CFIndex len)
 760 {
 761     if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
 762         CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
 763     {
 764         CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
 765         CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
 766         // create black-on-white glyph image
 767         CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
 768                 mode, glyphInfos, uniChars,
 769                 glyphs, len);
 770 
 771         // merge glyph image with white-on-black glyph
 772         tmpCanvas->stage = WHITE_ON_BLACK_STAGE;
 773         CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
 774                                                 mode, glyphInfos, uniChars,
 775                                                 glyphs, len);
 776         CGGI_FreeCanvas(tmpCanvas);
 777 
 778         [tmpCanvas release];
 779         return;
 780     }

 781     NSMutableDictionary *threadDict =
 782         [[NSThread currentThread] threadDictionary];
 783 
 784     NSString* theKey = (mode->glyphDescriptor == &rgb) ?
 785         threadLocalLCDCanvasKey : threadLocalAACanvasKey;
 786     
 787     CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
 788     if (canvas == nil) {
 789         canvas = [[CGGI_GlyphCanvas alloc] init];
 790         [threadDict setObject:canvas forKey:theKey];
 791     }
 792 
 793     CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
 794     // create black-on-white glyph image
 795     canvas->stage = BLACK_ON_WHITE_STAGE;
 796     CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
 797                                             glyphInfos, uniChars, glyphs, len);
 798 
 799     // merge glyph image with white-on-black glyph
 800     canvas->stage = WHITE_ON_BLACK_STAGE;
 801     CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
 802                                             glyphInfos, uniChars, glyphs, len);
 803 
 804 }
 805 
 806 /*
 807  * Finds the advances and bounding boxes of the characters in the run,
 808  * cycles through all the bounds and calculates the maximum canvas space
 809  * required by the largest glyph.
 810  *
 811  * Creates a GlyphInfo struct with a malloc that also encapsulates the
 812  * image the struct points to.  This is done to meet memory layout
 813  * expectations in the Sun text rasterizer memory managment code.
 814  * The image immediately follows the struct physically in memory.
 815  */
 816 static inline void
 817 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
 818                       const CGGI_RenderingMode *mode,
 819                       const UniChar uniChars[], const CGGlyph glyphs[],
 820                       CGSize advances[], CGRect bboxes[], const CFIndex len)
 821 {
 822     AWTFont *font = strike->fAWTFont;
 823     CGAffineTransform tx = strike->fTx;


< prev index next >