1/* 2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26#import <Accelerate/Accelerate.h> // for vImage_Buffer 27 28#import "JNIUtilities.h" 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 75static void 76DUMP_PIXELS(const char msg[], const UInt8 pixels[], 77 const size_t bytesPerPixel, const int width, const int height) 78{ 79 printf("| %s: (%d, %d)\n", msg, width, height); 80 81 if (width > 80 || height > 80) { 82 printf("| too big\n"); 83 return; 84 } 85 86 size_t i, j = 0, k, size = width * height; 87 for (i = 0; i < size; i++) { 88 for (k = 0; k < bytesPerPixel; k++) { 89 if (pixels[i * bytesPerPixel + k] > 0x80) j++; 90 } 91 } 92 93 if (j == 0) { 94 printf("| empty\n"); 95 return; 96 } 97 98 printf("|_"); 99 int x, y; 100 for (x = 0; x < width; x++) { 101 printf("__"); 102 } 103 printf("_\n"); 104 105 for (y = 0; y < height; y++) { 106 printf("| "); 107 for (x = 0; x < width; x++) { 108 int p = 0; 109 for(k = 0; k < bytesPerPixel; k++) { 110 p += pixels[(y * width + x) * bytesPerPixel + k]; 111 } 112 113 if (p < 0x80) { 114 printf(" "); 115 } else { 116 printf("[]"); 117 } 118 } 119 printf(" |\n"); 120 } 121} 122 123static void 124DUMP_IMG_PIXELS(const char msg[], const vImage_Buffer *image) 125{ 126 const void *pixels = image->data; 127 const size_t pixelSize = image->rowBytes / image->width; 128 const size_t width = image->width; 129 const size_t height = image->height; 130 131 DUMP_PIXELS(msg, pixels, pixelSize, width, height); 132} 133 134static void 135PRINT_CGSTATES_INFO(const CGContextRef cgRef) 136{ 137 // TODO(cpc): lots of SPI use in this method; remove/rewrite? 138#if 0 139 CGRect clip = CGContextGetClipBoundingBox(cgRef); 140 fprintf(stderr, " clip: ((%f, %f), (%f, %f))\n", 141 clip.origin.x, clip.origin.y, clip.size.width, clip.size.height); 142 143 CGAffineTransform ctm = CGContextGetCTM(cgRef); 144 fprintf(stderr, " ctm: (%f, %f, %f, %f, %f, %f)\n", 145 ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty); 146 147 CGAffineTransform txtTx = CGContextGetTextMatrix(cgRef); 148 fprintf(stderr, " txtTx: (%f, %f, %f, %f, %f, %f)\n", 149 txtTx.a, txtTx.b, txtTx.c, txtTx.d, txtTx.tx, txtTx.ty); 150 151 if (CGContextIsPathEmpty(cgRef) == 0) { 152 CGPoint pathpoint = CGContextGetPathCurrentPoint(cgRef); 153 CGRect pathbbox = CGContextGetPathBoundingBox(cgRef); 154 fprintf(stderr, " [pathpoint: (%f, %f)] [pathbbox: ((%f, %f), (%f, %f))]\n", 155 pathpoint.x, pathpoint.y, pathbbox.origin.x, pathbbox.origin.y, 156 pathbbox.size.width, pathbbox.size.width); 157 } 158 159 CGFloat linewidth = CGContextGetLineWidth(cgRef); 160 CGLineCap linecap = CGContextGetLineCap(cgRef); 161 CGLineJoin linejoin = CGContextGetLineJoin(cgRef); 162 CGFloat miterlimit = CGContextGetMiterLimit(cgRef); 163 size_t dashcount = CGContextGetLineDashCount(cgRef); 164 fprintf(stderr, " [linewidth: %f] [linecap: %d] [linejoin: %d] [miterlimit: %f] [dashcount: %lu]\n", 165 linewidth, linecap, linejoin, miterlimit, (unsigned long)dashcount); 166 167 CGFloat smoothness = CGContextGetSmoothness(cgRef); 168 bool antialias = CGContextGetShouldAntialias(cgRef); 169 bool smoothfont = CGContextGetShouldSmoothFonts(cgRef); 170 JRSFontRenderingStyle fRendMode = CGContextGetFontRenderingMode(cgRef); 171 fprintf(stderr, " [smoothness: %f] [antialias: %d] [smoothfont: %d] [fontrenderingmode: %d]\n", 172 smoothness, antialias, smoothfont, fRendMode); 173#endif 174} 175#endif 176 177#ifdef CGGI_DEBUG 178 179static void 180DUMP_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 --- 198static Int32 reverseGamma = 0; 199 200static UInt8 reverseGammaLut[256] = { 0 }; 201 202static inline UInt8* getReverseGammaLut() { 203 if (reverseGamma == 0) { 204 // initialize gamma lut 205 double gamma; 206 int i; 207 const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA"); 208 if (pGammaEnv != NULL) { 209 reverseGamma = atol(pGammaEnv); 210 } 211 212 if (reverseGamma < 100 || reverseGamma > 250) { 213 reverseGamma = 180; 214 } 215 216 gamma = 100.0 / reverseGamma; 217 for (i = 0; i < 256; i++) { 218 double x = ((double)i) / 255.0; 219 reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma)); 220 } 221 } 222 return reverseGammaLut; 223} 224 225static inline void 226CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst) 227{ 228 UInt8* lut = getReverseGammaLut(); 229 230 *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)]; // red 231 *(dst + 1) = lut[0xFF - (p >> 8 & 0xFF)]; // green 232 *(dst + 2) = lut[0xFF - (p & 0xFF)]; // blue 233} 234 235static void 236CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) 237{ 238 UInt32 *src = (UInt32 *)canvas->image->data; 239 size_t srcRowWidth = canvas->image->width; 240 241 UInt8 *dest = (UInt8 *)info->image; 242 size_t destRowWidth = info->width; 243 244 size_t height = info->height; 245 246 size_t y; 247 248 // fill empty glyph image with black-on-white glyph 249 for (y = 0; y < height; y++) { 250 size_t destRow = y * destRowWidth * 3; 251 size_t srcRow = y * srcRowWidth; 252 253 size_t x; 254 for (x = 0; x < destRowWidth; x++) { 255 CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x], 256 dest + destRow + x * 3); 257 } 258 } 259} 260 261//static void CGGI_copyImageFromCanvasToAlphaInfo 262//(CGGI_GlyphCanvas *canvas, GlyphInfo *info) 263//{ 264// vImage_Buffer infoBuffer; 265// infoBuffer.data = info->image; 266// infoBuffer.width = info->width; 267// infoBuffer.height = info->height; 268// infoBuffer.rowBytes = info->width; // three bytes per RGB pixel 269// 270// UInt8 scrapPixel[info->width * info->height]; 271// vImage_Buffer scrapBuffer; 272// scrapBuffer.data = &scrapPixel; 273// scrapBuffer.width = info->width; 274// scrapBuffer.height = info->height; 275// scrapBuffer.rowBytes = info->width; 276// 277// vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer, 278// &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags); 279//} 280 281static inline UInt8 282CGGI_ConvertBWPixelToByteGray(UInt32 p) 283{ 284 return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3); 285} 286 287static void 288CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) 289{ 290 UInt32 *src = (UInt32 *)canvas->image->data; 291 size_t srcRowWidth = canvas->image->width; 292 293 UInt8 *dest = (UInt8 *)info->image; 294 size_t destRowWidth = info->width; 295 296 size_t height = info->height; 297 298 size_t y; 299 300 // fill empty glyph image with black-on-white glyph 301 for (y = 0; y < height; y++) { 302 size_t destRow = y * destRowWidth; 303 size_t srcRow = y * srcRowWidth; 304 size_t x; 305 for (x = 0; x < destRowWidth; x++) { 306 UInt32 p = src[srcRow + x]; 307 dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p); 308 } 309 } 310} 311 312static void 313CGGI_CopyImageFromCanvasToARGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) 314{ 315 CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(canvas->context); 316 bool littleEndian = (bitmapInfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little; 317 318 UInt32 *src = (UInt32 *)canvas->image->data; 319 size_t srcRowWidth = canvas->image->width; 320 321 UInt8 *dest = (UInt8 *)info->image; 322 size_t destRowWidth = info->width; 323 324 size_t height = info->height; 325 326 size_t y; 327 328 for (y = 0; y < height; y++) { 329 size_t srcRow = y * srcRowWidth; 330 if (littleEndian) { 331 UInt16 destRowBytes = info->rowBytes; 332 memcpy(dest, src + srcRow, destRowBytes); 333 dest += destRowBytes; 334 } else { 335 size_t x; 336 for (x = 0; x < destRowWidth; x++) { 337 UInt32 p = src[srcRow + x]; 338 *dest++ = (p >> 24 & 0xFF); // blue (alpha-premultiplied) 339 *dest++ = (p >> 16 & 0xFF); // green (alpha-premultiplied) 340 *dest++ = (p >> 8 & 0xFF); // red (alpha-premultiplied) 341 *dest++ = (p & 0xFF); // alpha 342 } 343 } 344 } 345} 346 347#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions --- 348 349typedef struct CGGI_GlyphInfoDescriptor { 350 size_t pixelSize; 351 void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info); 352} CGGI_GlyphInfoDescriptor; 353 354typedef struct CGGI_RenderingMode { 355 CGGI_GlyphInfoDescriptor *glyphDescriptor; 356 JRSFontRenderingStyle cgFontMode; 357} CGGI_RenderingMode; 358 359static CGGI_GlyphInfoDescriptor grey = 360 { 1, &CGGI_CopyImageFromCanvasToAlphaInfo }; 361static CGGI_GlyphInfoDescriptor rgb = 362 { 3, &CGGI_CopyImageFromCanvasToRGBInfo }; 363static CGGI_GlyphInfoDescriptor argb = 364 { 4, &CGGI_CopyImageFromCanvasToARGBInfo }; 365 366static inline CGGI_GlyphInfoDescriptor* 367CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CTFontRef font) 368{ 369 return IsEmojiFont(font) ? &argb : mode->glyphDescriptor; 370} 371 372static inline CGGI_RenderingMode 373CGGI_GetRenderingMode(const AWTStrike *strike) 374{ 375 CGGI_RenderingMode mode; 376 mode.cgFontMode = strike->fStyle; 377 NSException *e = nil; 378 379 switch (strike->fAAStyle) { 380 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF: 381 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON: 382 mode.glyphDescriptor = &grey; 383 break; 384 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB: 385 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR: 386 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB: 387 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR: 388 mode.glyphDescriptor = &rgb; 389 break; 390 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP: 391 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT: 392 default: 393 /* we expect that text antialiasing hint has been already 394 * evaluated. Report an error if we get 'unevaluated' hint here. 395 */ 396 e = [NSException 397 exceptionWithName:@"IllegalArgumentException" 398 reason:@"Invalid hint value" 399 userInfo:nil]; 400 @throw e; 401 } 402 403 return mode; 404} 405 406 407#pragma mark --- Canvas Managment --- 408 409/* 410 * Creates a new canvas of a fixed size, and initializes the CGContext as 411 * an 32-bit ARGB BitmapContext with some generic RGB color space. 412 */ 413static inline void 414CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, 415 const vImagePixelCount width, const vImagePixelCount height, 416 const CGGI_RenderingMode* mode) 417{ 418 // our canvas is *always* 4-byte ARGB 419 size_t bytesPerRow = width * sizeof(UInt32); 420 size_t byteCount = bytesPerRow * height; 421 422 canvas->image = malloc(sizeof(vImage_Buffer)); 423 canvas->image->width = width; 424 canvas->image->height = height; 425 canvas->image->rowBytes = bytesPerRow; 426 427 canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8)); 428 if (canvas->image->data == NULL) { 429 [[NSException exceptionWithName:NSMallocException 430 reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise]; 431 } 432 433 uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst; 434 if (mode->glyphDescriptor == &rgb) { 435 bmpInfo |= kCGBitmapByteOrder32Host; 436 } 437 438 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 439 canvas->context = CGBitmapContextCreate(canvas->image->data, 440 width, height, 8, bytesPerRow, 441 colorSpace, 442 bmpInfo); 443 444 // set foreground color 445 CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f); 446 447 CGContextSetFontSize(canvas->context, 1); 448 CGContextSaveGState(canvas->context); 449 450 CGColorSpaceRelease(colorSpace); 451} 452 453/* 454 * Releases the BitmapContext and the associated memory backing it. 455 */ 456static inline void 457CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas) 458{ 459 if (canvas->context != NULL) { 460 CGContextRelease(canvas->context); 461 } 462 463 if (canvas->image != NULL) { 464 if (canvas->image->data != NULL) { 465 free(canvas->image->data); 466 } 467 free(canvas->image); 468 } 469} 470 471/* 472 * This is the slack space that is preallocated for the global GlyphCanvas 473 * when it needs to be expanded. It has been set somewhat liberally to 474 * avoid re-upsizing frequently. 475 */ 476#define CGGI_GLYPH_CANVAS_SLACK 2.5 477 478/* 479 * Quick and easy inline to check if this canvas is big enough. 480 */ 481static inline void 482CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, 483 const vImagePixelCount height, 484 const CGGI_RenderingMode* mode) 485{ 486 if (canvas->image != NULL && 487 width < canvas->image->width && 488 height < canvas->image->height) 489 { 490 return; 491 } 492 493 // if we don't have enough space to strike the largest glyph in the 494 // run, resize the canvas 495 CGGI_FreeCanvas(canvas); 496 CGGI_InitCanvas(canvas, 497 width * CGGI_GLYPH_CANVAS_SLACK, 498 height * CGGI_GLYPH_CANVAS_SLACK, 499 mode); 500 JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode); 501} 502 503/* 504 * Clear the canvas by blitting white (or transparent background for color glyphs) only into the region of interest 505 * (the rect which we will copy out of once the glyph is struck). 506 */ 507static inline void 508CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info, bool transparent) 509{ 510 vImage_Buffer canvasRectToClear; 511 canvasRectToClear.data = canvas->image->data; 512 canvasRectToClear.height = info->height; 513 canvasRectToClear.width = info->width; 514 // use the row stride of the canvas, not the info 515 canvasRectToClear.rowBytes = canvas->image->rowBytes; 516 517 // clean the canvas 518#ifdef CGGI_DEBUG 519 Pixel_8888 background = { 0xE0, 0xE0, 0xE0, 0xE0 }; 520#else 521 Pixel_8888 background = { transparent ? 0 : 0xFF, 522 transparent ? 0 : 0xFF, 523 transparent ? 0 : 0xFF, 524 transparent ? 0 : 0xFF }; 525#endif 526 527 // clear canvas background 528 vImageBufferFill_ARGB8888(&canvasRectToClear, background, kvImageNoFlags); 529} 530 531 532#pragma mark --- GlyphInfo Creation & Copy Functions --- 533 534/* 535 * Creates a GlyphInfo with exactly the correct size image and measurements. 536 */ 537#define CGGI_GLYPH_BBOX_PADDING 2.0f 538static inline GlyphInfo * 539CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox, 540 const AWTStrike *strike, 541 const CGGI_GlyphInfoDescriptor *glyphDescriptor) 542{ 543 size_t pixelSize = glyphDescriptor->pixelSize; 544 545 // adjust the bounding box to be 1px bigger on each side than what 546 // CGFont-whatever suggests - because it gives a bounding box that 547 // is too tight 548 bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f; 549 bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f; 550 bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING; 551 bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING; 552 553 vImagePixelCount width = ceilf(bbox.size.width); 554 vImagePixelCount height = ceilf(bbox.size.height); 555 556 // if the glyph is larger than 1MB, don't even try... 557 // the GlyphVector path should have taken over by now 558 // and zero pixels is ok 559 if (width * height > 1024 * 1024) { 560 width = 1; 561 height = 1; 562 } 563 advance = CGSizeApplyAffineTransform(advance, strike->fFontTx); 564 if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) { 565 advance.width = round(advance.width); 566 advance.height = round(advance.height); 567 } 568 advance = CGSizeApplyAffineTransform(advance, strike->fDevTx); 569 570#ifdef USE_IMAGE_ALIGNED_MEMORY 571 // create separate memory 572 GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo)); 573 void *image = (void *)malloc(height * width * pixelSize); 574#else 575 // create a GlyphInfo struct fused to the image it points to 576 GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) + 577 height * width * pixelSize); 578#endif 579 580 glyphInfo->advanceX = advance.width; 581 glyphInfo->advanceY = advance.height; 582 glyphInfo->topLeftX = round(bbox.origin.x); 583 glyphInfo->topLeftY = round(bbox.origin.y); 584 glyphInfo->width = width; 585 glyphInfo->height = height; 586 glyphInfo->rowBytes = width * pixelSize; 587 glyphInfo->cellInfo = NULL; 588 589#ifdef USE_IMAGE_ALIGNED_MEMORY 590 glyphInfo->image = image; 591#else 592 glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo); 593#endif 594 595 return glyphInfo; 596} 597 598 599#pragma mark --- Glyph Striking onto Canvas --- 600 601/* 602 * Clears the canvas, strikes the glyph with CoreGraphics, and then 603 * copies the struck pixels into the GlyphInfo image. 604 */ 605static inline void 606CGGI_CreateImageForGlyph 607 (CGGI_GlyphCanvas *canvas, const CGGlyph glyph, 608 GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike, CTFontRef font) 609{ 610 if (isnan(info->topLeftX) || isnan(info->topLeftY)) { 611 // Explicitly set glyphInfo width/height to be 0 to ensure 612 // zero length glyph image is copied into GlyphInfo from canvas 613 info->width = 0; 614 info->height = 0; 615 616 // copy the "empty" glyph from the canvas into the info 617 (*glyphDescriptor->copyFxnPtr)(canvas, info); 618 return; 619 } 620 621 // clean the canvas 622 CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb); 623 624 // strike the glyph in the upper right corner 625 CGFloat x = -info->topLeftX; 626 CGFloat y = canvas->image->height + info->topLeftY; 627 628 if (glyphDescriptor == &argb) { 629 // Emoji glyphs are not rendered by CGContextShowGlyphsAtPoint. 630 // Also, it's not possible to use transformation matrix to get the emoji glyph 631 // rendered for the desired font size - actual-size font object is needed. 632 // The logic here must match the logic in CGGlyphImages_GetGlyphMetrics, 633 // which calculates glyph metrics. 634 635 CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context); 636 CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c)); 637 CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0); 638 639 CGFloat normFactor = 1.0 / fontSize; 640 CGAffineTransform normalizedMatrix = CGAffineTransformScale(matrix, normFactor, normFactor); 641 CGContextSetTextMatrix(canvas->context, normalizedMatrix); 642 643 CGPoint userPoint = CGPointMake(x, y); 644 CGAffineTransform normalizedMatrixInv = CGAffineTransformInvert(normalizedMatrix); 645 CGPoint textPoint = CGPointApplyAffineTransform(userPoint, normalizedMatrixInv); 646 647 CTFontDrawGlyphs(sizedFont, &glyph, &textPoint, 1, canvas->context); 648 649 CFRelease(sizedFont); 650 // restore context's original state 651 CGContextSetTextMatrix(canvas->context, matrix); 652 CGContextSetFontSize(canvas->context, 1); // CTFontDrawGlyphs tampers with it 653 } else { 654 CGContextShowGlyphsAtPoint(canvas->context, x, y, &glyph, 1); 655 } 656 657 // copy the glyph from the canvas into the info 658 (*glyphDescriptor->copyFxnPtr)(canvas, info); 659} 660 661/* 662 * CoreText path... 663 */ 664static inline GlyphInfo * 665CGGI_CreateImageForUnicode 666 (CGGI_GlyphCanvas *canvas, const AWTStrike *strike, 667 const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar) 668{ 669 // save the state of the world 670 CGContextSaveGState(canvas->context); 671 672 // get the glyph, measure it using CG 673 CGGlyph glyph; 674 CTFontRef fallback; 675 if (uniChar > 0xFFFF) { 676 UTF16Char charRef[2]; 677 CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef); 678 CGGlyph glyphTmp[2]; 679 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2); 680 glyph = glyphTmp[0]; 681 } else { 682 UTF16Char charRef; 683 charRef = (UTF16Char) uniChar; // truncate. 684 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1); 685 } 686 687 CGAffineTransform tx = strike->fTx; 688 JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); 689 690 CGRect bbox; 691 CGSize advance; 692 CGGlyphImages_GetGlyphMetrics(fallback, &tx, style, &glyph, 1, &bbox, &advance); 693 694 CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback); 695 696 // create the Sun2D GlyphInfo we are going to strike into 697 GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor); 698 699 // fix the context size, just in case the substituted character is unexpectedly large 700 CGGI_SizeCanvas(canvas, info->width, info->height, mode); 701 702 // align the transform for the real CoreText strike 703 CGContextSetTextMatrix(canvas->context, strike->fAltTx); 704 705 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); 706 CGContextSetFont(canvas->context, cgFallback); 707 CFRelease(cgFallback); 708 709 // clean the canvas - align, strike, and copy the glyph from the canvas into the info 710 CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback); 711 712 // restore the state of the world 713 CGContextRestoreGState(canvas->context); 714 715 CFRelease(fallback); 716#ifdef CGGI_DEBUG 717 DUMP_GLYPHINFO(info); 718#endif 719 720#ifdef CGGI_DEBUG_DUMP 721 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 722#if 0 723 PRINT_CGSTATES_INFO(NULL); 724#endif 725#endif 726 727 return info; 728} 729 730 731#pragma mark --- GlyphInfo Filling and Canvas Managment --- 732 733/* 734 * Sets all the per-run properties for the canvas, and then iterates through 735 * the character run, and creates images in the GlyphInfo structs. 736 * 737 * Not inlined because it would create two copies in the function below 738 */ 739static void 740CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas, 741 const AWTStrike *strike, 742 const CGGI_RenderingMode *mode, 743 jlong glyphInfos[], 744 const UnicodeScalarValue uniChars[], 745 const CGGlyph glyphs[], 746 const CFIndex len) 747{ 748 CGContextSetTextMatrix(canvas->context, strike->fAltTx); 749 750 CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont); 751 JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle); 752 753 CTFontRef mainFont = (CTFontRef)strike->fAWTFont->fFont; 754 CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, mainFont); 755 756 CFIndex i; 757 for (i = 0; i < len; i++) { 758 GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]); 759 if (info != NULL) { 760 CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mainFontDescriptor, strike, mainFont); 761 } else { 762 info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]); 763 glyphInfos[i] = ptr_to_jlong(info); 764 } 765#ifdef CGGI_DEBUG 766 DUMP_GLYPHINFO(info); 767#endif 768 769#ifdef CGGI_DEBUG_DUMP 770 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 771#endif 772 } 773#ifdef CGGI_DEBUG_DUMP 774 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 775 PRINT_CGSTATES_INFO(canvas->context); 776#endif 777} 778 779static NSString *threadLocalAACanvasKey = 780 @"Java CoreGraphics Text Renderer Cached Canvas for AA"; 781 782static NSString *threadLocalLCDCanvasKey = 783 @"Java CoreGraphics Text Renderer Cached Canvas for LCD"; 784 785/* 786 * This is the maximum length and height times the above slack squared 787 * to determine if we go with the global canvas, or malloc one on the spot. 788 */ 789#define CGGI_GLYPH_CANVAS_MAX 100 790 791/* 792 * Based on the space needed to strike the largest character in the run, 793 * either use the global shared canvas, or make one up on the spot, strike 794 * the glyphs, and destroy it. 795 */ 796static inline void 797CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike, 798 const CGGI_RenderingMode *mode, 799 const UnicodeScalarValue uniChars[], const CGGlyph glyphs[], 800 const size_t maxWidth, const size_t maxHeight, 801 const CFIndex len) 802{ 803 if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK > 804 CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK) 805 { 806 CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init]; 807 CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode); 808 CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike, 809 mode, glyphInfos, uniChars, 810 glyphs, len); 811 CGGI_FreeCanvas(tmpCanvas); 812 813 [tmpCanvas release]; 814 return; 815 } 816 NSMutableDictionary *threadDict = 817 [[NSThread currentThread] threadDictionary]; 818 819 NSString* theKey = (mode->glyphDescriptor == &rgb) ? 820 threadLocalLCDCanvasKey : threadLocalAACanvasKey; 821 822 CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey]; 823 if (canvas == nil) { 824 canvas = [[CGGI_GlyphCanvas alloc] init]; 825 [threadDict setObject:canvas forKey:theKey]; 826 } 827 828 CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode); 829 CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode, 830 glyphInfos, uniChars, glyphs, len); 831} 832 833/* 834 * Finds the advances and bounding boxes of the characters in the run, 835 * cycles through all the bounds and calculates the maximum canvas space 836 * required by the largest glyph. 837 * 838 * Creates a GlyphInfo struct with a malloc that also encapsulates the 839 * image the struct points to. This is done to meet memory layout 840 * expectations in the Sun text rasterizer memory managment code. 841 * The image immediately follows the struct physically in memory. 842 */ 843static inline void 844CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike, 845 const CGGI_RenderingMode *mode, 846 const UnicodeScalarValue uniChars[], const CGGlyph glyphs[], 847 CGSize advances[], CGRect bboxes[], const CFIndex len) 848{ 849 AWTFont *font = strike->fAWTFont; 850 CGAffineTransform tx = strike->fTx; 851 JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); 852 853 CTFontRef fontRef = (CTFontRef)font->fFont; 854 CGGlyphImages_GetGlyphMetrics(fontRef, &tx, bboxCGMode, glyphs, len, bboxes, advances); 855 CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fontRef); 856 857 size_t maxWidth = 1; 858 size_t maxHeight = 1; 859 860 CFIndex i; 861 for (i = 0; i < len; i++) 862 { 863 if (uniChars[i] != 0) 864 { 865 glyphInfos[i] = 0L; 866 continue; // will be handled later 867 } 868 869 CGSize advance = advances[i]; 870 CGRect bbox = bboxes[i]; 871 872 GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor); 873 874 if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width; 875 if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height; 876 877 glyphInfos[i] = ptr_to_jlong(glyphInfo); 878 } 879 880 CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars, 881 glyphs, maxWidth, maxHeight, len); 882} 883 884 885#pragma mark --- Temporary Buffer Allocations and Initialization --- 886 887/* 888 * This stage separates the already valid glyph codes from the unicode values 889 * that need special handling - the rawGlyphCodes array is no longer used 890 * after this stage. 891 */ 892static void 893CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos, 894 const AWTStrike *strike, 895 const CGGI_RenderingMode *mode, 896 jint rawGlyphCodes[], 897 UnicodeScalarValue uniChars[], CGGlyph glyphs[], 898 CGSize advances[], CGRect bboxes[], 899 const CFIndex len) 900{ 901 CFIndex i; 902 for (i = 0; i < len; i++) { 903 jint code = rawGlyphCodes[i]; 904 if (code < 0) { 905 glyphs[i] = 0; 906 uniChars[i] = -code; 907 } else { 908 glyphs[i] = code; 909 uniChars[i] = 0; 910 } 911 } 912 913 CGGI_CreateGlyphInfos(glyphInfos, strike, mode, 914 uniChars, glyphs, advances, bboxes, len); 915 916#ifdef CGGI_DEBUG_HIT_COUNT 917 static size_t hitCount = 0; 918 hitCount++; 919 printf("%d\n", (int)hitCount); 920#endif 921} 922 923/* 924 * Conditionally stack allocates buffers for glyphs, bounding boxes, 925 * and advances. Unfortunately to use CG or CT in bulk runs (which is 926 * faster than calling them per character), we have to copy into and out 927 * of these buffers. Still a net win though. 928 */ 929void 930CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[], 931 const AWTStrike *strike, 932 jint rawGlyphCodes[], const CFIndex len) 933{ 934 const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike); 935 936 if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) { 937 CGRect bboxes[len]; 938 CGSize advances[len]; 939 CGGlyph glyphs[len]; 940 UnicodeScalarValue uniChars[len]; 941 942 CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, 943 rawGlyphCodes, uniChars, glyphs, 944 advances, bboxes, len); 945 946 return; 947 } 948 949 // just do one malloc, and carve it up for all the buffers 950 void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) * 951 sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len); 952 if (buffer == NULL) { 953 [[NSException exceptionWithName:NSMallocException 954 reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise]; 955 } 956 957 CGRect *bboxes = (CGRect *)(buffer); 958 CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len); 959 CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len); 960 UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len); 961 962 CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, 963 rawGlyphCodes, uniChars, glyphs, 964 advances, bboxes, len); 965 966 free(buffer); 967} 968 969/* 970 * Calculates bounding boxes (for given transform) and advance (for untransformed 1pt-size font) for specified glyphs. 971 */ 972void 973CGGlyphImages_GetGlyphMetrics(const CTFontRef font, 974 const CGAffineTransform *tx, 975 const JRSFontRenderingStyle style, 976 const CGGlyph glyphs[], 977 size_t count, 978 CGRect bboxes[], 979 CGSize advances[]) { 980 if (IsEmojiFont(font)) { 981 // Glyph metrics for emoji font are not strictly proportional to font size, 982 // so we need to construct real-sized font object to calculate them. 983 // The logic here must match the logic in CGGI_CreateImageForGlyph, 984 // which performs glyph drawing. 985 986 CGFloat fontSize = sqrt(fabs(tx->a * tx->d - tx->b * tx->c)); 987 CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0); 988 989 if (bboxes) { 990 // JRSFontGetBoundingBoxesForGlyphsAndStyle works incorrectly for AppleColorEmoji font: 991 // it uses bottom left corner of the glyph's bounding box as a fixed point of transformation 992 // instead of glyph's origin point (used at drawing). So, as a workaround, 993 // we request a bounding box for the untransformed glyph, and apply the transform ourselves. 994 JRSFontGetBoundingBoxesForGlyphsAndStyle(sizedFont, &CGAffineTransformIdentity, style, glyphs, count, bboxes); 995 CGAffineTransform txNormalized = CGAffineTransformMake(tx->a / fontSize, 996 tx->b / fontSize, 997 tx->c / fontSize, 998 tx->d / fontSize, 999 0, 0); 1000 for (int i = 0; i < count; i++) { 1001 bboxes[i] = CGRectApplyAffineTransform(bboxes[i], txNormalized); 1002 } 1003 } 1004 1005 if (advances) { 1006 CTFontGetAdvancesForGlyphs(sizedFont, kCTFontDefaultOrientation, glyphs, advances, count); 1007 for (int i = 0; i < count; i++) { 1008 // Calling code will scale the result back 1009 advances[i].width /= fontSize; 1010 advances[i].height /= fontSize; 1011 } 1012 } 1013 1014 CFRelease(sizedFont); 1015 } else { 1016 if (bboxes) { 1017 JRSFontGetBoundingBoxesForGlyphsAndStyle(font, tx, style, glyphs, count, bboxes); 1018 } 1019 if (advances) { 1020 CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count); 1021 } 1022 } 1023}