From d8a9899adbff364a83b28c44b6872952eeafcf4f Mon Sep 17 00:00:00 2001 From: Alexey Ushakov Date: Tue, 30 Jun 2020 15:37:08 +0300 Subject: [PATCH] JBR-2463 Font rendering problem on macOS Mojave Use adjusted advances for glyphs (cherry picked from commit 1af5dd4aaedc18ad36a1366a3e179ec0b9ea043c) --- .../native/libawt_lwawt/awt/LWCToolkit.h | 2 + .../native/libawt_lwawt/awt/LWCToolkit.m | 1 + .../java2d/opengl/CGLGraphicsConfig.m | 27 ++- .../common/java2d/opengl/OGLTextRenderer.c | 179 +++++++++++++++++- 4 files changed, 198 insertions(+), 11 deletions(-) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h index 399f56aeb6c4..839ac6e139e8 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h @@ -39,6 +39,8 @@ extern int gNumberOfButtons; // InputEvent mask array extern jint* gButtonDownMasks; +extern int useFontSmoothing; + @interface AWTToolkit : NSObject { } + (BOOL) inDoDragDropLoop; + (void) setInDoDragDropLoop:(BOOL)val; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m index bba7b8fe4b7c..f0be68a9bfff 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m @@ -52,6 +52,7 @@ int gNumberOfButtons; jint* gButtonDownMasks; +int useFontSmoothing; // Indicates that the app has been started with -XstartOnFirstThread // (directly or via WebStart settings), and AWT should not run its diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/opengl/CGLGraphicsConfig.m b/src/java.desktop/macosx/native/libawt_lwawt/java2d/opengl/CGLGraphicsConfig.m index 07ead78aa419..cd8a6fe91bcf 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/opengl/CGLGraphicsConfig.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/opengl/CGLGraphicsConfig.m @@ -228,19 +228,32 @@ Java_sun_java2d_opengl_CGLGraphicsConfig_getCGLConfigInfo Boolean status = false; Boolean fontSmoothingDisabled = CFPreferencesGetAppBooleanValue( - CFSTR("CGFontRenderingFontSmoothingDisabled"), - kCFPreferencesCurrentApplication, &status); - + CFSTR("CGFontRenderingFontSmoothingDisabled"), + kCFPreferencesCurrentApplication, &status); + useFontSmoothing = NO; if (status) { if (fontSmoothingDisabled) { J2dRlsTraceLn(J2D_TRACE_INFO, - "LCD_SHADER: disabled via macOS settings"); + "LCD_SHADER: disabled via macOS settings"); caps &= ~CAPS_EXT_LCD_SHADER; } } else if (IS_OSX_GT10_13) { - J2dRlsTraceLn(J2D_TRACE_INFO, - "LCD_SHADER: disabled on macOS 10.14+ by default"); - caps &= ~CAPS_EXT_LCD_SHADER; + // Adding support for "Use font smoothing when available" setting + Boolean status = false; + Boolean smoothFonts = CFPreferencesGetAppBooleanValue( + CFSTR("AppleFontSmoothing"), + kCFPreferencesCurrentApplication, + &status); + + if (!status) { + smoothFonts = YES; + } + useFontSmoothing = smoothFonts; + if (!smoothFonts) { + J2dRlsTraceLn(J2D_TRACE_INFO, + "LCD_SHADER: disabled on macOS 10.14+ by default"); + caps &= ~CAPS_EXT_LCD_SHADER; + } } GLint value = 0; diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c b/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c index 0abe292fcec9..f52f2636f021 100644 --- a/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c +++ b/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c @@ -86,6 +86,16 @@ static GlyphCacheInfo *glyphCacheAA = NULL; */ static GLhandleARB lcdTextProgram = 0; +/** + * The handle to the Gray text fragment program object. + */ +static GLhandleARB grayTextProgram = 0; + +/** + * Use this gamma if gray gamma shader is enabled + */ +#define GRAY_GAMMA 220 + /** * This value tracks the previous LCD contrast setting, so if the contrast * value hasn't changed since the last time the gamma uniforms were @@ -299,6 +309,28 @@ static const char *lcdTextShaderSource = " gl_FragColor = vec4(pow(result.rgb, invgamma), 1.0);" "}"; +static const char *grayGammaTextShaderSource = + "uniform vec3 src_adj;" + "uniform sampler2D glyph_tex;" + "uniform float invgamma;" + "void main(void)" + "{" + " float glyph_clr = float(texture2D(glyph_tex, gl_TexCoord[0].st));" + " if (dot(src_adj, vec3(1.0/3.0, 1.0/3.0, 1.0/3.0)) > 0.5) {" + " glyph_clr = pow(glyph_clr, invgamma);" + " }" + " gl_FragColor = vec4(src_adj, glyph_clr);" + "}"; + +static const char *grayTextShaderSource = + "uniform vec3 src_adj;" + "uniform sampler2D glyph_tex;" + "void main(void)" + "{" + " float glyph_clr = float(texture2D(glyph_tex, gl_TexCoord[0].st));" + " gl_FragColor = vec4(src_adj, glyph_clr);" + "}"; + /** * Compiles and links the LCD text shader program. If successful, this * function returns a handle to the newly created shader program; otherwise @@ -333,6 +365,45 @@ OGLTR_CreateLCDTextProgram() return lcdTextProgram; } +/** + * Compiles and links the LCD text shader program. If successful, this + * function returns a handle to the newly created shader program; otherwise + * returns 0. + */ +static GLhandleARB +OGLTR_CreateGrayTextProgram(jint contrast) +{ + GLhandleARB grayTextProgram; + GLint loc; + + J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateGrayTextProgram"); + + grayTextProgram = OGLContext_CreateFragmentProgram( + contrast > 0 ? grayGammaTextShaderSource : grayTextShaderSource); + + if (grayTextProgram == 0) { + J2dRlsTraceLn(J2D_TRACE_ERROR, + "OGLTR_CreateGrayTextProgram: error creating program"); + return 0; + } + + // "use" the program object temporarily so that we can set the uniforms + j2d_glUseProgramObjectARB(grayTextProgram); + + if (contrast > 0) { + double ig = 1.0 / (((double) contrast) / 100.0); + J2dTraceLn1(J2D_TRACE_INFO, + "OGLTR_CreateGrayTextProgram: contrast=%d", contrast); + + loc = j2d_glGetUniformLocationARB(grayTextProgram, "invgamma"); + j2d_glUniform1fARB(loc, ig); + } + + // "unuse" the program object; it will be re-bound later as needed + j2d_glUseProgramObjectARB(0); + + return grayTextProgram; +} /** * (Re)Initializes the gamma related uniforms. @@ -403,6 +474,45 @@ OGLTR_UpdateLCDTextColor(jint contrast) return JNI_TRUE; } +/** + * Updates the current gamma-adjusted source color ("src_adj") of the LCD + * text shader program. Note that we could calculate this value in the + * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work + * (and a measurable performance hit, maybe around 5%) since this value is + * constant over the entire glyph list. So instead we just calculate the + * gamma-adjusted value once and update the uniform parameter of the LCD + * shader as needed. + */ +static jboolean +OGLTR_UpdateGrayTextColor() +{ + GLfloat radj, gadj, badj; + GLfloat clr[4]; + GLint loc; + + /* + * Note: Ideally we would update the "src_adj" uniform parameter only + * when there is a change in the source color. Fortunately, the cost + * of querying the current OpenGL color state and updating the uniform + * value is quite small, and in the common case we only need to do this + * once per GlyphList, so we gain little from trying to optimize too + * eagerly here. + */ + + // get the current OpenGL primary color state + j2d_glGetFloatv(GL_CURRENT_COLOR, clr); + + radj = (GLfloat)clr[0]; + gadj = (GLfloat)clr[1]; + badj = (GLfloat)clr[2]; + + // update the "src_adj" parameter of the shader program with this value + loc = j2d_glGetUniformLocationARB(grayTextProgram, "src_adj"); + j2d_glUniform3fARB(loc, radj, gadj, badj); + + return JNI_TRUE; +} + /** * Enables the LCD text shader and updates any related state, such as the * gamma lookup table textures. @@ -464,6 +574,37 @@ OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, return JNI_TRUE; } +/** + * Enables the GrayScale text shader and updates any related state, such as the + * gamma lookup table textures. + */ +static jboolean +OGLTR_EnableGrayGlyphModeState(GLuint glyphTextureID, jint contrast) +{ + // bind the texture containing glyph data to texture unit 0 + j2d_glActiveTextureARB(GL_TEXTURE0_ARB); + j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID); + j2d_glEnable(GL_TEXTURE_2D); + j2d_glEnable(GL_BLEND); + j2d_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // create the Gray text shader, if necessary + if (grayTextProgram == 0) { + grayTextProgram = OGLTR_CreateGrayTextProgram(contrast); + if (grayTextProgram == 0) { + return JNI_FALSE; + } + } + + // enable the Gray text shader + j2d_glUseProgramObjectARB(grayTextProgram); + + // update the current color settings + if (!OGLTR_UpdateGrayTextColor()) { + return JNI_FALSE; + } + return JNI_TRUE; +} void OGLTR_EnableGlyphVertexCache(OGLContext *oglc) @@ -487,7 +628,7 @@ OGLTR_EnableGlyphVertexCache(OGLContext *oglc) // for grayscale/monochrome text, the current OpenGL source color // is modulated with the glyph image as part of the texture // application stage, so we use GL_MODULATE here - OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); + //OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); } void @@ -529,7 +670,16 @@ OGLTR_DisableGlyphModeState() case MODE_NO_CACHE_COLOR: case MODE_NO_CACHE_GRAY: + j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + /* FALLTHROUGH */ case MODE_USE_CACHE_GRAY: + j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + j2d_glUseProgramObjectARB(0); + j2d_glActiveTextureARB(GL_TEXTURE0_ARB); + j2d_glDisable(GL_TEXTURE_2D); + break; case MODE_NOT_INITED: default: break; @@ -538,7 +688,7 @@ OGLTR_DisableGlyphModeState() static jboolean OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, - GlyphInfo *ginfo, jint x, jint y) + GlyphInfo *ginfo, jint x, jint y, jint contrast) { CacheCellInfo *cell; jfloat x1, y1, x2, y2; @@ -546,6 +696,18 @@ OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, if (glyphMode != MODE_USE_CACHE_GRAY) { OGLTR_DisableGlyphModeState(); CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP); + j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (glyphCacheAA == NULL) { + if (!OGLTR_InitGlyphCache(JNI_FALSE)) { + return JNI_FALSE; + } + } + + if (!OGLTR_EnableGrayGlyphModeState(glyphCacheAA->cacheID, contrast)) + { + return JNI_FALSE; + } + glyphMode = MODE_USE_CACHE_GRAY; } @@ -1006,6 +1168,10 @@ OGLTR_DrawColorGlyphNoCache(OGLContext *oglc, GlyphInfo *ginfo, jint x, jint y) return JNI_TRUE; } +// Control subpixel positioning for macOS 13+ grayscale glyphs +#ifdef MACOSX +extern int useFontSmoothing; +#endif // see DrawGlyphList.c for more on this macro... #define FLOOR_ASSIGN(l, r) \ @@ -1020,6 +1186,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, { int glyphCounter; GLuint dstTextureID = 0; + jint grayGamma = 0; J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList"); @@ -1048,12 +1215,16 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, // * Means to prevent read-after-write problem. // At the moment, a GL_NV_texture_barrier extension is used // to achieve this. +#ifdef MACOSX if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) && dstOps->textureTarget == GL_TEXTURE_2D) { dstTextureID = dstOps->textureID; } + grayGamma = useFontSmoothing ? GRAY_GAMMA : 0; +#endif + for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) { jint x, y; jfloat glyphx, glyphy; @@ -1092,7 +1263,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH && ginfo->height <= OGLTR_CACHE_CELL_HEIGHT) { - ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y); + ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y, grayGamma); } else { ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y); } @@ -1133,7 +1304,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, break; } } - + OGLVertexCache_FlushVertexCache(); OGLTR_DisableGlyphModeState(); }