178
179 static void
180 DUMP_GLYPHINFO(const GlyphInfo *info)
181 {
182 printf("size: (%d, %d) pixelSize: %d\n",
183 info->width, info->height, info->rowBytes / info->width);
184 printf("adv: (%f, %f) top: (%f, %f)\n",
185 info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);
186
187 #ifdef CGGI_DEBUG_DUMP
188 DUMP_PIXELS("Glyph Info Struct",
189 info->image, info->rowBytes / info->width,
190 info->width, info->height);
191 #endif
192 }
193
194 #endif
195
196
197 #pragma mark --- Font Rendering Mode Descriptors ---
198
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
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 {
|
178
179 static void
180 DUMP_GLYPHINFO(const GlyphInfo *info)
181 {
182 printf("size: (%d, %d) pixelSize: %d\n",
183 info->width, info->height, info->rowBytes / info->width);
184 printf("adv: (%f, %f) top: (%f, %f)\n",
185 info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);
186
187 #ifdef CGGI_DEBUG_DUMP
188 DUMP_PIXELS("Glyph Info Struct",
189 info->image, info->rowBytes / info->width,
190 info->width, info->height);
191 #endif
192 }
193
194 #endif
195
196
197 #pragma mark --- Font Rendering Mode Descriptors ---
198 static Int32 reverseGamma = 0;
199
200 static UInt8 reverseGammaLut[256] = { 0 };
201
202 static inline UInt8* getReverseGammaLut() {
203 if (reverseGamma == 0) {
204 // initialize gamma lut
205 double gamma;
206 const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA");
207 if (pGammaEnv != NULL) {
208 reverseGamma = atol(pGammaEnv);
209 }
210
211 if (reverseGamma < 100 || reverseGamma > 250) {
212 reverseGamma = 180;
213 }
214
215 gamma = 100.0 / reverseGamma;
216 for (int i = 0; i < 256; i++) {
217 double x = ((double)i) / 255.0;
218 reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma));
219 }
220 }
221 return reverseGammaLut;
222 }
223
224 static inline void
225 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
226 {
227 UInt8* lut = getReverseGammaLut();
228
229 *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)]; // red
230 *(dst + 1) = lut[0xFF - (p >> 8 & 0xFF)]; // green
231 *(dst + 2) = lut[0xFF - (p & 0xFF)]; // blue
232 }
233
234 static void
235 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
236 {
237 UInt32 *src = (UInt32 *)canvas->image->data;
238 size_t srcRowWidth = canvas->image->width;
239
240 UInt8 *dest = (UInt8 *)info->image;
241 size_t destRowWidth = info->width;
242
243 size_t height = info->height;
244
245 size_t y;
246
247 // fill empty glyph image with black-on-white glyph
248 for (y = 0; y < height; y++) {
249 size_t destRow = y * destRowWidth * 3;
250 size_t srcRow = y * srcRowWidth;
251
252 size_t x;
253 for (x = 0; x < destRowWidth; x++) {
254 CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
255 dest + destRow + x * 3);
256 }
257 }
258 }
259
260 //static void CGGI_copyImageFromCanvasToAlphaInfo
261 //(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
262 //{
263 // vImage_Buffer infoBuffer;
264 // infoBuffer.data = info->image;
265 // infoBuffer.width = info->width;
266 // infoBuffer.height = info->height;
267 // infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
268 //
269 // UInt8 scrapPixel[info->width * info->height];
270 // vImage_Buffer scrapBuffer;
271 // scrapBuffer.data = &scrapPixel;
272 // scrapBuffer.width = info->width;
273 // scrapBuffer.height = info->height;
274 // scrapBuffer.rowBytes = info->width;
275 //
276 // vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
277 // &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
278 //}
279
280 static inline UInt8
281 CGGI_ConvertBWPixelToByteGray(UInt32 p)
282 {
283 return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3);
284 }
285
286 static void
287 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
288 {
289 UInt32 *src = (UInt32 *)canvas->image->data;
290 size_t srcRowWidth = canvas->image->width;
291
292 UInt8 *dest = (UInt8 *)info->image;
293 size_t destRowWidth = info->width;
294
295 size_t height = info->height;
296
297 size_t y;
298
299 // fill empty glyph image with black-on-white glyph
300 for (y = 0; y < height; y++) {
301 size_t destRow = y * destRowWidth;
302 size_t srcRow = y * srcRowWidth;
303 size_t x;
304 for (x = 0; x < destRowWidth; x++) {
305 UInt32 p = src[srcRow + x];
306 dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
307 }
308 }
309 }
310
311
312 #pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
313
314 typedef struct CGGI_GlyphInfoDescriptor {
315 size_t pixelSize;
316 void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
317 } CGGI_GlyphInfoDescriptor;
318
319 typedef struct CGGI_RenderingMode {
320 CGGI_GlyphInfoDescriptor *glyphDescriptor;
321 JRSFontRenderingStyle cgFontMode;
322 } CGGI_RenderingMode;
323
324 static CGGI_GlyphInfoDescriptor grey =
325 { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
326 static CGGI_GlyphInfoDescriptor rgb =
327 { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
328
329 static inline CGGI_RenderingMode
330 CGGI_GetRenderingMode(const AWTStrike *strike)
331 {
332 CGGI_RenderingMode mode;
333 mode.cgFontMode = strike->fStyle;
334 NSException *e = nil;
335
336 switch (strike->fAAStyle) {
337 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
338 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
339 mode.glyphDescriptor = &grey;
340 break;
341 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
342 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
343 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
344 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
345 mode.glyphDescriptor = &rgb;
346 break;
347 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
348 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
349 default:
350 /* we expect that text antialiasing hint has been already
351 * evaluated. Report an error if we get 'unevaluated' hint here.
352 */
353 e = [NSException
354 exceptionWithName:@"IllegalArgumentException"
355 reason:@"Invalid hint value"
356 userInfo:nil];
357 @throw e;
358 }
359
360 return mode;
361 }
362
363
364 #pragma mark --- Canvas Managment ---
365
366 /*
367 * Creates a new canvas of a fixed size, and initializes the CGContext as
368 * an 32-bit ARGB BitmapContext with some generic RGB color space.
369 */
370 static inline void
371 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
372 const vImagePixelCount width, const vImagePixelCount height,
373 const CGGI_RenderingMode* mode)
374 {
375 // our canvas is *always* 4-byte ARGB
376 size_t bytesPerRow = width * sizeof(UInt32);
377 size_t byteCount = bytesPerRow * height;
378
379 canvas->image = malloc(sizeof(vImage_Buffer));
380 canvas->image->width = width;
381 canvas->image->height = height;
382 canvas->image->rowBytes = bytesPerRow;
383
384 canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
385 if (canvas->image->data == NULL) {
386 [[NSException exceptionWithName:NSMallocException
387 reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
388 }
389
390 uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
391 if (mode->glyphDescriptor == &rgb) {
392 bmpInfo |= kCGBitmapByteOrder32Host;
393 }
394
395 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
396 canvas->context = CGBitmapContextCreate(canvas->image->data,
397 width, height, 8, bytesPerRow,
398 colorSpace,
399 bmpInfo);
400
401 // set foreground color
402 CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
403
404 CGContextSetFontSize(canvas->context, 1);
405 CGContextSaveGState(canvas->context);
406
407 CGColorSpaceRelease(colorSpace);
408 }
409
410 /*
411 * Releases the BitmapContext and the associated memory backing it.
412 */
413 static inline void
414 CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
415 {
416 if (canvas->context != NULL) {
417 CGContextRelease(canvas->context);
418 }
419
420 if (canvas->image != NULL) {
421 if (canvas->image->data != NULL) {
422 free(canvas->image->data);
423 }
424 free(canvas->image);
425 }
426 }
427
428 /*
429 * This is the slack space that is preallocated for the global GlyphCanvas
430 * when it needs to be expanded. It has been set somewhat liberally to
431 * avoid re-upsizing frequently.
432 */
433 #define CGGI_GLYPH_CANVAS_SLACK 2.5
434
435 /*
436 * Quick and easy inline to check if this canvas is big enough.
437 */
438 static inline void
439 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
440 const vImagePixelCount height,
441 const CGGI_RenderingMode* mode)
442 {
443 if (canvas->image != NULL &&
444 width < canvas->image->width &&
445 height < canvas->image->height)
446 {
447 return;
448 }
449
450 // if we don't have enough space to strike the largest glyph in the
451 // run, resize the canvas
452 CGGI_FreeCanvas(canvas);
453 CGGI_InitCanvas(canvas,
454 width * CGGI_GLYPH_CANVAS_SLACK,
455 height * CGGI_GLYPH_CANVAS_SLACK,
456 mode);
457 JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
458 }
459
460 /*
461 * Clear the canvas by blitting white only into the region of interest
462 * (the rect which we will copy out of once the glyph is struck).
463 */
464 static inline void
465 CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
466 {
467 vImage_Buffer canvasRectToClear;
468 canvasRectToClear.data = canvas->image->data;
469 canvasRectToClear.height = info->height;
470 canvasRectToClear.width = info->width;
471 // use the row stride of the canvas, not the info
472 canvasRectToClear.rowBytes = canvas->image->rowBytes;
473
474 // clean the canvas
475 #ifdef CGGI_DEBUG
476 Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
477 #else
478 Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
479 #endif
480
481 // clear canvas background and set foreground color
482 vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
483 }
484
485
486 #pragma mark --- GlyphInfo Creation & Copy Functions ---
487
488 /*
489 * Creates a GlyphInfo with exactly the correct size image and measurements.
490 */
491 #define CGGI_GLYPH_BBOX_PADDING 2.0f
492 static inline GlyphInfo *
493 CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
494 const AWTStrike *strike,
495 const CGGI_RenderingMode *mode)
496 {
497 size_t pixelSize = mode->glyphDescriptor->pixelSize;
498
499 // adjust the bounding box to be 1px bigger on each side than what
500 // CGFont-whatever suggests - because it gives a bounding box that
501 // is too tight
596 glyph = glyphTmp[0];
597 } else {
598 UTF16Char charRef;
599 charRef = (UTF16Char) uniChar; // truncate.
600 fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
601 }
602
603 CGAffineTransform tx = strike->fTx;
604 JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
605
606 CGRect bbox;
607 JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
608
609 CGSize advance;
610 CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
611
612 // create the Sun2D GlyphInfo we are going to strike into
613 GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
614
615 // fix the context size, just in case the substituted character is unexpectedly large
616 CGGI_SizeCanvas(canvas, info->width, info->height, mode);
617
618 // align the transform for the real CoreText strike
619 CGContextSetTextMatrix(canvas->context, strike->fAltTx);
620
621 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
622 CGContextSetFont(canvas->context, cgFallback);
623 CFRelease(cgFallback);
624
625 // clean the canvas - align, strike, and copy the glyph from the canvas into the info
626 CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
627
628 // restore the state of the world
629 CGContextRestoreGState(canvas->context);
630
631 CFRelease(fallback);
632 #ifdef CGGI_DEBUG
633 DUMP_GLYPHINFO(info);
634 #endif
635
636 #ifdef CGGI_DEBUG_DUMP
672 if (info != NULL) {
673 CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
674 } else {
675 info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
676 glyphInfos[i] = ptr_to_jlong(info);
677 }
678 #ifdef CGGI_DEBUG
679 DUMP_GLYPHINFO(info);
680 #endif
681
682 #ifdef CGGI_DEBUG_DUMP
683 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
684 #endif
685 }
686 #ifdef CGGI_DEBUG_DUMP
687 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
688 PRINT_CGSTATES_INFO(canvas->context);
689 #endif
690 }
691
692 static NSString *threadLocalAACanvasKey =
693 @"Java CoreGraphics Text Renderer Cached Canvas for AA";
694
695 static NSString *threadLocalLCDCanvasKey =
696 @"Java CoreGraphics Text Renderer Cached Canvas for LCD";
697
698 /*
699 * This is the maximum length and height times the above slack squared
700 * to determine if we go with the global canvas, or malloc one on the spot.
701 */
702 #define CGGI_GLYPH_CANVAS_MAX 100
703
704 /*
705 * Based on the space needed to strike the largest character in the run,
706 * either use the global shared canvas, or make one up on the spot, strike
707 * the glyphs, and destroy it.
708 */
709 static inline void
710 CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
711 const CGGI_RenderingMode *mode,
712 const UniChar uniChars[], const CGGlyph glyphs[],
713 const size_t maxWidth, const size_t maxHeight,
714 const CFIndex len)
715 {
716 if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
717 CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
718 {
719 CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
720 CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
721 CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
722 mode, glyphInfos, uniChars,
723 glyphs, len);
724 CGGI_FreeCanvas(tmpCanvas);
725
726 [tmpCanvas release];
727 return;
728 }
729 NSMutableDictionary *threadDict =
730 [[NSThread currentThread] threadDictionary];
731
732 NSString* theKey = (mode->glyphDescriptor == &rgb) ?
733 threadLocalLCDCanvasKey : threadLocalAACanvasKey;
734
735 CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
736 if (canvas == nil) {
737 canvas = [[CGGI_GlyphCanvas alloc] init];
738 [threadDict setObject:canvas forKey:theKey];
739 }
740
741 CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
742 CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
743 glyphInfos, uniChars, glyphs, len);
744 }
745
746 /*
747 * Finds the advances and bounding boxes of the characters in the run,
748 * cycles through all the bounds and calculates the maximum canvas space
749 * required by the largest glyph.
750 *
751 * Creates a GlyphInfo struct with a malloc that also encapsulates the
752 * image the struct points to. This is done to meet memory layout
753 * expectations in the Sun text rasterizer memory managment code.
754 * The image immediately follows the struct physically in memory.
755 */
756 static inline void
757 CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
758 const CGGI_RenderingMode *mode,
759 const UniChar uniChars[], const CGGlyph glyphs[],
760 CGSize advances[], CGRect bboxes[], const CFIndex len)
761 {
|