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