20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #import <Accelerate/Accelerate.h> // for vImage_Buffer
27 #import <JavaNativeFoundation/JavaNativeFoundation.h>
28
29 #import "CGGlyphImages.h"
30 #import "CoreTextSupport.h"
31 #import "fontscalerdefs.h" // contains the definition of GlyphInfo struct
32
33 #import "sun_awt_SunHints.h"
34
35 //#define USE_IMAGE_ALIGNED_MEMORY 1
36 //#define CGGI_DEBUG 1
37 //#define CGGI_DEBUG_DUMP 1
38 //#define CGGI_DEBUG_HIT_COUNT 1
39
40 #define PRINT_TX(x) \
41 NSLog(@"(%f, %f, %f, %f, %f, %f)", x.a, x.b, x.c, x.d, x.tx, x.ty);
42
43 /*
44 * The GlyphCanvas is a global shared CGContext that characters are struck into.
45 * For each character, the glyph is struck, copied into a GlyphInfo struct, and
46 * the canvas is cleared for the next glyph.
47 *
48 * If the necessary canvas is too large, the shared one will not be used and a
49 * temporary one will be provided.
50 */
51 @interface CGGI_GlyphCanvas : NSObject {
52 @public
53 CGContextRef context;
54 vImage_Buffer *image;
55 }
56 @end;
57
58 @implementation CGGI_GlyphCanvas
59 @end
60
61
62 #pragma mark --- Debugging Helpers ---
63
64 /*
65 * These debug functions are only compiled when CGGI_DEBUG is activated.
66 * They will print out a full UInt8 canvas and any pixels struck (assuming
67 * the canvas is not too big).
68 *
69 * As another debug feature, the entire canvas will be filled with a light
70 * alpha value so it is easy to see where the glyph painting regions are
71 * at runtime.
72 */
73
74 #ifdef CGGI_DEBUG_DUMP
75 static void
76 DUMP_PIXELS(const char msg[], const UInt8 pixels[],
77 const size_t bytesPerPixel, const int width, const int height)
78 {
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;
|
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 {
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;
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
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;
|