提交 aa627fd2 编写于 作者: B bae

8023794: [macosx] LCD Rendering hints seems not working without FRACTIONALMETRICS=ON

Reviewed-by: serb, prr
上级 4cffa7cc
...@@ -366,8 +366,7 @@ public final class LWCToolkit extends LWToolkit { ...@@ -366,8 +366,7 @@ public final class LWCToolkit extends LWToolkit {
protected void initializeDesktopProperties() { protected void initializeDesktopProperties() {
super.initializeDesktopProperties(); super.initializeDesktopProperties();
Map <Object, Object> fontHints = new HashMap<>(); Map <Object, Object> fontHints = new HashMap<>();
fontHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); fontHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
fontHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
desktopProperties.put(SunToolkit.DESKTOPFONTHINTS, fontHints); desktopProperties.put(SunToolkit.DESKTOPFONTHINTS, fontHints);
desktopProperties.put("awt.mouse.numButtons", BUTTONS); desktopProperties.put("awt.mouse.numButtons", BUTTONS);
......
...@@ -311,22 +311,27 @@ JNF_COCOA_ENTER(env); ...@@ -311,22 +311,27 @@ JNF_COCOA_ENTER(env);
jlong *glyphInfos = jlong *glyphInfos =
(*env)->GetPrimitiveArrayCritical(env, glyphInfoLongArray, NULL); (*env)->GetPrimitiveArrayCritical(env, glyphInfoLongArray, NULL);
if (glyphInfos != NULL) {
jint *rawGlyphCodes = jint *rawGlyphCodes =
(*env)->GetPrimitiveArrayCritical(env, glyphCodes, NULL); (*env)->GetPrimitiveArrayCritical(env, glyphCodes, NULL);
@try {
if (rawGlyphCodes != NULL) { if (rawGlyphCodes != NULL && glyphInfos != NULL) {
CGGlyphImages_GetGlyphImagePtrs(glyphInfos, awtStrike, CGGlyphImages_GetGlyphImagePtrs(glyphInfos, awtStrike,
rawGlyphCodes, len); rawGlyphCodes, len);
}
}
@finally {
if (rawGlyphCodes != NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, glyphCodes, (*env)->ReleasePrimitiveArrayCritical(env, glyphCodes,
rawGlyphCodes, JNI_ABORT); rawGlyphCodes, JNI_ABORT);
} }
if (glyphInfos != NULL) {
// Do not use JNI_COMMIT, as that will not free the buffer copy // Do not use JNI_COMMIT, as that will not free the buffer copy
// when +ProtectJavaHeap is on. // when +ProtectJavaHeap is on.
(*env)->ReleasePrimitiveArrayCritical(env, glyphInfoLongArray, (*env)->ReleasePrimitiveArrayCritical(env, glyphInfoLongArray,
glyphInfos, 0); glyphInfos, 0);
} }
}
JNF_COCOA_EXIT(env); JNF_COCOA_EXIT(env);
} }
......
...@@ -195,19 +195,41 @@ DUMP_GLYPHINFO(const GlyphInfo *info) ...@@ -195,19 +195,41 @@ DUMP_GLYPHINFO(const GlyphInfo *info)
#pragma mark --- Font Rendering Mode Descriptors --- #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;
int i;
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 (i = 0; i < 256; i++) {
double x = ((double)i) / 255.0;
reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma));
}
}
return reverseGammaLut;
}
static inline void static inline void
CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst) CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
{ {
#if __LITTLE_ENDIAN__ UInt8* lut = getReverseGammaLut();
*(dst + 2) = 0xFF - (p >> 24 & 0xFF);
*(dst + 1) = 0xFF - (p >> 16 & 0xFF); *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)]; // red
*(dst) = 0xFF - (p >> 8 & 0xFF); *(dst + 1) = lut[0xFF - (p >> 8 & 0xFF)]; // green
#else *(dst + 2) = lut[0xFF - (p & 0xFF)]; // blue
*(dst) = 0xFF - (p >> 16 & 0xFF);
*(dst + 1) = 0xFF - (p >> 8 & 0xFF);
*(dst + 2) = 0xFF - (p & 0xFF);
#endif
} }
static void static void
...@@ -222,17 +244,14 @@ CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) ...@@ -222,17 +244,14 @@ CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
size_t height = info->height; size_t height = info->height;
size_t y; size_t y;
// fill empty glyph image with black-on-white glyph
for (y = 0; y < height; y++) { for (y = 0; y < height; y++) {
size_t destRow = y * destRowWidth * 3; size_t destRow = y * destRowWidth * 3;
size_t srcRow = y * srcRowWidth; size_t srcRow = y * srcRowWidth;
size_t x; size_t x;
for (x = 0; x < destRowWidth; 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], CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
dest + destRow + x * 3); dest + destRow + x * 3);
} }
...@@ -260,13 +279,9 @@ CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) ...@@ -260,13 +279,9 @@ CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
//} //}
static inline UInt8 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);
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
} }
static void static void
...@@ -281,14 +296,15 @@ CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) ...@@ -281,14 +296,15 @@ CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
size_t height = info->height; size_t height = info->height;
size_t y; size_t y;
// fill empty glyph image with black-on-white glyph
for (y = 0; y < height; y++) { for (y = 0; y < height; y++) {
size_t destRow = y * destRowWidth; size_t destRow = y * destRowWidth;
size_t srcRow = y * srcRowWidth; size_t srcRow = y * srcRowWidth;
size_t x; size_t x;
for (x = 0; x < destRowWidth; x++) { for (x = 0; x < destRowWidth; x++) {
UInt32 p = src[srcRow + x]; UInt32 p = src[srcRow + x];
dest[destRow + x] = CGGI_ConvertPixelToGreyBit(p); dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
} }
} }
} }
...@@ -316,13 +332,11 @@ CGGI_GetRenderingMode(const AWTStrike *strike) ...@@ -316,13 +332,11 @@ CGGI_GetRenderingMode(const AWTStrike *strike)
{ {
CGGI_RenderingMode mode; CGGI_RenderingMode mode;
mode.cgFontMode = strike->fStyle; mode.cgFontMode = strike->fStyle;
NSException *e = nil;
switch (strike->fAAStyle) { 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_OFF:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
default:
mode.glyphDescriptor = &grey; mode.glyphDescriptor = &grey;
break; break;
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
...@@ -331,6 +345,17 @@ CGGI_GetRenderingMode(const AWTStrike *strike) ...@@ -331,6 +345,17 @@ CGGI_GetRenderingMode(const AWTStrike *strike)
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
mode.glyphDescriptor = &rgb; mode.glyphDescriptor = &rgb;
break; 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; return mode;
...@@ -345,7 +370,8 @@ CGGI_GetRenderingMode(const AWTStrike *strike) ...@@ -345,7 +370,8 @@ CGGI_GetRenderingMode(const AWTStrike *strike)
*/ */
static inline void static inline void
CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, 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 // our canvas is *always* 4-byte ARGB
size_t bytesPerRow = width * sizeof(UInt32); size_t bytesPerRow = width * sizeof(UInt32);
...@@ -356,19 +382,26 @@ CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, ...@@ -356,19 +382,26 @@ CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
canvas->image->height = height; canvas->image->height = height;
canvas->image->rowBytes = bytesPerRow; 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) { if (canvas->image->data == NULL) {
[[NSException exceptionWithName:NSMallocException [[NSException exceptionWithName:NSMallocException
reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise]; 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, canvas->context = CGBitmapContextCreate(canvas->image->data,
width, height, 8, bytesPerRow, width, height, 8, bytesPerRow,
colorSpace, colorSpace,
kCGImageAlphaPremultipliedFirst); bmpInfo);
// set foreground color
CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f); CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
CGContextSetFontSize(canvas->context, 1); CGContextSetFontSize(canvas->context, 1);
CGContextSaveGState(canvas->context); CGContextSaveGState(canvas->context);
...@@ -404,7 +437,9 @@ CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas) ...@@ -404,7 +437,9 @@ CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
* Quick and easy inline to check if this canvas is big enough. * Quick and easy inline to check if this canvas is big enough.
*/ */
static inline void 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 && if (canvas->image != NULL &&
width < canvas->image->width && width < canvas->image->width &&
...@@ -418,8 +453,9 @@ CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vI ...@@ -418,8 +453,9 @@ CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vI
CGGI_FreeCanvas(canvas); CGGI_FreeCanvas(canvas);
CGGI_InitCanvas(canvas, CGGI_InitCanvas(canvas,
width * CGGI_GLYPH_CANVAS_SLACK, width * CGGI_GLYPH_CANVAS_SLACK,
height * CGGI_GLYPH_CANVAS_SLACK); height * CGGI_GLYPH_CANVAS_SLACK,
JRSFontSetRenderingStyleOnContext(canvas->context, style); mode);
JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
} }
/* /*
...@@ -443,6 +479,7 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info) ...@@ -443,6 +479,7 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF }; Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
#endif #endif
// clear canvas background and set foreground color
vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags); vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
} }
...@@ -577,7 +614,7 @@ CGGI_CreateImageForUnicode ...@@ -577,7 +614,7 @@ CGGI_CreateImageForUnicode
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode); GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
// fix the context size, just in case the substituted character is unexpectedly large // 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 // align the transform for the real CoreText strike
CGContextSetTextMatrix(canvas->context, strike->fAltTx); CGContextSetTextMatrix(canvas->context, strike->fAltTx);
...@@ -653,8 +690,11 @@ CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas, ...@@ -653,8 +690,11 @@ CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
#endif #endif
} }
static NSString *threadLocalCanvasKey = static NSString *threadLocalAACanvasKey =
@"Java CoreGraphics Text Renderer Cached Canvas"; @"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 * This is the maximum length and height times the above slack squared
...@@ -678,7 +718,7 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike, ...@@ -678,7 +718,7 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*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_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight); CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike, CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
mode, glyphInfos, uniChars, mode, glyphInfos, uniChars,
glyphs, len); glyphs, len);
...@@ -687,16 +727,19 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike, ...@@ -687,16 +727,19 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
[tmpCanvas release]; [tmpCanvas release];
return; return;
} }
NSMutableDictionary *threadDict = NSMutableDictionary *threadDict =
[[NSThread currentThread] threadDictionary]; [[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) { if (canvas == nil) {
canvas = [[CGGI_GlyphCanvas alloc] init]; 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, CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
glyphInfos, uniChars, glyphs, len); glyphInfos, uniChars, glyphs, len);
} }
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package sun.java2d.opengl; package sun.java2d.opengl;
import java.awt.AlphaComposite; import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.GraphicsEnvironment; import java.awt.GraphicsEnvironment;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Transparency; import java.awt.Transparency;
...@@ -400,8 +401,8 @@ public abstract class OGLSurfaceData extends SurfaceData ...@@ -400,8 +401,8 @@ public abstract class OGLSurfaceData extends SurfaceData
/** /**
* For now, we can only render LCD text if: * For now, we can only render LCD text if:
* - the fragment shader extension is available, and * - the fragment shader extension is available, and
* - blending is disabled, and * - the source color is opaque, and
* - the source color is opaque * - blending is SrcOverNoEa or disabled
* - and the destination is opaque * - and the destination is opaque
* *
* Eventually, we could enhance the native OGL text rendering code * Eventually, we could enhance the native OGL text rendering code
...@@ -411,9 +412,19 @@ public abstract class OGLSurfaceData extends SurfaceData ...@@ -411,9 +412,19 @@ public abstract class OGLSurfaceData extends SurfaceData
public boolean canRenderLCDText(SunGraphics2D sg2d) { public boolean canRenderLCDText(SunGraphics2D sg2d) {
return return
graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) && graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) &&
sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR && sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR &&
sg2d.surfaceData.getTransparency() == Transparency.OPAQUE; (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY ||
(sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite)));
}
private boolean canHandleComposite(Composite c) {
if (c instanceof AlphaComposite) {
AlphaComposite ac = (AlphaComposite)c;
return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f;
}
return false;
} }
public void validatePipe(SunGraphics2D sg2d) { public void validatePipe(SunGraphics2D sg2d) {
......
...@@ -748,7 +748,7 @@ OGLContext_IsLCDShaderSupportAvailable(JNIEnv *env, ...@@ -748,7 +748,7 @@ OGLContext_IsLCDShaderSupportAvailable(JNIEnv *env,
// finally, check to see if the hardware supports the required number // finally, check to see if the hardware supports the required number
// of texture units // of texture units
j2d_glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &maxTexUnits); j2d_glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &maxTexUnits);
if (maxTexUnits < 4) { if (maxTexUnits < 2) {
J2dRlsTraceLn1(J2D_TRACE_INFO, J2dRlsTraceLn1(J2D_TRACE_INFO,
"OGLContext_IsLCDShaderSupportAvailable: not enough tex units (%d)", "OGLContext_IsLCDShaderSupportAvailable: not enough tex units (%d)",
maxTexUnits); maxTexUnits);
......
...@@ -275,12 +275,9 @@ OGLTR_AddToGlyphCache(GlyphInfo *glyph, jboolean rgbOrder) ...@@ -275,12 +275,9 @@ OGLTR_AddToGlyphCache(GlyphInfo *glyph, jboolean rgbOrder)
* changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()). * changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()).
* *
* The "main" function is executed for each "fragment" (or pixel) in the * The "main" function is executed for each "fragment" (or pixel) in the
* glyph image. We have determined that the pow() function can be quite * glyph image. The pow() routine operates on vectors, gives precise results,
* slow and it only operates on scalar values, not vectors as we require. * and provides acceptable level of performance, so we use it to perform
* So instead we build two 3D textures containing gamma (and inverse gamma) * the gamma adjustment.
* lookup tables that allow us to approximate a component-wise pow() function
* with a single 3D texture lookup. This approach is at least 2x faster
* than the equivalent pow() calls.
* *
* The variables involved in the equation can be expressed as follows: * The variables involved in the equation can be expressed as follows:
* *
...@@ -299,8 +296,8 @@ static const char *lcdTextShaderSource = ...@@ -299,8 +296,8 @@ static const char *lcdTextShaderSource =
"uniform vec3 src_adj;" "uniform vec3 src_adj;"
"uniform sampler2D glyph_tex;" "uniform sampler2D glyph_tex;"
"uniform sampler2D dst_tex;" "uniform sampler2D dst_tex;"
"uniform sampler3D invgamma_tex;" "uniform vec3 gamma;"
"uniform sampler3D gamma_tex;" "uniform vec3 invgamma;"
"" ""
"void main(void)" "void main(void)"
"{" "{"
...@@ -312,12 +309,12 @@ static const char *lcdTextShaderSource = ...@@ -312,12 +309,12 @@ static const char *lcdTextShaderSource =
" }" " }"
// load the RGB value from the corresponding destination pixel // load the RGB value from the corresponding destination pixel
" vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));"
// gamma adjust the dest color using the invgamma LUT // gamma adjust the dest color
" vec3 dst_adj = vec3(texture3D(invgamma_tex, dst_clr.stp));" " vec3 dst_adj = pow(dst_clr.rgb, gamma);"
// linearly interpolate the three color values // linearly interpolate the three color values
" vec3 result = mix(dst_adj, src_adj, glyph_clr);" " vec3 result = mix(dst_adj, src_adj, glyph_clr);"
// gamma re-adjust the resulting color (alpha is always set to 1.0) // gamma re-adjust the resulting color (alpha is always set to 1.0)
" gl_FragColor = vec4(vec3(texture3D(gamma_tex, result.stp)), 1.0);" " gl_FragColor = vec4(pow(result.rgb, invgamma), 1.0);"
"}"; "}";
/** /**
...@@ -348,10 +345,6 @@ OGLTR_CreateLCDTextProgram() ...@@ -348,10 +345,6 @@ OGLTR_CreateLCDTextProgram()
j2d_glUniform1iARB(loc, 0); // texture unit 0 j2d_glUniform1iARB(loc, 0); // texture unit 0
loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex"); loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex");
j2d_glUniform1iARB(loc, 1); // texture unit 1 j2d_glUniform1iARB(loc, 1); // texture unit 1
loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma_tex");
j2d_glUniform1iARB(loc, 2); // texture unit 2
loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma_tex");
j2d_glUniform1iARB(loc, 3); // texture unit 3
// "unuse" the program object; it will be re-bound later as needed // "unuse" the program object; it will be re-bound later as needed
j2d_glUseProgramObjectARB(0); j2d_glUseProgramObjectARB(0);
...@@ -360,108 +353,26 @@ OGLTR_CreateLCDTextProgram() ...@@ -360,108 +353,26 @@ OGLTR_CreateLCDTextProgram()
} }
/** /**
* Initializes a 3D texture object for use as a three-dimensional gamma * (Re)Initializes the gamma related uniforms.
* lookup table. Note that the wrap mode is initialized to GL_LINEAR so
* that the table will interpolate adjacent values when the index falls
* somewhere in between.
*/
static GLuint
OGLTR_InitGammaLutTexture()
{
GLuint lutTextureID;
j2d_glGenTextures(1, &lutTextureID);
j2d_glBindTexture(GL_TEXTURE_3D, lutTextureID);
j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return lutTextureID;
}
/**
* Updates the lookup table in the given texture object with the float
* values in the given system memory buffer. Note that we could use
* glTexSubImage3D() when updating the texture after its first
* initialization, but since we're updating the entire table (with
* power-of-two dimensions) and this is a relatively rare event, we'll
* just stick with glTexImage3D().
*/
static void
OGLTR_UpdateGammaLutTexture(GLuint texID, GLfloat *lut, jint size)
{
j2d_glBindTexture(GL_TEXTURE_3D, texID);
j2d_glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB8,
size, size, size, 0, GL_RGB, GL_FLOAT, lut);
}
/**
* (Re)Initializes the gamma lookup table textures.
* *
* The given contrast value is an int in the range [100, 250] which we will * The given contrast value is an int in the range [100, 250] which we will
* then scale to fit in the range [1.0, 2.5]. We create two LUTs, one * then scale to fit in the range [1.0, 2.5].
* that essentially calculates pow(x, gamma) and the other calculates
* pow(x, 1/gamma). These values are replicated in all three dimensions, so
* given a single 3D texture coordinate (typically this will be a triplet
* in the form (r,g,b)), the 3D texture lookup will return an RGB triplet:
*
* (pow(r,g), pow(y,g), pow(z,g)
*
* where g is either gamma or 1/gamma, depending on the table.
*/ */
static jboolean static jboolean
OGLTR_UpdateLCDTextContrast(jint contrast) OGLTR_UpdateLCDTextContrast(jint contrast)
{ {
double gamma = ((double)contrast) / 100.0; double g = ((double)contrast) / 100.0;
double ig = gamma; double ig = 1.0 / g;
double g = 1.0 / ig; GLint loc;
GLfloat lut[LUT_EDGE][LUT_EDGE][LUT_EDGE][3];
GLfloat invlut[LUT_EDGE][LUT_EDGE][LUT_EDGE][3];
int min = 0;
int max = LUT_EDGE - 1;
int x, y, z;
J2dTraceLn1(J2D_TRACE_INFO, J2dTraceLn1(J2D_TRACE_INFO,
"OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast);
for (z = min; z <= max; z++) { loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma");
double zval = ((double)z) / max; j2d_glUniform3fARB(loc, g, g, g);
GLfloat gz = (GLfloat)pow(zval, g);
GLfloat igz = (GLfloat)pow(zval, ig);
for (y = min; y <= max; y++) {
double yval = ((double)y) / max;
GLfloat gy = (GLfloat)pow(yval, g);
GLfloat igy = (GLfloat)pow(yval, ig);
for (x = min; x <= max; x++) { loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma");
double xval = ((double)x) / max; j2d_glUniform3fARB(loc, ig, ig, ig);
GLfloat gx = (GLfloat)pow(xval, g);
GLfloat igx = (GLfloat)pow(xval, ig);
lut[z][y][x][0] = gx;
lut[z][y][x][1] = gy;
lut[z][y][x][2] = gz;
invlut[z][y][x][0] = igx;
invlut[z][y][x][1] = igy;
invlut[z][y][x][2] = igz;
}
}
}
if (gammaLutTextureID == 0) {
gammaLutTextureID = OGLTR_InitGammaLutTexture();
}
OGLTR_UpdateGammaLutTexture(gammaLutTextureID, (GLfloat *)lut, LUT_EDGE);
if (invGammaLutTextureID == 0) {
invGammaLutTextureID = OGLTR_InitGammaLutTexture();
}
OGLTR_UpdateGammaLutTexture(invGammaLutTextureID,
(GLfloat *)invlut, LUT_EDGE);
return JNI_TRUE; return JNI_TRUE;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册