< prev index next >
src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m
Print this page
@@ -46,18 +46,32 @@
* the canvas is cleared for the next glyph.
*
* If the necessary canvas is too large, the shared one will not be used and a
* temporary one will be provided.
*/
+typedef enum {
+ BLACK_ON_WHITE_STAGE,
+ WHITE_ON_BLACK_STAGE
+} CGGI_GlyphRenderingStage;
+
@interface CGGI_GlyphCanvas : NSObject {
@public
CGContextRef context;
vImage_Buffer *image;
+ CGGI_GlyphRenderingStage stage;
}
@end;
@implementation CGGI_GlyphCanvas
+- (id) init {
+ if (self = [super init]) {
+ context = NULL;
+ image = NULL;
+ stage = BLACK_ON_WHITE_STAGE;
+ }
+ return self;
+}
@end
#pragma mark --- Debugging Helpers ---
@@ -197,19 +211,13 @@
#pragma mark --- Font Rendering Mode Descriptors ---
static inline void
CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
{
-#if __LITTLE_ENDIAN__
- *(dst + 2) = 0xFF - (p >> 24 & 0xFF);
- *(dst + 1) = 0xFF - (p >> 16 & 0xFF);
- *(dst) = 0xFF - (p >> 8 & 0xFF);
-#else
- *(dst) = 0xFF - (p >> 16 & 0xFF);
- *(dst + 1) = 0xFF - (p >> 8 & 0xFF);
- *(dst + 2) = 0xFF - (p & 0xFF);
-#endif
+ *(dst + 0) = 0xFF - (p >> 16 & 0xFF); // red
+ *(dst + 1) = 0xFF - (p >> 8 & 0xFF); // green
+ *(dst + 2) = 0xFF - (p & 0xFF); // blue
}
static void
CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
{
@@ -220,25 +228,46 @@
size_t destRowWidth = info->width;
size_t height = info->height;
size_t y;
+ switch (canvas->stage) {
+ case BLACK_ON_WHITE_STAGE:
+ // fill empty glyph image with black-on-white glyph
for (y = 0; y < height; y++) {
size_t destRow = y * destRowWidth * 3;
size_t srcRow = y * srcRowWidth;
size_t x;
for (x = 0; x < destRowWidth; x++) {
- // size_t x3 = x * 3;
- // UInt32 p = src[srcRow + x];
- // dest[destRow + x3] = 0xFF - (p >> 16 & 0xFF);
- // dest[destRow + x3 + 1] = 0xFF - (p >> 8 & 0xFF);
- // dest[destRow + x3 + 2] = 0xFF - (p & 0xFF);
CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
dest + destRow + x * 3);
}
}
+ break;
+ case WHITE_ON_BLACK_STAGE:
+ // merge black-on-white glyph (which is already in the glyph image)
+ // with white-on-black glyph
+ for (y = 0; y < height; y++) {
+ size_t destRow = y * destRowWidth * 3;
+ size_t srcRow = y * srcRowWidth;
+
+ size_t x;
+ for (x = 0; x < destRowWidth; x++) {
+ UInt8* pDst = dest + destRow + x * 3;
+ UInt32 srcPixel = src[srcRow + x];
+
+ UInt16 r = *(pDst + 0) + (0xff & (srcPixel >> 16));
+ *(pDst + 0) = (UInt8)(r >> 1);
+ UInt16 g = *(pDst + 1) + (0xff & (srcPixel >> 8));
+ *(pDst + 1) = (UInt8)(g >> 1);
+ UInt16 b = *(pDst + 2) + (0xff & (srcPixel ));
+ *(pDst + 2) = (UInt8)(b >> 1);
+ }
+ }
+ break;
+ }
}
//static void CGGI_copyImageFromCanvasToAlphaInfo
//(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
//{
@@ -258,17 +287,18 @@
// vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
// &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
//}
static inline UInt8
-CGGI_ConvertPixelToGreyBit(UInt32 p)
+CGGI_ConvertWBPixelToByteGray(UInt32 p) {
+ return ((p >> 16 & 0xFF) + (p >> 8 & 0xFF) + (p & 0xFF)) / 3;
+}
+
+static inline UInt8
+CGGI_ConvertBWPixelToByteGray(UInt32 p)
{
-#ifdef __LITTLE_ENDIAN__
- return 0xFF - ((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3;
-#else
- return 0xFF - ((p >> 16 & 0xFF) + (p >> 8 & 0xFF) + (p & 0xFF)) / 3;
-#endif
+ return 0xFF - CGGI_ConvertWBPixelToByteGray(p);
}
static void
CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
{
@@ -279,19 +309,38 @@
size_t destRowWidth = info->width;
size_t height = info->height;
size_t y;
+ switch (canvas->stage) {
+ case BLACK_ON_WHITE_STAGE:
+ // fill empty glyph image with black-on-white glyph
+ for (y = 0; y < height; y++) {
+ size_t destRow = y * destRowWidth;
+ size_t srcRow = y * srcRowWidth;
+ size_t x;
+ for (x = 0; x < destRowWidth; x++) {
+ UInt32 p = src[srcRow + x];
+ dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
+ }
+ }
+ break;
+ case WHITE_ON_BLACK_STAGE:
+ // merge black-on-white glyph (which is already in the glyph image)
+ // with white-on-black glyph
for (y = 0; y < height; y++) {
size_t destRow = y * destRowWidth;
size_t srcRow = y * srcRowWidth;
size_t x;
for (x = 0; x < destRowWidth; x++) {
UInt32 p = src[srcRow + x];
- dest[destRow + x] = CGGI_ConvertPixelToGreyBit(p);
+ UInt16 gray = dest[destRow + x] + CGGI_ConvertWBPixelToByteGray(p);
+ dest[destRow + x] = (UInt8)(gray >> 1);
+ }
}
+ break;
}
}
#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
@@ -314,25 +363,31 @@
static inline CGGI_RenderingMode
CGGI_GetRenderingMode(const AWTStrike *strike)
{
CGGI_RenderingMode mode;
mode.cgFontMode = strike->fStyle;
+ NSException *e = nil;
switch (strike->fAAStyle) {
- case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
- case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
- default:
mode.glyphDescriptor = &grey;
break;
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
mode.glyphDescriptor = &rgb;
break;
+ case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
+ case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
+ default:
+ e = [NSException
+ exceptionWithName:@"IllegalArgumentException"
+ reason:@"Invalid hint value"
+ userInfo:nil];
+ @throw e;
}
return mode;
}
@@ -343,34 +398,44 @@
* Creates a new canvas of a fixed size, and initializes the CGContext as
* an 32-bit ARGB BitmapContext with some generic RGB color space.
*/
static inline void
CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
- const vImagePixelCount width, const vImagePixelCount height)
+ const vImagePixelCount width, const vImagePixelCount height,
+ const CGGI_RenderingMode* mode)
{
// our canvas is *always* 4-byte ARGB
size_t bytesPerRow = width * sizeof(UInt32);
size_t byteCount = bytesPerRow * height;
canvas->image = malloc(sizeof(vImage_Buffer));
canvas->image->width = width;
canvas->image->height = height;
canvas->image->rowBytes = bytesPerRow;
- canvas->image->data = (void *)calloc(byteCount, sizeof(UInt32));
+ canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
if (canvas->image->data == NULL) {
[[NSException exceptionWithName:NSMallocException
reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
}
+ canvas->stage = BLACK_ON_WHITE_STAGE;
+
+ uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
+ if (mode->glyphDescriptor == &rgb) {
+ bmpInfo |= kCGBitmapByteOrder32Host;
+ }
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
canvas->context = CGBitmapContextCreate(canvas->image->data,
width, height, 8, bytesPerRow,
colorSpace,
- kCGImageAlphaPremultipliedFirst);
+ bmpInfo);
+ // set foreground color
CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
+
CGContextSetFontSize(canvas->context, 1);
CGContextSaveGState(canvas->context);
CGColorSpaceRelease(colorSpace);
}
@@ -402,11 +467,13 @@
/*
* Quick and easy inline to check if this canvas is big enough.
*/
static inline void
-CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vImagePixelCount height, const JRSFontRenderingStyle style)
+CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
+ const vImagePixelCount height,
+ const CGGI_RenderingMode* mode)
{
if (canvas->image != NULL &&
width < canvas->image->width &&
height < canvas->image->height)
{
@@ -416,12 +483,13 @@
// if we don't have enough space to strike the largest glyph in the
// run, resize the canvas
CGGI_FreeCanvas(canvas);
CGGI_InitCanvas(canvas,
width * CGGI_GLYPH_CANVAS_SLACK,
- height * CGGI_GLYPH_CANVAS_SLACK);
- JRSFontSetRenderingStyleOnContext(canvas->context, style);
+ height * CGGI_GLYPH_CANVAS_SLACK,
+ mode);
+ JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
}
/*
* Clear the canvas by blitting white only into the region of interest
* (the rect which we will copy out of once the glyph is struck).
@@ -437,15 +505,28 @@
canvasRectToClear.rowBytes = canvas->image->rowBytes;
// clean the canvas
#ifdef CGGI_DEBUG
Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
+ Pixel_8888 opaqueBlack = { 0x10, 0x10, 0x10, 0xF0 };
#else
Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
+ Pixel_8888 opaqueBlack = { 0x00, 0x00, 0x00, 0xFF };
#endif
+ // clear canvas background and set foreground color
+ switch(canvas->stage) {
+ case BLACK_ON_WHITE_STAGE:
vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
+ CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
+ break;
+ case WHITE_ON_BLACK_STAGE:
+ vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueBlack, kvImageNoFlags);
+ CGContextSetRGBFillColor(canvas->context, 1.0f, 1.0f, 1.0f, 1.0f);
+ break;
+ }
+ CGContextSaveGState(canvas->context);
}
#pragma mark --- GlyphInfo Creation & Copy Functions ---
@@ -575,11 +656,11 @@
// create the Sun2D GlyphInfo we are going to strike into
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
// fix the context size, just in case the substituted character is unexpectedly large
- CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode);
+ CGGI_SizeCanvas(canvas, info->width, info->height, mode);
// align the transform for the real CoreText strike
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
@@ -651,12 +732,15 @@
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
PRINT_CGSTATES_INFO(canvas->context);
#endif
}
-static NSString *threadLocalCanvasKey =
- @"Java CoreGraphics Text Renderer Cached Canvas";
+static NSString *threadLocalAACanvasKey =
+ @"Java CoreGraphics Text Renderer Cached Canvas for AA";
+
+static NSString *threadLocalLCDCanvasKey =
+ @"Java CoreGraphics Text Renderer Cached Canvas for LCD";
/*
* This is the maximum length and height times the above slack squared
* to determine if we go with the global canvas, or malloc one on the spot.
*/
@@ -676,31 +760,49 @@
{
if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
{
CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
- CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight);
+ CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
+ // create black-on-white glyph image
+ CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
+ mode, glyphInfos, uniChars,
+ glyphs, len);
+
+ // merge glyph image with white-on-black glyph
+ tmpCanvas->stage = WHITE_ON_BLACK_STAGE;
CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
mode, glyphInfos, uniChars,
glyphs, len);
CGGI_FreeCanvas(tmpCanvas);
[tmpCanvas release];
return;
}
-
NSMutableDictionary *threadDict =
[[NSThread currentThread] threadDictionary];
- CGGI_GlyphCanvas *canvas = [threadDict objectForKey:threadLocalCanvasKey];
+
+ NSString* theKey = (mode->glyphDescriptor == &rgb) ?
+ threadLocalLCDCanvasKey : threadLocalAACanvasKey;
+
+ CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
if (canvas == nil) {
canvas = [[CGGI_GlyphCanvas alloc] init];
- [threadDict setObject:canvas forKey:threadLocalCanvasKey];
+ [threadDict setObject:canvas forKey:theKey];
}
- CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode->cgFontMode);
+ CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
+ // create black-on-white glyph image
+ canvas->stage = BLACK_ON_WHITE_STAGE;
CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
glyphInfos, uniChars, glyphs, len);
+
+ // merge glyph image with white-on-black glyph
+ canvas->stage = WHITE_ON_BLACK_STAGE;
+ CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
+ glyphInfos, uniChars, glyphs, len);
+
}
/*
* Finds the advances and bounding boxes of the characters in the run,
* cycles through all the bounds and calculates the maximum canvas space
< prev index next >