< prev index next >

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

Print this page




  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 {


 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;




  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 CGGI_LCD_GLYPH_IN_TWO_STAGES
  41 
  42 #define PRINT_TX(x) \
  43     NSLog(@"(%f, %f, %f, %f, %f, %f)", x.a, x.b, x.c, x.d, x.tx, x.ty);
  44 
  45 /*
  46  * The GlyphCanvas is a global shared CGContext that characters are struck into.
  47  * For each character, the glyph is struck, copied into a GlyphInfo struct, and
  48  * the canvas is cleared for the next glyph.
  49  *
  50  * If the necessary canvas is too large, the shared one will not be used and a
  51  * temporary one will be provided.
  52  */
  53 typedef enum {
  54     BLACK_ON_WHITE_STAGE,
  55     WHITE_ON_BLACK_STAGE
  56 } CGGI_GlyphRenderingStage;
  57 
  58 @interface CGGI_GlyphCanvas : NSObject {
  59 @public
  60     CGContextRef context;
  61     vImage_Buffer *image;
  62     CGGI_GlyphRenderingStage stage;
  63 }
  64 @end;
  65 
  66 @implementation CGGI_GlyphCanvas
  67 - (id) init {
  68     if (self = [super init]) {
  69         context = NULL;
  70         image = NULL;
  71         stage = BLACK_ON_WHITE_STAGE;
  72     }
  73     return self;
  74 }
  75 @end
  76 
  77 
  78 #pragma mark --- Debugging Helpers ---
  79 
  80 /*
  81  * These debug functions are only compiled when CGGI_DEBUG is activated.
  82  * They will print out a full UInt8 canvas and any pixels struck (assuming
  83  * the canvas is not too big).
  84  *
  85  * As another debug feature, the entire canvas will be filled with a light
  86  * alpha value so it is easy to see where the glyph painting regions are
  87  * at runtime.
  88  */
  89 
  90 #ifdef CGGI_DEBUG_DUMP
  91 static void
  92 DUMP_PIXELS(const char msg[], const UInt8 pixels[],
  93             const size_t bytesPerPixel, const int width, const int height)
  94 {


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






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





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




 297 }
 298 
 299 static void
 300 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
 301 {
 302     UInt32 *src = (UInt32 *)canvas->image->data;
 303     size_t srcRowWidth = canvas->image->width;
 304 
 305     UInt8 *dest = (UInt8 *)info->image;
 306     size_t destRowWidth = info->width;
 307 
 308     size_t height = info->height;
 309 
 310     size_t y;
 311     
 312     if (canvas->stage != BLACK_ON_WHITE_STAGE) {
 313         // not needed for AA
 314         return;
 315     }
 316     
 317     // fill empty glyph image with black-on-white glyph
 318     for (y = 0; y < height; y++) {
 319         size_t destRow = y * destRowWidth;
 320         size_t srcRow = y * srcRowWidth;

 321         size_t x;
 322         for (x = 0; x < destRowWidth; x++) {
 323             UInt32 p = src[srcRow + x];
 324             dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
 325         }
 326     }
 327 }
 328 
 329 
 330 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
 331 
 332 typedef struct CGGI_GlyphInfoDescriptor {
 333     size_t pixelSize;
 334     void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
 335 } CGGI_GlyphInfoDescriptor;
 336 
 337 typedef struct CGGI_RenderingMode {
 338     CGGI_GlyphInfoDescriptor *glyphDescriptor;
 339     JRSFontRenderingStyle cgFontMode;
 340 } CGGI_RenderingMode;
 341 
 342 static CGGI_GlyphInfoDescriptor grey =
 343     { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
 344 static CGGI_GlyphInfoDescriptor rgb =
 345     { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
 346 
 347 static inline CGGI_RenderingMode
 348 CGGI_GetRenderingMode(const AWTStrike *strike)
 349 {
 350     CGGI_RenderingMode mode;
 351     mode.cgFontMode = strike->fStyle;
 352     NSException *e = nil;
 353 
 354     switch (strike->fAAStyle) {

 355     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
 356     case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:


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


 625         glyph = glyphTmp[0];
 626     } else {
 627         UTF16Char charRef;
 628         charRef = (UTF16Char) uniChar; // truncate.
 629         fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
 630     }
 631 
 632     CGAffineTransform tx = strike->fTx;
 633     JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
 634 
 635     CGRect bbox;
 636     JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
 637 
 638     CGSize advance;
 639     CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
 640 
 641     // create the Sun2D GlyphInfo we are going to strike into
 642     GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
 643 
 644     // fix the context size, just in case the substituted character is unexpectedly large
 645     CGGI_SizeCanvas(canvas, info->width, info->height, mode);
 646 
 647     // align the transform for the real CoreText strike
 648     CGContextSetTextMatrix(canvas->context, strike->fAltTx);
 649 
 650     const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
 651     CGContextSetFont(canvas->context, cgFallback);
 652     CFRelease(cgFallback);
 653 
 654     // clean the canvas - align, strike, and copy the glyph from the canvas into the info
 655     CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
 656 
 657     // restore the state of the world
 658     CGContextRestoreGState(canvas->context);
 659 
 660     CFRelease(fallback);
 661 #ifdef CGGI_DEBUG
 662     DUMP_GLYPHINFO(info);
 663 #endif
 664 
 665 #ifdef CGGI_DEBUG_DUMP


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

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


< prev index next >