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 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 225 static inline void 226 CGGI_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 235 static void 236 CGGI_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 281 static inline UInt8 282 CGGI_ConvertBWPixelToByteGray(UInt32 p) 283 { 284 return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3); 285 } 286 287 static void 288 CGGI_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 312 313 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions --- 314 315 typedef struct CGGI_GlyphInfoDescriptor { 316 size_t pixelSize; 317 void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info); 318 } CGGI_GlyphInfoDescriptor; 319 320 typedef struct CGGI_RenderingMode { 321 CGGI_GlyphInfoDescriptor *glyphDescriptor; 322 JRSFontRenderingStyle cgFontMode; 323 } CGGI_RenderingMode; 324 325 static CGGI_GlyphInfoDescriptor grey = 326 { 1, &CGGI_CopyImageFromCanvasToAlphaInfo }; 327 static CGGI_GlyphInfoDescriptor rgb = 328 { 3, &CGGI_CopyImageFromCanvasToRGBInfo }; 329 330 static inline CGGI_RenderingMode 331 CGGI_GetRenderingMode(const AWTStrike *strike) 332 { 333 CGGI_RenderingMode mode; 334 mode.cgFontMode = strike->fStyle; 335 NSException *e = nil; 336 337 switch (strike->fAAStyle) { 338 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF: 339 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON: 340 mode.glyphDescriptor = &grey; 341 break; 342 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB: 343 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR: 344 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB: 345 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR: 346 mode.glyphDescriptor = &rgb; 347 break; 348 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP: 349 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT: 350 default: 351 /* we expect that text antialiasing hint has been already 352 * evaluated. Report an error if we get 'unevaluated' hint here. 353 */ 354 e = [NSException 355 exceptionWithName:@"IllegalArgumentException" 356 reason:@"Invalid hint value" 357 userInfo:nil]; 358 @throw e; 359 } 360 361 return mode; 362 } 363 364 365 #pragma mark --- Canvas Managment --- 366 367 /* 368 * Creates a new canvas of a fixed size, and initializes the CGContext as 369 * an 32-bit ARGB BitmapContext with some generic RGB color space. 370 */ 371 static inline void 372 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, 373 const vImagePixelCount width, const vImagePixelCount height, 374 const CGGI_RenderingMode* mode) 375 { 376 // our canvas is *always* 4-byte ARGB 377 size_t bytesPerRow = width * sizeof(UInt32); 378 size_t byteCount = bytesPerRow * height; 379 380 canvas->image = malloc(sizeof(vImage_Buffer)); 381 canvas->image->width = width; 382 canvas->image->height = height; 383 canvas->image->rowBytes = bytesPerRow; 384 385 canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8)); 386 if (canvas->image->data == NULL) { 387 [[NSException exceptionWithName:NSMallocException 388 reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise]; 389 } 390 391 uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst; 392 if (mode->glyphDescriptor == &rgb) { 393 bmpInfo |= kCGBitmapByteOrder32Host; 394 } 395 396 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 397 canvas->context = CGBitmapContextCreate(canvas->image->data, 398 width, height, 8, bytesPerRow, 399 colorSpace, 400 bmpInfo); 401 402 // set foreground color 403 CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f); 404 405 CGContextSetFontSize(canvas->context, 1); 406 CGContextSaveGState(canvas->context); 407 408 CGColorSpaceRelease(colorSpace); 409 } 410 411 /* 412 * Releases the BitmapContext and the associated memory backing it. 413 */ 414 static inline void 415 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas) 416 { 417 if (canvas->context != NULL) { 418 CGContextRelease(canvas->context); 419 } 420 421 if (canvas->image != NULL) { 422 if (canvas->image->data != NULL) { 423 free(canvas->image->data); 424 } 425 free(canvas->image); 426 } 427 } 428 429 /* 430 * This is the slack space that is preallocated for the global GlyphCanvas 431 * when it needs to be expanded. It has been set somewhat liberally to 432 * avoid re-upsizing frequently. 433 */ 434 #define CGGI_GLYPH_CANVAS_SLACK 2.5 435 436 /* 437 * Quick and easy inline to check if this canvas is big enough. 438 */ 439 static inline void 440 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, 441 const vImagePixelCount height, 442 const CGGI_RenderingMode* mode) 443 { 444 if (canvas->image != NULL && 445 width < canvas->image->width && 446 height < canvas->image->height) 447 { 448 return; 449 } 450 451 // if we don't have enough space to strike the largest glyph in the 452 // run, resize the canvas 453 CGGI_FreeCanvas(canvas); 454 CGGI_InitCanvas(canvas, 455 width * CGGI_GLYPH_CANVAS_SLACK, 456 height * CGGI_GLYPH_CANVAS_SLACK, 457 mode); 458 JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode); 459 } 460 461 /* 462 * Clear the canvas by blitting white only into the region of interest 463 * (the rect which we will copy out of once the glyph is struck). 464 */ 465 static inline void 466 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info) 467 { 468 vImage_Buffer canvasRectToClear; 469 canvasRectToClear.data = canvas->image->data; 470 canvasRectToClear.height = info->height; 471 canvasRectToClear.width = info->width; 472 // use the row stride of the canvas, not the info 473 canvasRectToClear.rowBytes = canvas->image->rowBytes; 474 475 // clean the canvas 476 #ifdef CGGI_DEBUG 477 Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 }; 478 #else 479 Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF }; 480 #endif 481 482 // clear canvas background and set foreground color 483 vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags); 484 } 485 486 487 #pragma mark --- GlyphInfo Creation & Copy Functions --- 488 489 /* 490 * Creates a GlyphInfo with exactly the correct size image and measurements. 491 */ 492 #define CGGI_GLYPH_BBOX_PADDING 2.0f 493 static inline GlyphInfo * 494 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox, 495 const AWTStrike *strike, 496 const CGGI_RenderingMode *mode) 497 { 498 size_t pixelSize = mode->glyphDescriptor->pixelSize; 499 500 // adjust the bounding box to be 1px bigger on each side than what 501 // CGFont-whatever suggests - because it gives a bounding box that 502 // is too tight 503 bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f; 504 bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f; 505 bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING; 506 bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING; 507 508 vImagePixelCount width = ceilf(bbox.size.width); 509 vImagePixelCount height = ceilf(bbox.size.height); 510 511 // if the glyph is larger than 1MB, don't even try... 512 // the GlyphVector path should have taken over by now 513 // and zero pixels is ok 514 if (width * height > 1024 * 1024) { 515 width = 1; 516 height = 1; 517 } 518 advance = CGSizeApplyAffineTransform(advance, strike->fFontTx); 519 if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) { 520 advance.width = round(advance.width); 521 advance.height = round(advance.height); 522 } 523 advance = CGSizeApplyAffineTransform(advance, strike->fDevTx); 524 525 #ifdef USE_IMAGE_ALIGNED_MEMORY 526 // create separate memory 527 GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo)); 528 void *image = (void *)malloc(height * width * pixelSize); 529 #else 530 // create a GlyphInfo struct fused to the image it points to 531 GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) + 532 height * width * pixelSize); 533 #endif 534 535 glyphInfo->advanceX = advance.width; 536 glyphInfo->advanceY = advance.height; 537 glyphInfo->topLeftX = round(bbox.origin.x); 538 glyphInfo->topLeftY = round(bbox.origin.y); 539 glyphInfo->width = width; 540 glyphInfo->height = height; 541 glyphInfo->rowBytes = width * pixelSize; 542 glyphInfo->cellInfo = NULL; 543 544 #ifdef USE_IMAGE_ALIGNED_MEMORY 545 glyphInfo->image = image; 546 #else 547 glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo); 548 #endif 549 550 return glyphInfo; 551 } 552 553 554 #pragma mark --- Glyph Striking onto Canvas --- 555 556 /* 557 * Clears the canvas, strikes the glyph with CoreGraphics, and then 558 * copies the struck pixels into the GlyphInfo image. 559 */ 560 static inline void 561 CGGI_CreateImageForGlyph 562 (CGGI_GlyphCanvas *canvas, const CGGlyph glyph, 563 GlyphInfo *info, const CGGI_RenderingMode *mode) 564 { 565 // clean the canvas 566 CGGI_ClearCanvas(canvas, info); 567 568 if (isnan(info->topLeftX) || isnan(info->topLeftY)) { 569 return; 570 } 571 // strike the glyph in the upper right corner 572 CGContextShowGlyphsAtPoint(canvas->context, 573 -info->topLeftX, 574 canvas->image->height + info->topLeftY, 575 &glyph, 1); 576 // copy the glyph from the canvas into the info 577 (*mode->glyphDescriptor->copyFxnPtr)(canvas, info); 578 } 579 580 /* 581 * CoreText path... 582 */ 583 static inline GlyphInfo * 584 CGGI_CreateImageForUnicode 585 (CGGI_GlyphCanvas *canvas, const AWTStrike *strike, 586 const CGGI_RenderingMode *mode, const UniChar uniChar) 587 { 588 // save the state of the world 589 CGContextSaveGState(canvas->context); 590 591 // get the glyph, measure it using CG 592 CGGlyph glyph; 593 CTFontRef fallback; 594 if (uniChar > 0xFFFF) { 595 UTF16Char charRef[2]; 596 CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef); 597 CGGlyph glyphTmp[2]; 598 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2); 599 glyph = glyphTmp[0]; 600 } else { 601 UTF16Char charRef; 602 charRef = (UTF16Char) uniChar; // truncate. 603 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1); 604 } 605 606 CGAffineTransform tx = strike->fTx; 607 JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); 608 609 CGRect bbox; 610 JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox); 611 612 CGSize advance; 613 CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1); 614 615 // create the Sun2D GlyphInfo we are going to strike into 616 GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode); 617 618 // fix the context size, just in case the substituted character is unexpectedly large 619 CGGI_SizeCanvas(canvas, info->width, info->height, mode); 620 621 // align the transform for the real CoreText strike 622 CGContextSetTextMatrix(canvas->context, strike->fAltTx); 623 624 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); 625 CGContextSetFont(canvas->context, cgFallback); 626 CFRelease(cgFallback); 627 628 // clean the canvas - align, strike, and copy the glyph from the canvas into the info 629 CGGI_CreateImageForGlyph(canvas, glyph, info, mode); 630 631 // restore the state of the world 632 CGContextRestoreGState(canvas->context); 633 634 CFRelease(fallback); 635 #ifdef CGGI_DEBUG 636 DUMP_GLYPHINFO(info); 637 #endif 638 639 #ifdef CGGI_DEBUG_DUMP 640 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 641 #if 0 642 PRINT_CGSTATES_INFO(NULL); 643 #endif 644 #endif 645 646 return info; 647 } 648 649 650 #pragma mark --- GlyphInfo Filling and Canvas Managment --- 651 652 /* 653 * Sets all the per-run properties for the canvas, and then iterates through 654 * the character run, and creates images in the GlyphInfo structs. 655 * 656 * Not inlined because it would create two copies in the function below 657 */ 658 static void 659 CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas, 660 const AWTStrike *strike, 661 const CGGI_RenderingMode *mode, 662 jlong glyphInfos[], 663 const UniChar uniChars[], 664 const CGGlyph glyphs[], 665 const CFIndex len) 666 { 667 CGContextSetTextMatrix(canvas->context, strike->fAltTx); 668 669 CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont); 670 JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle); 671 672 CFIndex i; 673 for (i = 0; i < len; i++) { 674 GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]); 675 if (info != NULL) { 676 CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode); 677 } else { 678 info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]); 679 glyphInfos[i] = ptr_to_jlong(info); 680 } 681 #ifdef CGGI_DEBUG 682 DUMP_GLYPHINFO(info); 683 #endif 684 685 #ifdef CGGI_DEBUG_DUMP 686 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 687 #endif 688 } 689 #ifdef CGGI_DEBUG_DUMP 690 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 691 PRINT_CGSTATES_INFO(canvas->context); 692 #endif 693 } 694 695 static NSString *threadLocalAACanvasKey = 696 @"Java CoreGraphics Text Renderer Cached Canvas for AA"; 697 698 static NSString *threadLocalLCDCanvasKey = 699 @"Java CoreGraphics Text Renderer Cached Canvas for LCD"; 700 701 /* 702 * This is the maximum length and height times the above slack squared 703 * to determine if we go with the global canvas, or malloc one on the spot. 704 */ 705 #define CGGI_GLYPH_CANVAS_MAX 100 706 707 /* 708 * Based on the space needed to strike the largest character in the run, 709 * either use the global shared canvas, or make one up on the spot, strike 710 * the glyphs, and destroy it. 711 */ 712 static inline void 713 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike, 714 const CGGI_RenderingMode *mode, 715 const UniChar uniChars[], const CGGlyph glyphs[], 716 const size_t maxWidth, const size_t maxHeight, 717 const CFIndex len) 718 { 719 if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK > 720 CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK) 721 { 722 CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init]; 723 CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode); 724 CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike, 725 mode, glyphInfos, uniChars, 726 glyphs, len); 727 CGGI_FreeCanvas(tmpCanvas); 728 729 [tmpCanvas release]; 730 return; 731 } 732 NSMutableDictionary *threadDict = 733 [[NSThread currentThread] threadDictionary]; 734 735 NSString* theKey = (mode->glyphDescriptor == &rgb) ? 736 threadLocalLCDCanvasKey : threadLocalAACanvasKey; 737 738 CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey]; 739 if (canvas == nil) { 740 canvas = [[CGGI_GlyphCanvas alloc] init]; 741 [threadDict setObject:canvas forKey:theKey]; 742 } 743 744 CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode); 745 CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode, 746 glyphInfos, uniChars, glyphs, len); 747 } 748 749 /* 750 * Finds the advances and bounding boxes of the characters in the run, 751 * cycles through all the bounds and calculates the maximum canvas space 752 * required by the largest glyph. 753 * 754 * Creates a GlyphInfo struct with a malloc that also encapsulates the 755 * image the struct points to. This is done to meet memory layout 756 * expectations in the Sun text rasterizer memory managment code. 757 * The image immediately follows the struct physically in memory. 758 */ 759 static inline void 760 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike, 761 const CGGI_RenderingMode *mode, 762 const UniChar uniChars[], const CGGlyph glyphs[], 763 CGSize advances[], CGRect bboxes[], const CFIndex len) 764 { 765 AWTFont *font = strike->fAWTFont; 766 CGAffineTransform tx = strike->fTx; 767 JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); 768 769 JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes); 770 CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len); 771 772 size_t maxWidth = 1; 773 size_t maxHeight = 1; 774 775 CFIndex i; 776 for (i = 0; i < len; i++) 777 { 778 if (uniChars[i] != 0) 779 { 780 glyphInfos[i] = 0L; 781 continue; // will be handled later 782 } 783 784 CGSize advance = advances[i]; 785 CGRect bbox = bboxes[i]; 786 787 GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode); 788 789 if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width; 790 if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height; 791 792 glyphInfos[i] = ptr_to_jlong(glyphInfo); 793 } 794 795 CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars, 796 glyphs, maxWidth, maxHeight, len); 797 } 798 799 800 #pragma mark --- Temporary Buffer Allocations and Initialization --- 801 802 /* 803 * This stage separates the already valid glyph codes from the unicode values 804 * that need special handling - the rawGlyphCodes array is no longer used 805 * after this stage. 806 */ 807 static void 808 CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos, 809 const AWTStrike *strike, 810 const CGGI_RenderingMode *mode, 811 jint rawGlyphCodes[], 812 UniChar uniChars[], CGGlyph glyphs[], 813 CGSize advances[], CGRect bboxes[], 814 const CFIndex len) 815 { 816 CFIndex i; 817 for (i = 0; i < len; i++) { 818 jint code = rawGlyphCodes[i]; 819 if (code < 0) { 820 glyphs[i] = 0; 821 uniChars[i] = -code; 822 } else { 823 glyphs[i] = code; 824 uniChars[i] = 0; 825 } 826 } 827 828 CGGI_CreateGlyphInfos(glyphInfos, strike, mode, 829 uniChars, glyphs, advances, bboxes, len); 830 831 #ifdef CGGI_DEBUG_HIT_COUNT 832 static size_t hitCount = 0; 833 hitCount++; 834 printf("%d\n", (int)hitCount); 835 #endif 836 } 837 838 /* 839 * Conditionally stack allocates buffers for glyphs, bounding boxes, 840 * and advances. Unfortunately to use CG or CT in bulk runs (which is 841 * faster than calling them per character), we have to copy into and out 842 * of these buffers. Still a net win though. 843 */ 844 void 845 CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[], 846 const AWTStrike *strike, 847 jint rawGlyphCodes[], const CFIndex len) 848 { 849 const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike); 850 851 if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) { 852 CGRect bboxes[len]; 853 CGSize advances[len]; 854 CGGlyph glyphs[len]; 855 UniChar uniChars[len]; 856 857 CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, 858 rawGlyphCodes, uniChars, glyphs, 859 advances, bboxes, len); 860 return; 861 } 862 863 // just do one malloc, and carve it up for all the buffers 864 void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) * 865 sizeof(CGGlyph) * sizeof(UniChar) * len); 866 if (buffer == NULL) { 867 [[NSException exceptionWithName:NSMallocException 868 reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise]; 869 } 870 871 CGRect *bboxes = (CGRect *)(buffer); 872 CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len); 873 CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len); 874 UniChar *uniChars = (UniChar *)(glyphs + sizeof(UniChar) * len); 875 876 CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, 877 rawGlyphCodes, uniChars, glyphs, 878 advances, bboxes, len); 879 free(buffer); 880 }