--- old/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m 2015-05-21 16:57:10.000000000 +0300 +++ new/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m 2015-05-21 16:57:10.000000000 +0300 @@ -195,19 +195,40 @@ #pragma mark --- Font Rendering Mode Descriptors --- +static Int32 reverseGamma = 0; + +static UInt8 reverseGammaLut[256] = { 0 }; + +static inline UInt8* getReverseGammaLut() { + if (reverseGamma == 0) { + // initialize gamma lut + double gamma; + const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA"); + if (pGammaEnv != NULL) { + reverseGamma = atol(pGammaEnv); + } + + if (reverseGamma < 100 || reverseGamma > 250) { + reverseGamma = 180; + } + + gamma = 100.0 / reverseGamma; + for (int i = 0; i < 256; i++) { + double x = ((double)i) / 255.0; + reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma)); + } + } + return reverseGammaLut; +} 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 + UInt8* lut = getReverseGammaLut(); + + *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)]; // red + *(dst + 1) = lut[0xFF - (p >> 8 & 0xFF)]; // green + *(dst + 2) = lut[0xFF - (p & 0xFF)]; // blue } static void @@ -222,17 +243,14 @@ size_t height = info->height; size_t y; + + // 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); } @@ -260,13 +278,9 @@ //} static inline UInt8 -CGGI_ConvertPixelToGreyBit(UInt32 p) +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 - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3); } static void @@ -281,14 +295,15 @@ size_t height = info->height; size_t y; + + // 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_ConvertPixelToGreyBit(p); + dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p); } } } @@ -316,13 +331,11 @@ { 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: @@ -331,6 +344,17 @@ 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: + /* we expect that text antialiasing hint has been already + * evaluated. Report an error if we get 'unevaluated' hint here. + */ + e = [NSException + exceptionWithName:@"IllegalArgumentException" + reason:@"Invalid hint value" + userInfo:nil]; + @throw e; } return mode; @@ -345,7 +369,8 @@ */ 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); @@ -356,19 +381,26 @@ 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]; } - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst; + if (mode->glyphDescriptor == &rgb) { + bmpInfo |= kCGBitmapByteOrder32Host; + } + + CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 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); @@ -404,7 +436,9 @@ * 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 && @@ -418,8 +452,9 @@ 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); } /* @@ -443,6 +478,7 @@ Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF }; #endif + // clear canvas background and set foreground color vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags); } @@ -577,7 +613,7 @@ 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); @@ -653,8 +689,11 @@ #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 @@ -678,25 +717,28 @@ 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); CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike, - mode, glyphInfos, uniChars, - glyphs, len); + 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); CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode, glyphInfos, uniChars, glyphs, len); }