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