IDEA-57233 Editor font antialising/appearance problems on Linux

Used desktop DPI instead of hard-coded 72
Compensated increased glyph bitmap size by adjusting font size
Added LCD filter for sub-pixel rendering
Use fontconfig library to provide right rendering options for fonts
Corrected sizes passed to fontconfig library and hinting disabling policy
Added logging and versioned fontconfig lib loading
Resolved font rendering problem in lenses

(cherry picked from commit 0456745afb)

IDEA-152816, IDEA-152454 fix text rendering issues (text cutoff and incorrect rendering in editor fragment components)

FcMatchFont-type pattern substitutions shouldn't be invoked before specific font is selected - it can apply unrelated rules

port commit e21cd635 from JBR 9

(cherry picked from commit 5d704a963b)

partially rollback JBR-363 fix, to apply corresponding change from OpenJDK 12

(cherry picked from commit 3d7ac30072)
This commit is contained in:
Alexey Ushakov
2015-09-21 16:19:19 +03:00
committed by alexey.ushakov@jetbrains.com
parent b6c8116630
commit db3c33474a
3 changed files with 1363 additions and 16 deletions

View File

@@ -25,6 +25,7 @@
package sun.font;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@@ -47,10 +48,10 @@ class FreetypeFontScaler extends FontScaler {
/* At the moment fontmanager library depends on freetype library
and therefore no need to load it explicitly here */
FontManagerNativeLibrary.load();
initIDs(FreetypeFontScaler.class);
initIDs(FreetypeFontScaler.class, Toolkit.class, PhysicalFont.class);
}
private static native void initIDs(Class<?> FFS);
private static native void initIDs(Class<?> FFS, Class<?> toolkitClass, Class<?> pfClass);
private void invalidateScaler() throws FontScalerException {
nativeScaler = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@
#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
#include "jvm_md.h"
#include "sunfontids.h"
#include "sun_font_FreetypeFontScaler.h"
@@ -33,8 +33,11 @@
#if !defined(_WIN32) && !defined(__APPLE_)
#include <dlfcn.h>
#endif
#include <stdlib.h>
#include <math.h>
#include <dlfcn.h>
#include "ft2build.h"
#include FT_LCD_FILTER_H
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_BBOX_H
@@ -43,6 +46,10 @@
#include FT_SYNTHESIS_H
#include FT_LCD_FILTER_H
#include FT_MODULE_H
#include FT_LCD_FILTER_H
/* Use bundled fontconfig.h for now */
#include "fontconfig.h"
#include "fontscaler.h"
@@ -50,6 +57,14 @@
#define FloatToFTFixed(f) (FT_Fixed)((f) * (float)(ftFixed1))
#define FTFixedToFloat(x) ((x) / (float)(ftFixed1))
#define FT26Dot6ToFloat(x) ((x) / ((float) (1<<6)))
#define ROUND(x) ((int) ((x<0) ? (x-0.5) : (x+0.5)))
#define FT26Dot6ToDouble(x) ((x) / ((double) (1<<6)))
#define ROUND(x) ((int) (x+0.5))
#define DEFAULT_DPI 72
#define ADJUST_FONT_SIZE(X, DPI) (((X)*DEFAULT_DPI + ((DPI)>>1))/(DPI))
#define FONTCONFIG_DLL JNI_LIB_NAME("fontconfig")
#define FONTCONFIG_DLL_VERSIONED VERSIONED_JNI_LIB_NAME("fontconfig", "1")
typedef struct {
/* Important note:
@@ -81,7 +96,12 @@ typedef struct FTScalerContext {
jint fmType; /* fractional metrics - on/off */
jboolean doBold; /* perform algorithmic bolding? */
jboolean doItalize; /* perform algorithmic italicizing? */
int renderFlags; /* configuration specific to particular engine */
/* Fontconfig info */
FT_Render_Mode renderFlags;
FT_Int32 loadFlags;
FT_LcdFilter lcdFilter;
int pathType;
int ptsz; /* size in points */
} FTScalerContext;
@@ -97,12 +117,112 @@ void z_error(char *s) {}
/**************** Error handling utilities *****************/
static jmethodID invalidateScalerMID;
static jmethodID getDefaultToolkitMID;
static jclass tkClass;
static jmethodID getScreenResolutionMID;
static jfieldID platNameFID;
typedef FcBool (*FcPatternAddPtrType) (FcPattern *p, const char *object, FcValue value, FcBool append);
typedef FcBool (*FcPatternAddBoolPtrType) (FcPattern *p, const char *object, FcBool b);
typedef FcBool (*FcPatternAddDoublePtrType) (FcPattern *p, const char *object, double d);
typedef FcBool (*FcConfigSubstitutePtrType) (FcConfig *config, FcPattern *p, FcMatchKind kind);
typedef void (*FcDefaultSubstitutePtrType) (FcPattern *pattern);
typedef FcPattern* (*FcPatternCreatePtrType) ();
typedef FcPattern* (*FcFontMatchPtrType) (FcConfig *config, FcPattern *p, FcResult *result);
typedef void (*FcPatternDestroyPtrType) (FcPattern *p);
typedef FcResult (*FcPatternGetBoolPtrType) (const FcPattern *p, const char *object, int n, FcBool *b);
typedef FcResult (*FcPatternGetIntegerPtrType) (const FcPattern *p, const char *object, int n, int *i);
static void *libFontConfig = NULL;
static jboolean logFC = JNI_FALSE;
static FcPatternAddPtrType FcPatternAddPtr;
static FcPatternAddBoolPtrType FcPatternAddBoolPtr;
static FcPatternAddDoublePtrType FcPatternAddDoublePtr;
static FcConfigSubstitutePtrType FcConfigSubstitutePtr;
static FcDefaultSubstitutePtrType FcDefaultSubstitutePtr;
static FcPatternCreatePtrType FcPatternCreatePtr;
static FcFontMatchPtrType FcFontMatchPtr;
static FcPatternDestroyPtrType FcPatternDestroyPtr;
static FcPatternGetBoolPtrType FcPatternGetBoolPtr;
static FcPatternGetIntegerPtrType FcPatternGetIntegerPtr;
static void* openFontConfig() {
void* libfontconfig = NULL;
char *fcLogEnabled = getenv("OPENJDK_FFS_LOG_FC");
if (fcLogEnabled != NULL && !strcmp(fcLogEnabled, "yes")) {
logFC = JNI_TRUE;
}
char *useFC = getenv("OPENJDK_FFS_USE_FC");
if (useFC != NULL && !strcmp(useFC, "no")) {
if (logFC) fprintf(stderr, "FC_LOG: fontconfig disabled in freetypescaler\n");
return NULL;
}
libfontconfig = dlopen(FONTCONFIG_DLL_VERSIONED, RTLD_LOCAL | RTLD_LAZY);
if (libfontconfig == NULL) {
libfontconfig = dlopen(FONTCONFIG_DLL, RTLD_LOCAL | RTLD_LAZY);
if (libfontconfig == NULL) {
if (logFC) fprintf(stderr, "FC_LOG: cannot open %s\n", FONTCONFIG_DLL);
return NULL;
}
}
return libfontconfig;
}
JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_initIDs(
JNIEnv *env, jobject scaler, jclass FFSClass) {
JNIEnv *env, jobject scaler, jclass FFSClass, jclass TKClass, jclass PFClass) {
invalidateScalerMID =
(*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
getDefaultToolkitMID =
(*env)->GetStaticMethodID(env, TKClass, "getDefaultToolkit",
"()Ljava/awt/Toolkit;");
getScreenResolutionMID =
(*env)->GetMethodID(env, TKClass, "getScreenResolution", "()I");
tkClass = (*env)->NewGlobalRef(env, TKClass);
platNameFID = (*env)->GetFieldID(env, PFClass, "platName", "Ljava/lang/String;");
libFontConfig = openFontConfig();
if (libFontConfig) {
FcPatternAddPtr = (FcPatternAddPtrType) dlsym(libFontConfig, "FcPatternAdd");
FcPatternAddBoolPtr = (FcPatternAddBoolPtrType) dlsym(libFontConfig, "FcPatternAddBool");
FcPatternAddDoublePtr = (FcPatternAddDoublePtrType) dlsym(libFontConfig, "FcPatternAddDouble");
FcConfigSubstitutePtr = (FcConfigSubstitutePtrType) dlsym(libFontConfig, "FcConfigSubstitute");
FcDefaultSubstitutePtr = (FcDefaultSubstitutePtrType) dlsym(libFontConfig, "FcDefaultSubstitute");
FcPatternCreatePtr = (FcPatternCreatePtrType) dlsym(libFontConfig, "FcPatternCreate");
FcFontMatchPtr = (FcFontMatchPtrType) dlsym(libFontConfig, "FcFontMatch");
FcPatternDestroyPtr = (FcPatternDestroyPtrType) dlsym(libFontConfig, "FcPatternDestroy");
FcPatternGetBoolPtr = (FcPatternGetBoolPtrType) dlsym(libFontConfig, "FcPatternGetBool");
FcPatternGetIntegerPtr = (FcPatternGetIntegerPtrType) dlsym(libFontConfig, "FcPatternGetInteger");
}
}
static char* getPhysFontName(JNIEnv *env, jobject font2d) {
jstring jstr;
jstr = (*env)->GetObjectField(env, font2d, platNameFID);
char* str = (*env)->GetStringUTFChars(env, jstr, NULL);
return str;
}
static int getScreenResolution(JNIEnv *env) {
jthrowable exc;
jclass tk = (*env)->CallStaticObjectMethod(
env, tkClass, getDefaultToolkitMID);
int dpi = (*env)->CallIntMethod(env, tk, getScreenResolutionMID);
/* Test if there is no exception here (can get java.awt.HeadlessException)
* Fallback to default DPI otherwise
*/
exc = (*env)->ExceptionOccurred(env);
if (exc) {
(*env)->ExceptionClear(env);
return DEFAULT_DPI;
}
return dpi;
}
static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
@@ -562,27 +682,234 @@ static void setupTransform(FT_Matrix* target, FTScalerContext *context) {
static int setupFTContext(JNIEnv *env,
jobject font2D,
FTScalerInfo *scalerInfo,
FTScalerContext *context) {
FTScalerContext *context,
FT_Bool configureFont) {
FT_Matrix matrix;
int errCode = 0;
scalerInfo->env = env;
scalerInfo->font2D = font2D;
if (context != NULL) {
setupTransform(&matrix, context);
FT_Set_Transform(scalerInfo->face, &matrix, NULL);
FT_UInt dpi = (FT_UInt) getScreenResolution(env);
errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
errCode = FT_Set_Char_Size(scalerInfo->face, 0, ADJUST_FONT_SIZE(context->ptsz, dpi), dpi, dpi);
if (errCode) return errCode;
if (errCode == 0) {
errCode = FT_Activate_Size(scalerInfo->face->size);
errCode = FT_Activate_Size(scalerInfo->face->size);
if (errCode) return errCode;
if (configureFont) {
context->renderFlags = FT_RENDER_MODE_NORMAL;
context->lcdFilter = FT_LCD_FILTER_NONE;
context->loadFlags = FT_LOAD_DEFAULT;
if (libFontConfig == NULL) {
if (context->aaType == TEXT_AA_OFF) {
context->loadFlags = FT_LOAD_TARGET_MONO;
} else if (context->aaType == TEXT_AA_ON) {
context->loadFlags = FT_LOAD_TARGET_LIGHT;
} else {
context->lcdFilter = FT_LCD_FILTER_LIGHT;
if (context->aaType == TEXT_AA_LCD_HRGB ||
context->aaType == TEXT_AA_LCD_HBGR) {
context->loadFlags = FT_LOAD_TARGET_LCD;
} else {
context->loadFlags = FT_LOAD_TARGET_LCD_V;
}
}
context->renderFlags = FT_LOAD_TARGET_MODE(context->loadFlags);
return 0;
}
FcPattern *fcPattern = 0;
fcPattern = (*FcPatternCreatePtr)();
FcValue fcValue;
fcValue.type = FcTypeString;
char *fontName = getPhysFontName(env, font2D);
if (logFC) fprintf(stderr, "FC_LOG: %s ", fontName);
fcValue.u.s = (const FcChar8*)fontName;
(*FcPatternAddPtr)(fcPattern, FC_FILE, fcValue, FcTrue);
(*FcPatternAddBoolPtr)(fcPattern, FC_SCALABLE, FcTrue);
double fcSize = FT26Dot6ToDouble(ADJUST_FONT_SIZE(context->ptsz, dpi));
(*FcPatternAddDoublePtr)(fcPattern, FC_SIZE, fcSize);
if (logFC) fprintf(stderr, " size=%f", fcSize);
(*FcConfigSubstitutePtr)(0, fcPattern, FcMatchPattern);
(*FcDefaultSubstitutePtr)(fcPattern);
FcResult matchResult = FcResultNoMatch;
FcPattern *resultPattern = 0;
resultPattern = (*FcFontMatchPtr)(0, fcPattern, &matchResult);
if (matchResult != FcResultMatch) {
(*FcPatternDestroyPtr)(fcPattern);
if (logFC) fprintf(stderr, " - NOT FOUND\n");
return 1;
}
if (logFC) fprintf(stderr, "\nFC_LOG: ");
(*FcPatternDestroyPtr)(fcPattern);
FcPattern *pattern = resultPattern;
FcBool fcHinting = FcFalse;
FcBool fcHintingSet = (*FcPatternGetBoolPtr)(pattern, FC_HINTING, 0, &fcHinting) == FcResultMatch;
if (logFC && fcHintingSet) fprintf(stderr, "FC_HINTING(%d) ", fcHinting);
int fcHintStyle = FC_HINT_NONE;
FcBool fcHintStyleSet = (*FcPatternGetIntegerPtr)(pattern, FC_HINT_STYLE, 0, &fcHintStyle) == FcResultMatch;
if (logFC && fcHintStyleSet) {
switch (fcHintStyle) {
case FC_HINT_NONE:
fprintf(stderr, "FC_HINT_NONE ");
break;
case FC_HINT_SLIGHT:
fprintf(stderr, "FC_HINT_SLIGHT ");
break;
case FC_HINT_MEDIUM:
fprintf(stderr, "FC_HINT_MEDIUM ");
break;
case FC_HINT_FULL:
fprintf(stderr, "FC_HINT_FULL ");
break;
default:
fprintf(stderr, "FC_HINT_UNKNOWN ");
break;
}
}
if (fcHintingSet && !fcHinting) {
fcHintStyleSet = FcTrue;
fcHintStyle = FC_HINT_NONE;
}
if (fcHintStyleSet && fcHintStyle == FC_HINT_NONE) {
fcHintingSet = FcTrue;
fcHinting = FcFalse;
}
FcBool fcAntialias = FcFalse;
FcBool fcAntialiasSet = (*FcPatternGetBoolPtr)(pattern, FC_ANTIALIAS, 0, &fcAntialias) == FcResultMatch;
if (logFC) {
switch(context->aaType) {
case TEXT_AA_ON:
fprintf(stderr, "JDK_AA_ON ");
break;
case TEXT_AA_OFF:
fprintf(stderr, "JDK_AA_OFF ");
break;
case TEXT_AA_LCD_HRGB:
fprintf(stderr, "JDK_AA_LCD_HRGB ");
break;
case TEXT_AA_LCD_HBGR:
fprintf(stderr, "JDK_AA_LCD_HBGR ");
break;
default:
fprintf(stderr, "JDK_AA_UNKNOWN ");
break;
}
if (fcAntialiasSet) fprintf(stderr, "FC_ANTIALIAS(%d) ", fcAntialias);
}
if (context->aaType == TEXT_AA_ON) { // Greyscale AA
context->renderFlags = FT_RENDER_MODE_NORMAL;
if (fcHintingSet) {
switch (fcHintStyle) {
case FC_HINT_NONE:
context->loadFlags = FT_LOAD_NO_HINTING;
break;
case FC_HINT_SLIGHT:
context->loadFlags = FT_LOAD_TARGET_LIGHT;
break;
case FC_HINT_MEDIUM:
case FC_HINT_FULL:
default:
break;
}
}
}
else if (context->aaType == TEXT_AA_OFF) { // No AA
context->renderFlags = FT_RENDER_MODE_MONO;
context->loadFlags = (!fcHintingSet || fcHinting) ? FT_LOAD_TARGET_MONO : FT_LOAD_NO_HINTING;
} else {
int fcRGBA = FC_RGBA_UNKNOWN;
if (fcAntialiasSet && fcAntialias) {
if ((*FcPatternGetIntegerPtr)(pattern, FC_RGBA, 0, &fcRGBA) == FcResultMatch) {
switch (fcRGBA) {
case FC_RGBA_RGB:
case FC_RGBA_BGR:
if (logFC) fprintf(stderr, fcRGBA == FC_RGBA_RGB ? "FC_RGBA_RGB " : "FC_RGBA_BGR ");
context->loadFlags = FT_LOAD_TARGET_LCD;
context->renderFlags = FT_RENDER_MODE_LCD;
break;
case FC_RGBA_VRGB:
case FC_RGBA_VBGR:
if (logFC) fprintf(stderr, fcRGBA == FC_RGBA_VRGB ? "FC_RGBA_VRGB " : "FC_RGBA_VBGR ");
context->loadFlags = FT_LOAD_TARGET_LCD_V;
context->renderFlags = FT_RENDER_MODE_LCD_V;
break;
default:
if (logFC) fprintf(stderr, "FC_RGBA_UNKNOWN ");
break;
}
}
}
if (fcRGBA == FC_RGBA_UNKNOWN) {
if (context->aaType == TEXT_AA_LCD_HRGB ||
context->aaType == TEXT_AA_LCD_HBGR) {
context->loadFlags = FT_LOAD_TARGET_LCD;
context->renderFlags = FT_RENDER_MODE_LCD;
} else {
context->loadFlags = FT_LOAD_TARGET_LCD_V;
context->renderFlags = FT_RENDER_MODE_LCD_V;
}
}
}
FcBool fcAutohint = FcFalse;
FcBool fcAutohintSet = (*FcPatternGetBoolPtr)(pattern, FC_AUTOHINT, 0, &fcAutohint) == FcResultMatch;
if (logFC && fcAutohintSet) fprintf(stderr, "FC_AUTOHINT(%d) ", fcAutohint);
if (fcAutohintSet && fcAutohint) {
context->loadFlags |= FT_LOAD_FORCE_AUTOHINT;
}
FT_LcdFilter fcLCDFilter;
FcBool fcLCDFilterSet = (*FcPatternGetIntegerPtr)(pattern, FC_LCD_FILTER, 0, (int*) &fcLCDFilter) == FcResultMatch;
context->lcdFilter = FT_LCD_FILTER_DEFAULT;
if (fcLCDFilterSet) {
switch (fcLCDFilter) {
case FC_LCD_NONE:
if (logFC) fprintf(stderr, "FC_LCD_NONE");
context->lcdFilter = FT_LCD_FILTER_NONE;
break;
case FC_LCD_LIGHT:
if (logFC) fprintf(stderr, "FC_LCD_LIGHT");
context->lcdFilter = FT_LCD_FILTER_LIGHT;
break;
case FC_LCD_LEGACY:
if (logFC) fprintf(stderr, "FC_LCD_LEGACY");
context->lcdFilter = FT_LCD_FILTER_LEGACY;
break;
case FC_LCD_DEFAULT:
if (logFC) fprintf(stderr, "FC_LCD_DEFAULT");
break;
default:
if (logFC) fprintf(stderr, "FC_LCD_UNKNOWN");
;
}
}
(*FcPatternDestroyPtr)(pattern);
if (logFC) fprintf(stderr, "\n");
}
FT_Library_SetLcdFilter(scalerInfo->library, FT_LCD_FILTER_DEFAULT);
}
return errCode;
return 0;
}
// using same values as for the transformation matrix
@@ -615,7 +942,7 @@ Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
}
errCode = setupFTContext(env, font2D, scalerInfo, context);
errCode = setupFTContext(env, font2D, scalerInfo, context, FcFalse);
if (errCode) {
metrics = (*env)->NewObject(env,
@@ -900,6 +1227,7 @@ static jlong
GlyphInfo *glyphInfo;
int renderFlags = FT_LOAD_DEFAULT, target;
FT_GlyphSlot ftglyph;
FT_LcdFilter lcdFilter = FT_LCD_FILTER_NONE;
FTScalerContext* context =
(FTScalerContext*) jlong_to_ptr(pScalerContext);
@@ -910,7 +1238,7 @@ static jlong
return ptr_to_jlong(getNullGlyphImage());
}
error = setupFTContext(env, font2D, scalerInfo, context);
error = setupFTContext(env, font2D, scalerInfo, context, FcTrue);
if (error) {
invalidateJavaScaler(env, scaler, scalerInfo);
return ptr_to_jlong(getNullGlyphImage());
@@ -957,6 +1285,8 @@ static jlong
}
ftglyph = scalerInfo->face->glyph;
FT_Library library = ftglyph->library;
FT_Library_SetLcdFilter (library, context->lcdFilter);
/* apply styles */
if (context->doBold) { /* if bold style */
@@ -1107,7 +1437,7 @@ Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
/* Freetype functions *may* cause callback to java
that can use cached values. Make sure our cache is up to date.
NB: scaler context is not important at this point, can use NULL. */
int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
int errCode = setupFTContext(env, font2D, scalerInfo, NULL, FcFalse);
if (errCode) {
return;
}
@@ -1170,7 +1500,7 @@ Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
/* Freetype functions *may* cause callback to java
that can use cached values. Make sure our cache is up to date.
Scaler context is not important here, can use NULL. */
errCode = setupFTContext(env, font2D, scalerInfo, NULL);
errCode = setupFTContext(env, font2D, scalerInfo, NULL, FcFalse);
if (errCode) {
return 0;
}
@@ -1193,7 +1523,7 @@ static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
return NULL;
}
error = setupFTContext(env, font2D, scalerInfo, context);
error = setupFTContext(env, font2D, scalerInfo, context, FcTrue);
if (error) {
return NULL;
}
@@ -1635,3 +1965,9 @@ Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
sunFontIDs.pt2DFloatCtr, x, y);
}
void JNI_OnUnload(JavaVM *vm, void *reserved) {
if (libFontConfig != NULL) {
dlclose(libFontConfig);
}
}