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 {
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
199 static inline void
200 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
201 {
202 #if __LITTLE_ENDIAN__
203 *(dst + 2) = 0xFF - (p >> 24 & 0xFF);
204 *(dst + 1) = 0xFF - (p >> 16 & 0xFF);
205 *(dst) = 0xFF - (p >> 8 & 0xFF);
206 #else
207 *(dst) = 0xFF - (p >> 16 & 0xFF);
208 *(dst + 1) = 0xFF - (p >> 8 & 0xFF);
209 *(dst + 2) = 0xFF - (p & 0xFF);
210 #endif
211 }
212
213 static void
214 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
215 {
216 UInt32 *src = (UInt32 *)canvas->image->data;
217 size_t srcRowWidth = canvas->image->width;
218
219 UInt8 *dest = (UInt8 *)info->image;
220 size_t destRowWidth = info->width;
221
222 size_t height = info->height;
223
224 size_t y;
225 for (y = 0; y < height; y++) {
226 size_t destRow = y * destRowWidth * 3;
227 size_t srcRow = y * srcRowWidth;
228
229 size_t x;
230 for (x = 0; x < destRowWidth; x++) {
231 // size_t x3 = x * 3;
232 // UInt32 p = src[srcRow + x];
233 // dest[destRow + x3] = 0xFF - (p >> 16 & 0xFF);
234 // dest[destRow + x3 + 1] = 0xFF - (p >> 8 & 0xFF);
235 // dest[destRow + x3 + 2] = 0xFF - (p & 0xFF);
236 CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
237 dest + destRow + x * 3);
238 }
239 }
240 }
241
242 //static void CGGI_copyImageFromCanvasToAlphaInfo
243 //(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
244 //{
245 // vImage_Buffer infoBuffer;
246 // infoBuffer.data = info->image;
247 // infoBuffer.width = info->width;
248 // infoBuffer.height = info->height;
249 // infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
250 //
251 // UInt8 scrapPixel[info->width * info->height];
252 // vImage_Buffer scrapBuffer;
253 // scrapBuffer.data = &scrapPixel;
254 // scrapBuffer.width = info->width;
255 // scrapBuffer.height = info->height;
256 // scrapBuffer.rowBytes = info->width;
257 //
258 // vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
259 // &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
260 //}
261
262 static inline UInt8
263 CGGI_ConvertPixelToGreyBit(UInt32 p)
264 {
265 #ifdef __LITTLE_ENDIAN__
266 return 0xFF - ((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3;
267 #else
268 return 0xFF - ((p >> 16 & 0xFF) + (p >> 8 & 0xFF) + (p & 0xFF)) / 3;
269 #endif
270 }
271
272 static void
273 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
274 {
275 UInt32 *src = (UInt32 *)canvas->image->data;
276 size_t srcRowWidth = canvas->image->width;
277
278 UInt8 *dest = (UInt8 *)info->image;
279 size_t destRowWidth = info->width;
280
281 size_t height = info->height;
282
283 size_t y;
284 for (y = 0; y < height; y++) {
285 size_t destRow = y * destRowWidth;
286 size_t srcRow = y * srcRowWidth;
287
288 size_t x;
289 for (x = 0; x < destRowWidth; x++) {
290 UInt32 p = src[srcRow + x];
291 dest[destRow + x] = CGGI_ConvertPixelToGreyBit(p);
292 }
293 }
294 }
295
296
297 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
298
299 typedef struct CGGI_GlyphInfoDescriptor {
300 size_t pixelSize;
301 void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
302 } CGGI_GlyphInfoDescriptor;
303
304 typedef struct CGGI_RenderingMode {
305 CGGI_GlyphInfoDescriptor *glyphDescriptor;
306 JRSFontRenderingStyle cgFontMode;
307 } CGGI_RenderingMode;
308
309 static CGGI_GlyphInfoDescriptor grey =
310 { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
311 static CGGI_GlyphInfoDescriptor rgb =
312 { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
313
314 static inline CGGI_RenderingMode
315 CGGI_GetRenderingMode(const AWTStrike *strike)
316 {
317 CGGI_RenderingMode mode;
318 mode.cgFontMode = strike->fStyle;
319
320 switch (strike->fAAStyle) {
321 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
322 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
323 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
324 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
325 default:
326 mode.glyphDescriptor = &grey;
327 break;
328 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
329 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
330 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
331 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
332 mode.glyphDescriptor = &rgb;
333 break;
334 }
335
336 return mode;
337 }
338
339
340 #pragma mark --- Canvas Managment ---
341
342 /*
343 * Creates a new canvas of a fixed size, and initializes the CGContext as
344 * an 32-bit ARGB BitmapContext with some generic RGB color space.
345 */
346 static inline void
347 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
348 const vImagePixelCount width, const vImagePixelCount height)
349 {
350 // our canvas is *always* 4-byte ARGB
351 size_t bytesPerRow = width * sizeof(UInt32);
352 size_t byteCount = bytesPerRow * height;
353
354 canvas->image = malloc(sizeof(vImage_Buffer));
355 canvas->image->width = width;
356 canvas->image->height = height;
357 canvas->image->rowBytes = bytesPerRow;
358
359 canvas->image->data = (void *)calloc(byteCount, sizeof(UInt32));
360 if (canvas->image->data == NULL) {
361 [[NSException exceptionWithName:NSMallocException
362 reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
363 }
364
365 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
366 canvas->context = CGBitmapContextCreate(canvas->image->data,
367 width, height, 8, bytesPerRow,
368 colorSpace,
369 kCGImageAlphaPremultipliedFirst);
370
371 CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
372 CGContextSetFontSize(canvas->context, 1);
373 CGContextSaveGState(canvas->context);
374
375 CGColorSpaceRelease(colorSpace);
376 }
377
378 /*
379 * Releases the BitmapContext and the associated memory backing it.
380 */
381 static inline void
382 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
383 {
384 if (canvas->context != NULL) {
385 CGContextRelease(canvas->context);
386 }
387
388 if (canvas->image != NULL) {
389 if (canvas->image->data != NULL) {
390 free(canvas->image->data);
391 }
392 free(canvas->image);
393 }
394 }
395
396 /*
397 * This is the slack space that is preallocated for the global GlyphCanvas
398 * when it needs to be expanded. It has been set somewhat liberally to
399 * avoid re-upsizing frequently.
400 */
401 #define CGGI_GLYPH_CANVAS_SLACK 2.5
402
403 /*
404 * Quick and easy inline to check if this canvas is big enough.
405 */
406 static inline void
407 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vImagePixelCount height, const JRSFontRenderingStyle style)
408 {
409 if (canvas->image != NULL &&
410 width < canvas->image->width &&
411 height < canvas->image->height)
412 {
413 return;
414 }
415
416 // if we don't have enough space to strike the largest glyph in the
417 // run, resize the canvas
418 CGGI_FreeCanvas(canvas);
419 CGGI_InitCanvas(canvas,
420 width * CGGI_GLYPH_CANVAS_SLACK,
421 height * CGGI_GLYPH_CANVAS_SLACK);
422 JRSFontSetRenderingStyleOnContext(canvas->context, style);
423 }
424
425 /*
426 * Clear the canvas by blitting white only into the region of interest
427 * (the rect which we will copy out of once the glyph is struck).
428 */
429 static inline void
430 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
431 {
432 vImage_Buffer canvasRectToClear;
433 canvasRectToClear.data = canvas->image->data;
434 canvasRectToClear.height = info->height;
435 canvasRectToClear.width = info->width;
436 // use the row stride of the canvas, not the info
437 canvasRectToClear.rowBytes = canvas->image->rowBytes;
438
439 // clean the canvas
440 #ifdef CGGI_DEBUG
441 Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
442 #else
443 Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
444 #endif
445
446 vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
447 }
448
449
450 #pragma mark --- GlyphInfo Creation & Copy Functions ---
451
452 /*
453 * Creates a GlyphInfo with exactly the correct size image and measurements.
454 */
455 #define CGGI_GLYPH_BBOX_PADDING 2.0f
456 static inline GlyphInfo *
457 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
458 const AWTStrike *strike,
459 const CGGI_RenderingMode *mode)
460 {
461 size_t pixelSize = mode->glyphDescriptor->pixelSize;
462
463 // adjust the bounding box to be 1px bigger on each side than what
464 // CGFont-whatever suggests - because it gives a bounding box that
465 // is too tight
466 bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;
560 glyph = glyphTmp[0];
561 } else {
562 UTF16Char charRef;
563 charRef = (UTF16Char) uniChar; // truncate.
564 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
565 }
566
567 CGAffineTransform tx = strike->fTx;
568 JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
569
570 CGRect bbox;
571 JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
572
573 CGSize advance;
574 CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
575
576 // create the Sun2D GlyphInfo we are going to strike into
577 GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
578
579 // fix the context size, just in case the substituted character is unexpectedly large
580 CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode);
581
582 // align the transform for the real CoreText strike
583 CGContextSetTextMatrix(canvas->context, strike->fAltTx);
584
585 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
586 CGContextSetFont(canvas->context, cgFallback);
587 CFRelease(cgFallback);
588
589 // clean the canvas - align, strike, and copy the glyph from the canvas into the info
590 CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
591
592 // restore the state of the world
593 CGContextRestoreGState(canvas->context);
594
595 CFRelease(fallback);
596 #ifdef CGGI_DEBUG
597 DUMP_GLYPHINFO(info);
598 #endif
599
600 #ifdef CGGI_DEBUG_DUMP
636 if (info != NULL) {
637 CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
638 } else {
639 info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
640 glyphInfos[i] = ptr_to_jlong(info);
641 }
642 #ifdef CGGI_DEBUG
643 DUMP_GLYPHINFO(info);
644 #endif
645
646 #ifdef CGGI_DEBUG_DUMP
647 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
648 #endif
649 }
650 #ifdef CGGI_DEBUG_DUMP
651 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
652 PRINT_CGSTATES_INFO(canvas->context);
653 #endif
654 }
655
656 static NSString *threadLocalCanvasKey =
657 @"Java CoreGraphics Text Renderer Cached Canvas";
658
659 /*
660 * This is the maximum length and height times the above slack squared
661 * to determine if we go with the global canvas, or malloc one on the spot.
662 */
663 #define CGGI_GLYPH_CANVAS_MAX 100
664
665 /*
666 * Based on the space needed to strike the largest character in the run,
667 * either use the global shared canvas, or make one up on the spot, strike
668 * the glyphs, and destroy it.
669 */
670 static inline void
671 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
672 const CGGI_RenderingMode *mode,
673 const UniChar uniChars[], const CGGlyph glyphs[],
674 const size_t maxWidth, const size_t maxHeight,
675 const CFIndex len)
676 {
677 if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
678 CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
679 {
680 CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
681 CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight);
682 CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
683 mode, glyphInfos, uniChars,
684 glyphs, len);
685 CGGI_FreeCanvas(tmpCanvas);
686
687 [tmpCanvas release];
688 return;
689 }
690
691 NSMutableDictionary *threadDict =
692 [[NSThread currentThread] threadDictionary];
693 CGGI_GlyphCanvas *canvas = [threadDict objectForKey:threadLocalCanvasKey];
694 if (canvas == nil) {
695 canvas = [[CGGI_GlyphCanvas alloc] init];
696 [threadDict setObject:canvas forKey:threadLocalCanvasKey];
697 }
698
699 CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode->cgFontMode);
700 CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
701 glyphInfos, uniChars, glyphs, len);
702 }
703
704 /*
705 * Finds the advances and bounding boxes of the characters in the run,
706 * cycles through all the bounds and calculates the maximum canvas space
707 * required by the largest glyph.
708 *
709 * Creates a GlyphInfo struct with a malloc that also encapsulates the
710 * image the struct points to. This is done to meet memory layout
711 * expectations in the Sun text rasterizer memory managment code.
712 * The image immediately follows the struct physically in memory.
713 */
714 static inline void
715 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
716 const CGGI_RenderingMode *mode,
717 const UniChar uniChars[], const CGGlyph glyphs[],
718 CGSize advances[], CGRect bboxes[], const CFIndex len)
719 {
720 AWTFont *font = strike->fAWTFont;
721 CGAffineTransform tx = strike->fTx;
|
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 {
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;
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
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;
|