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 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 { 95 printf("| %s: (%d, %d)\n", msg, width, height); 96 97 if (width > 80 || height > 80) { 98 printf("| too big\n"); 99 return; 100 } 101 102 size_t i, j = 0, k, size = width * height; 103 for (i = 0; i < size; i++) { 104 for (k = 0; k < bytesPerPixel; k++) { 105 if (pixels[i * bytesPerPixel + k] > 0x80) j++; 106 } 107 } 108 109 if (j == 0) { 110 printf("| empty\n"); 111 return; 112 } 113 114 printf("|_"); 115 int x, y; 116 for (x = 0; x < width; x++) { 117 printf("__"); 118 } 119 printf("_\n"); 120 121 for (y = 0; y < height; y++) { 122 printf("| "); 123 for (x = 0; x < width; x++) { 124 int p = 0; 125 for(k = 0; k < bytesPerPixel; k++) { 126 p += pixels[(y * width + x) * bytesPerPixel + k]; 127 } 128 129 if (p < 0x80) { 130 printf(" "); 131 } else { 132 printf("[]"); 133 } 134 } 135 printf(" |\n"); 136 } 137 } 138 139 static void 140 DUMP_IMG_PIXELS(const char msg[], const vImage_Buffer *image) 141 { 142 const void *pixels = image->data; 143 const size_t pixelSize = image->rowBytes / image->width; 144 const size_t width = image->width; 145 const size_t height = image->height; 146 147 DUMP_PIXELS(msg, pixels, pixelSize, width, height); 148 } 149 150 static void 151 PRINT_CGSTATES_INFO(const CGContextRef cgRef) 152 { 153 // TODO(cpc): lots of SPI use in this method; remove/rewrite? 154 #if 0 155 CGRect clip = CGContextGetClipBoundingBox(cgRef); 156 fprintf(stderr, " clip: ((%f, %f), (%f, %f))\n", 157 clip.origin.x, clip.origin.y, clip.size.width, clip.size.height); 158 159 CGAffineTransform ctm = CGContextGetCTM(cgRef); 160 fprintf(stderr, " ctm: (%f, %f, %f, %f, %f, %f)\n", 161 ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty); 162 163 CGAffineTransform txtTx = CGContextGetTextMatrix(cgRef); 164 fprintf(stderr, " txtTx: (%f, %f, %f, %f, %f, %f)\n", 165 txtTx.a, txtTx.b, txtTx.c, txtTx.d, txtTx.tx, txtTx.ty); 166 167 if (CGContextIsPathEmpty(cgRef) == 0) { 168 CGPoint pathpoint = CGContextGetPathCurrentPoint(cgRef); 169 CGRect pathbbox = CGContextGetPathBoundingBox(cgRef); 170 fprintf(stderr, " [pathpoint: (%f, %f)] [pathbbox: ((%f, %f), (%f, %f))]\n", 171 pathpoint.x, pathpoint.y, pathbbox.origin.x, pathbbox.origin.y, 172 pathbbox.size.width, pathbbox.size.width); 173 } 174 175 CGFloat linewidth = CGContextGetLineWidth(cgRef); 176 CGLineCap linecap = CGContextGetLineCap(cgRef); 177 CGLineJoin linejoin = CGContextGetLineJoin(cgRef); 178 CGFloat miterlimit = CGContextGetMiterLimit(cgRef); 179 size_t dashcount = CGContextGetLineDashCount(cgRef); 180 fprintf(stderr, " [linewidth: %f] [linecap: %d] [linejoin: %d] [miterlimit: %f] [dashcount: %lu]\n", 181 linewidth, linecap, linejoin, miterlimit, (unsigned long)dashcount); 182 183 CGFloat smoothness = CGContextGetSmoothness(cgRef); 184 bool antialias = CGContextGetShouldAntialias(cgRef); 185 bool smoothfont = CGContextGetShouldSmoothFonts(cgRef); 186 JRSFontRenderingStyle fRendMode = CGContextGetFontRenderingMode(cgRef); 187 fprintf(stderr, " [smoothness: %f] [antialias: %d] [smoothfont: %d] [fontrenderingmode: %d]\n", 188 smoothness, antialias, smoothfont, fRendMode); 189 #endif 190 } 191 #endif 192 193 #ifdef CGGI_DEBUG 194 195 static void 196 DUMP_GLYPHINFO(const GlyphInfo *info) 197 { 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; 532 bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f; 533 bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING; 534 bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING; 535 536 vImagePixelCount width = ceilf(bbox.size.width); 537 vImagePixelCount height = ceilf(bbox.size.height); 538 539 // if the glyph is larger than 1MB, don't even try... 540 // the GlyphVector path should have taken over by now 541 // and zero pixels is ok 542 if (width * height > 1024 * 1024) { 543 width = 1; 544 height = 1; 545 } 546 advance = CGSizeApplyAffineTransform(advance, strike->fFontTx); 547 if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) { 548 advance.width = round(advance.width); 549 advance.height = round(advance.height); 550 } 551 advance = CGSizeApplyAffineTransform(advance, strike->fDevTx); 552 553 #ifdef USE_IMAGE_ALIGNED_MEMORY 554 // create separate memory 555 GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo)); 556 void *image = (void *)malloc(height * width * pixelSize); 557 #else 558 // create a GlyphInfo struct fused to the image it points to 559 GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) + 560 height * width * pixelSize); 561 #endif 562 563 glyphInfo->advanceX = advance.width; 564 glyphInfo->advanceY = advance.height; 565 glyphInfo->topLeftX = round(bbox.origin.x); 566 glyphInfo->topLeftY = round(bbox.origin.y); 567 glyphInfo->width = width; 568 glyphInfo->height = height; 569 glyphInfo->rowBytes = width * pixelSize; 570 glyphInfo->cellInfo = NULL; 571 572 #ifdef USE_IMAGE_ALIGNED_MEMORY 573 glyphInfo->image = image; 574 #else 575 glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo); 576 #endif 577 578 return glyphInfo; 579 } 580 581 582 #pragma mark --- Glyph Striking onto Canvas --- 583 584 /* 585 * Clears the canvas, strikes the glyph with CoreGraphics, and then 586 * copies the struck pixels into the GlyphInfo image. 587 */ 588 static inline void 589 CGGI_CreateImageForGlyph 590 (CGGI_GlyphCanvas *canvas, const CGGlyph glyph, 591 GlyphInfo *info, const CGGI_RenderingMode *mode) 592 { 593 // clean the canvas 594 CGGI_ClearCanvas(canvas, info); 595 596 // strike the glyph in the upper right corner 597 CGContextShowGlyphsAtPoint(canvas->context, 598 -info->topLeftX, 599 canvas->image->height + info->topLeftY, 600 &glyph, 1); 601 602 // copy the glyph from the canvas into the info 603 (*mode->glyphDescriptor->copyFxnPtr)(canvas, info); 604 } 605 606 /* 607 * CoreText path... 608 */ 609 static inline GlyphInfo * 610 CGGI_CreateImageForUnicode 611 (CGGI_GlyphCanvas *canvas, const AWTStrike *strike, 612 const CGGI_RenderingMode *mode, const UniChar uniChar) 613 { 614 // save the state of the world 615 CGContextSaveGState(canvas->context); 616 617 // get the glyph, measure it using CG 618 CGGlyph glyph; 619 CTFontRef fallback; 620 if (uniChar > 0xFFFF) { 621 UTF16Char charRef[2]; 622 CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef); 623 CGGlyph glyphTmp[2]; 624 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2); 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 666 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); 667 #if 0 668 PRINT_CGSTATES_INFO(NULL); 669 #endif 670 #endif 671 672 return info; 673 } 674 675 676 #pragma mark --- GlyphInfo Filling and Canvas Managment --- 677 678 /* 679 * Sets all the per-run properties for the canvas, and then iterates through 680 * the character run, and creates images in the GlyphInfo structs. 681 * 682 * Not inlined because it would create two copies in the function below 683 */ 684 static void 685 CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas, 686 const AWTStrike *strike, 687 const CGGI_RenderingMode *mode, 688 jlong glyphInfos[], 689 const UniChar uniChars[], 690 const CGGlyph glyphs[], 691 const CFIndex len) 692 { 693 CGContextSetTextMatrix(canvas->context, strike->fAltTx); 694 695 CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont); 696 JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle); 697 698 CFIndex i; 699 for (i = 0; i < len; i++) { 700 GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]); 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; 816 JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); 817 818 JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes); 819 CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len); 820 821 size_t maxWidth = 1; 822 size_t maxHeight = 1; 823 824 CFIndex i; 825 for (i = 0; i < len; i++) 826 { 827 if (uniChars[i] != 0) 828 { 829 glyphInfos[i] = 0L; 830 continue; // will be handled later 831 } 832 833 CGSize advance = advances[i]; 834 CGRect bbox = bboxes[i]; 835 836 GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode); 837 838 if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width; 839 if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height; 840 841 glyphInfos[i] = ptr_to_jlong(glyphInfo); 842 } 843 844 CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars, 845 glyphs, maxWidth, maxHeight, len); 846 } 847 848 849 #pragma mark --- Temporary Buffer Allocations and Initialization --- 850 851 /* 852 * This stage separates the already valid glyph codes from the unicode values 853 * that need special handling - the rawGlyphCodes array is no longer used 854 * after this stage. 855 */ 856 static void 857 CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos, 858 const AWTStrike *strike, 859 const CGGI_RenderingMode *mode, 860 jint rawGlyphCodes[], 861 UniChar uniChars[], CGGlyph glyphs[], 862 CGSize advances[], CGRect bboxes[], 863 const CFIndex len) 864 { 865 CFIndex i; 866 for (i = 0; i < len; i++) { 867 jint code = rawGlyphCodes[i]; 868 if (code < 0) { 869 glyphs[i] = 0; 870 uniChars[i] = -code; 871 } else { 872 glyphs[i] = code; 873 uniChars[i] = 0; 874 } 875 } 876 877 CGGI_CreateGlyphInfos(glyphInfos, strike, mode, 878 uniChars, glyphs, advances, bboxes, len); 879 880 #ifdef CGGI_DEBUG_HIT_COUNT 881 static size_t hitCount = 0; 882 hitCount++; 883 printf("%d\n", (int)hitCount); 884 #endif 885 } 886 887 /* 888 * Conditionally stack allocates buffers for glyphs, bounding boxes, 889 * and advances. Unfortunately to use CG or CT in bulk runs (which is 890 * faster than calling them per character), we have to copy into and out 891 * of these buffers. Still a net win though. 892 */ 893 void 894 CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[], 895 const AWTStrike *strike, 896 jint rawGlyphCodes[], const CFIndex len) 897 { 898 const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike); 899 900 if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) { 901 CGRect bboxes[len]; 902 CGSize advances[len]; 903 CGGlyph glyphs[len]; 904 UniChar uniChars[len]; 905 906 CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, 907 rawGlyphCodes, uniChars, glyphs, 908 advances, bboxes, len); 909 910 return; 911 } 912 913 // just do one malloc, and carve it up for all the buffers 914 void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) * 915 sizeof(CGGlyph) * sizeof(UniChar) * len); 916 if (buffer == NULL) { 917 [[NSException exceptionWithName:NSMallocException 918 reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise]; 919 } 920 921 CGRect *bboxes = (CGRect *)(buffer); 922 CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len); 923 CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len); 924 UniChar *uniChars = (UniChar *)(glyphs + sizeof(UniChar) * len); 925 926 CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode, 927 rawGlyphCodes, uniChars, glyphs, 928 advances, bboxes, len); 929 930 free(buffer); 931 }