193 |
193 |
194 #endif |
194 #endif |
195 |
195 |
196 |
196 |
197 #pragma mark --- Font Rendering Mode Descriptors --- |
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 } |
198 |
223 |
199 static inline void |
224 static inline void |
200 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst) |
225 CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst) |
201 { |
226 { |
202 #if __LITTLE_ENDIAN__ |
227 UInt8* lut = getReverseGammaLut(); |
203 *(dst + 2) = 0xFF - (p >> 24 & 0xFF); |
228 |
204 *(dst + 1) = 0xFF - (p >> 16 & 0xFF); |
229 *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)]; // red |
205 *(dst) = 0xFF - (p >> 8 & 0xFF); |
230 *(dst + 1) = lut[0xFF - (p >> 8 & 0xFF)]; // green |
206 #else |
231 *(dst + 2) = lut[0xFF - (p & 0xFF)]; // blue |
207 *(dst) = 0xFF - (p >> 16 & 0xFF); |
|
208 *(dst + 1) = 0xFF - (p >> 8 & 0xFF); |
|
209 *(dst + 2) = 0xFF - (p & 0xFF); |
|
210 #endif |
|
211 } |
232 } |
212 |
233 |
213 static void |
234 static void |
214 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) |
235 CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) |
215 { |
236 { |
220 size_t destRowWidth = info->width; |
241 size_t destRowWidth = info->width; |
221 |
242 |
222 size_t height = info->height; |
243 size_t height = info->height; |
223 |
244 |
224 size_t y; |
245 size_t y; |
|
246 |
|
247 // fill empty glyph image with black-on-white glyph |
225 for (y = 0; y < height; y++) { |
248 for (y = 0; y < height; y++) { |
226 size_t destRow = y * destRowWidth * 3; |
249 size_t destRow = y * destRowWidth * 3; |
227 size_t srcRow = y * srcRowWidth; |
250 size_t srcRow = y * srcRowWidth; |
228 |
251 |
229 size_t x; |
252 size_t x; |
230 for (x = 0; x < destRowWidth; x++) { |
253 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], |
254 CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x], |
237 dest + destRow + x * 3); |
255 dest + destRow + x * 3); |
238 } |
256 } |
239 } |
257 } |
240 } |
258 } |
258 // vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer, |
276 // vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer, |
259 // &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags); |
277 // &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags); |
260 //} |
278 //} |
261 |
279 |
262 static inline UInt8 |
280 static inline UInt8 |
263 CGGI_ConvertPixelToGreyBit(UInt32 p) |
281 CGGI_ConvertBWPixelToByteGray(UInt32 p) |
264 { |
282 { |
265 #ifdef __LITTLE_ENDIAN__ |
283 return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3); |
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 } |
284 } |
271 |
285 |
272 static void |
286 static void |
273 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) |
287 CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) |
274 { |
288 { |
279 size_t destRowWidth = info->width; |
293 size_t destRowWidth = info->width; |
280 |
294 |
281 size_t height = info->height; |
295 size_t height = info->height; |
282 |
296 |
283 size_t y; |
297 size_t y; |
|
298 |
|
299 // fill empty glyph image with black-on-white glyph |
284 for (y = 0; y < height; y++) { |
300 for (y = 0; y < height; y++) { |
285 size_t destRow = y * destRowWidth; |
301 size_t destRow = y * destRowWidth; |
286 size_t srcRow = y * srcRowWidth; |
302 size_t srcRow = y * srcRowWidth; |
287 |
|
288 size_t x; |
303 size_t x; |
289 for (x = 0; x < destRowWidth; x++) { |
304 for (x = 0; x < destRowWidth; x++) { |
290 UInt32 p = src[srcRow + x]; |
305 UInt32 p = src[srcRow + x]; |
291 dest[destRow + x] = CGGI_ConvertPixelToGreyBit(p); |
306 dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p); |
292 } |
307 } |
293 } |
308 } |
294 } |
309 } |
295 |
310 |
296 |
311 |
314 static inline CGGI_RenderingMode |
329 static inline CGGI_RenderingMode |
315 CGGI_GetRenderingMode(const AWTStrike *strike) |
330 CGGI_GetRenderingMode(const AWTStrike *strike) |
316 { |
331 { |
317 CGGI_RenderingMode mode; |
332 CGGI_RenderingMode mode; |
318 mode.cgFontMode = strike->fStyle; |
333 mode.cgFontMode = strike->fStyle; |
|
334 NSException *e = nil; |
319 |
335 |
320 switch (strike->fAAStyle) { |
336 switch (strike->fAAStyle) { |
321 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT: |
|
322 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF: |
337 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF: |
323 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON: |
338 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON: |
324 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP: |
|
325 default: |
|
326 mode.glyphDescriptor = &grey; |
339 mode.glyphDescriptor = &grey; |
327 break; |
340 break; |
328 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB: |
341 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB: |
329 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR: |
342 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR: |
330 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB: |
343 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB: |
331 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR: |
344 case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR: |
332 mode.glyphDescriptor = &rgb; |
345 mode.glyphDescriptor = &rgb; |
333 break; |
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; |
334 } |
358 } |
335 |
359 |
336 return mode; |
360 return mode; |
337 } |
361 } |
338 |
362 |
343 * Creates a new canvas of a fixed size, and initializes the CGContext as |
367 * 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. |
368 * an 32-bit ARGB BitmapContext with some generic RGB color space. |
345 */ |
369 */ |
346 static inline void |
370 static inline void |
347 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, |
371 CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, |
348 const vImagePixelCount width, const vImagePixelCount height) |
372 const vImagePixelCount width, const vImagePixelCount height, |
|
373 const CGGI_RenderingMode* mode) |
349 { |
374 { |
350 // our canvas is *always* 4-byte ARGB |
375 // our canvas is *always* 4-byte ARGB |
351 size_t bytesPerRow = width * sizeof(UInt32); |
376 size_t bytesPerRow = width * sizeof(UInt32); |
352 size_t byteCount = bytesPerRow * height; |
377 size_t byteCount = bytesPerRow * height; |
353 |
378 |
354 canvas->image = malloc(sizeof(vImage_Buffer)); |
379 canvas->image = malloc(sizeof(vImage_Buffer)); |
355 canvas->image->width = width; |
380 canvas->image->width = width; |
356 canvas->image->height = height; |
381 canvas->image->height = height; |
357 canvas->image->rowBytes = bytesPerRow; |
382 canvas->image->rowBytes = bytesPerRow; |
358 |
383 |
359 canvas->image->data = (void *)calloc(byteCount, sizeof(UInt32)); |
384 canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8)); |
360 if (canvas->image->data == NULL) { |
385 if (canvas->image->data == NULL) { |
361 [[NSException exceptionWithName:NSMallocException |
386 [[NSException exceptionWithName:NSMallocException |
362 reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise]; |
387 reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise]; |
363 } |
388 } |
364 |
389 |
365 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); |
390 uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst; |
|
391 if (mode->glyphDescriptor == &rgb) { |
|
392 bmpInfo |= kCGBitmapByteOrder32Host; |
|
393 } |
|
394 |
|
395 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); |
366 canvas->context = CGBitmapContextCreate(canvas->image->data, |
396 canvas->context = CGBitmapContextCreate(canvas->image->data, |
367 width, height, 8, bytesPerRow, |
397 width, height, 8, bytesPerRow, |
368 colorSpace, |
398 colorSpace, |
369 kCGImageAlphaPremultipliedFirst); |
399 bmpInfo); |
370 |
400 |
|
401 // set foreground color |
371 CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f); |
402 CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f); |
|
403 |
372 CGContextSetFontSize(canvas->context, 1); |
404 CGContextSetFontSize(canvas->context, 1); |
373 CGContextSaveGState(canvas->context); |
405 CGContextSaveGState(canvas->context); |
374 |
406 |
375 CGColorSpaceRelease(colorSpace); |
407 CGColorSpaceRelease(colorSpace); |
376 } |
408 } |
402 |
434 |
403 /* |
435 /* |
404 * Quick and easy inline to check if this canvas is big enough. |
436 * Quick and easy inline to check if this canvas is big enough. |
405 */ |
437 */ |
406 static inline void |
438 static inline void |
407 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vImagePixelCount height, const JRSFontRenderingStyle style) |
439 CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, |
|
440 const vImagePixelCount height, |
|
441 const CGGI_RenderingMode* mode) |
408 { |
442 { |
409 if (canvas->image != NULL && |
443 if (canvas->image != NULL && |
410 width < canvas->image->width && |
444 width < canvas->image->width && |
411 height < canvas->image->height) |
445 height < canvas->image->height) |
412 { |
446 { |
416 // if we don't have enough space to strike the largest glyph in the |
450 // if we don't have enough space to strike the largest glyph in the |
417 // run, resize the canvas |
451 // run, resize the canvas |
418 CGGI_FreeCanvas(canvas); |
452 CGGI_FreeCanvas(canvas); |
419 CGGI_InitCanvas(canvas, |
453 CGGI_InitCanvas(canvas, |
420 width * CGGI_GLYPH_CANVAS_SLACK, |
454 width * CGGI_GLYPH_CANVAS_SLACK, |
421 height * CGGI_GLYPH_CANVAS_SLACK); |
455 height * CGGI_GLYPH_CANVAS_SLACK, |
422 JRSFontSetRenderingStyleOnContext(canvas->context, style); |
456 mode); |
|
457 JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode); |
423 } |
458 } |
424 |
459 |
425 /* |
460 /* |
426 * Clear the canvas by blitting white only into the region of interest |
461 * 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). |
462 * (the rect which we will copy out of once the glyph is struck). |
441 Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 }; |
476 Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 }; |
442 #else |
477 #else |
443 Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF }; |
478 Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF }; |
444 #endif |
479 #endif |
445 |
480 |
|
481 // clear canvas background and set foreground color |
446 vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags); |
482 vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags); |
447 } |
483 } |
448 |
484 |
449 |
485 |
450 #pragma mark --- GlyphInfo Creation & Copy Functions --- |
486 #pragma mark --- GlyphInfo Creation & Copy Functions --- |
575 |
611 |
576 // create the Sun2D GlyphInfo we are going to strike into |
612 // create the Sun2D GlyphInfo we are going to strike into |
577 GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode); |
613 GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode); |
578 |
614 |
579 // fix the context size, just in case the substituted character is unexpectedly large |
615 // fix the context size, just in case the substituted character is unexpectedly large |
580 CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode); |
616 CGGI_SizeCanvas(canvas, info->width, info->height, mode); |
581 |
617 |
582 // align the transform for the real CoreText strike |
618 // align the transform for the real CoreText strike |
583 CGContextSetTextMatrix(canvas->context, strike->fAltTx); |
619 CGContextSetTextMatrix(canvas->context, strike->fAltTx); |
584 |
620 |
585 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); |
621 const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); |
651 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); |
687 DUMP_IMG_PIXELS("CGGI Canvas", canvas->image); |
652 PRINT_CGSTATES_INFO(canvas->context); |
688 PRINT_CGSTATES_INFO(canvas->context); |
653 #endif |
689 #endif |
654 } |
690 } |
655 |
691 |
656 static NSString *threadLocalCanvasKey = |
692 static NSString *threadLocalAACanvasKey = |
657 @"Java CoreGraphics Text Renderer Cached Canvas"; |
693 @"Java CoreGraphics Text Renderer Cached Canvas for AA"; |
|
694 |
|
695 static NSString *threadLocalLCDCanvasKey = |
|
696 @"Java CoreGraphics Text Renderer Cached Canvas for LCD"; |
658 |
697 |
659 /* |
698 /* |
660 * This is the maximum length and height times the above slack squared |
699 * 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. |
700 * to determine if we go with the global canvas, or malloc one on the spot. |
662 */ |
701 */ |
676 { |
715 { |
677 if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK > |
716 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) |
717 CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK) |
679 { |
718 { |
680 CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init]; |
719 CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init]; |
681 CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight); |
720 CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode); |
682 CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike, |
721 CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike, |
683 mode, glyphInfos, uniChars, |
722 mode, glyphInfos, uniChars, |
684 glyphs, len); |
723 glyphs, len); |
685 CGGI_FreeCanvas(tmpCanvas); |
724 CGGI_FreeCanvas(tmpCanvas); |
686 |
725 |
687 [tmpCanvas release]; |
726 [tmpCanvas release]; |
688 return; |
727 return; |
689 } |
728 } |
690 |
|
691 NSMutableDictionary *threadDict = |
729 NSMutableDictionary *threadDict = |
692 [[NSThread currentThread] threadDictionary]; |
730 [[NSThread currentThread] threadDictionary]; |
693 CGGI_GlyphCanvas *canvas = [threadDict objectForKey:threadLocalCanvasKey]; |
731 |
|
732 NSString* theKey = (mode->glyphDescriptor == &rgb) ? |
|
733 threadLocalLCDCanvasKey : threadLocalAACanvasKey; |
|
734 |
|
735 CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey]; |
694 if (canvas == nil) { |
736 if (canvas == nil) { |
695 canvas = [[CGGI_GlyphCanvas alloc] init]; |
737 canvas = [[CGGI_GlyphCanvas alloc] init]; |
696 [threadDict setObject:canvas forKey:threadLocalCanvasKey]; |
738 [threadDict setObject:canvas forKey:theKey]; |
697 } |
739 } |
698 |
740 |
699 CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode->cgFontMode); |
741 CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode); |
700 CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode, |
742 CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode, |
701 glyphInfos, uniChars, glyphs, len); |
743 glyphInfos, uniChars, glyphs, len); |
702 } |
744 } |
703 |
745 |
704 /* |
746 /* |