mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-410 Added emoji support for Linux
This commit is contained in:
committed by
Vitaly Provodin
parent
e4ce4ce9c2
commit
fd0d59b9cb
@@ -502,7 +502,7 @@ public final class GlyphList {
|
||||
}
|
||||
|
||||
public static boolean canContainColorGlyphs() {
|
||||
return FontUtilities.isMacOSX;
|
||||
return FontUtilities.isMacOSX || FontUtilities.isLinux;
|
||||
}
|
||||
|
||||
public boolean isColorGlyph(int glyphIndex) {
|
||||
|
||||
@@ -80,6 +80,8 @@
|
||||
#define ROUND(x) ((int) ((x<0) ? (x-0.5) : (x+0.5)))
|
||||
#define FT26Dot6ToDouble(x) ((x) / ((double) (1<<6)))
|
||||
#define FT26Dot6ToInt(x) (((int)(x)) >> 6)
|
||||
#define FT26Dot6ToIntCeil(x) (((int)(x - 1 + (1 << 6))) >> 6)
|
||||
#define IntToFT26Dot6(x) (((FT_Fixed)(x)) << 6)
|
||||
#define DEFAULT_DPI 72
|
||||
#define MAX_DPI 1024
|
||||
#define ADJUST_FONT_SIZE(X, DPI) (((X)*DEFAULT_DPI + ((DPI)>>1))/(DPI))
|
||||
@@ -134,8 +136,17 @@ typedef struct FTScalerContext {
|
||||
|
||||
int pathType;
|
||||
int ptsz; /* size in points */
|
||||
int fixedSizeIndex;/* -1 for scalable fonts and index inside
|
||||
* scalerInfo->face->available_sizes otherwise */
|
||||
} FTScalerContext;
|
||||
|
||||
/* SampledBGRABitmap contains (possibly) downscaled image data
|
||||
* prepared for sampling when generating transformed bitmap */
|
||||
typedef struct SampledBGRABitmap {
|
||||
unsigned char* data;
|
||||
int left, top, width, height, rowBytes, xDownscale, yDownscale;
|
||||
} SampledBGRABitmap;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* These are referenced in the freetype sources if DEBUG macro is defined.
|
||||
To simplify work with debuging version of freetype we define
|
||||
@@ -851,7 +862,31 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
|
||||
FT_Set_Transform(scalerInfo->face, &matrix, NULL);
|
||||
FT_UInt dpi = (FT_UInt) getScreenResolution(env);
|
||||
|
||||
errCode = FT_Set_Char_Size(scalerInfo->face, 0, ADJUST_FONT_SIZE(context->ptsz, dpi), dpi, dpi);
|
||||
if (FT_IS_SCALABLE(scalerInfo->face)) { // Standard scalable face
|
||||
context->fixedSizeIndex = -1;
|
||||
errCode = FT_Set_Char_Size(scalerInfo->face, 0,
|
||||
ADJUST_FONT_SIZE(context->ptsz, dpi),
|
||||
dpi, dpi);
|
||||
} else { // Non-scalable face (that should only be bitmap faces)
|
||||
const int ptsz = context->ptsz;
|
||||
// Best size is smallest, but not smaller than requested
|
||||
int bestSizeIndex = 0;
|
||||
FT_Pos bestSize = scalerInfo->face->available_sizes[0].size;
|
||||
int i;
|
||||
for (i = 1; i < scalerInfo->face->num_fixed_sizes; i++) {
|
||||
FT_Pos size = scalerInfo->face->available_sizes[i].size;
|
||||
if ((size >= ptsz && bestSize >= ptsz && size < bestSize) ||
|
||||
(size < ptsz && bestSize < ptsz && size > bestSize) ||
|
||||
(size >= ptsz && bestSize < ptsz)) {
|
||||
bestSizeIndex = i;
|
||||
bestSize = size;
|
||||
}
|
||||
}
|
||||
context->fixedSizeIndex = bestSizeIndex;
|
||||
errCode = FT_Set_Char_Size(scalerInfo->face, 0,
|
||||
ADJUST_FONT_SIZE(bestSize, dpi),
|
||||
dpi, dpi);
|
||||
}
|
||||
if (errCode) return errCode;
|
||||
|
||||
errCode = FT_Activate_Size(scalerInfo->face->size);
|
||||
@@ -971,9 +1006,14 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
|
||||
|
||||
if (context->aaType == TEXT_AA_ON) { // Greyscale AA
|
||||
setupLoadRenderFlags(context, fcHintStyle, fcAutohint, fcAutohintSet, FT_LOAD_DEFAULT, FT_RENDER_MODE_NORMAL);
|
||||
}
|
||||
else if (context->aaType == TEXT_AA_OFF) { // No AA
|
||||
setupLoadRenderFlags(context, fcHintStyle, fcAutohint, fcAutohintSet, FT_LOAD_TARGET_MONO, FT_RENDER_MODE_MONO);
|
||||
} else if (context->aaType == TEXT_AA_OFF) { // No AA
|
||||
/* We disable MONO for non-scalable fonts, because that
|
||||
* is most probably a colored bitmap glyph */
|
||||
setupLoadRenderFlags(context, fcHintStyle, fcAutohint, fcAutohintSet,
|
||||
context->fixedSizeIndex == -1 ?
|
||||
FT_LOAD_TARGET_MONO : FT_LOAD_TARGET_NORMAL,
|
||||
context->fixedSizeIndex == -1 ?
|
||||
FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL);
|
||||
} else {
|
||||
int fcRGBA = FC_RGBA_UNKNOWN;
|
||||
if (fcAntialiasSet && fcAntialias) {
|
||||
@@ -1012,6 +1052,10 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context->fixedSizeIndex != -1) {
|
||||
// This is most probably a colored bitmap glyph, so enable COLOR
|
||||
context->loadFlags |= FT_LOAD_COLOR;
|
||||
}
|
||||
|
||||
FT_LcdFilter fcLCDFilter;
|
||||
FcBool fcLCDFilterSet = (*FcPatternGetIntegerPtr)(pattern, FC_LCD_FILTER, 0, (int*) &fcLCDFilter) == FcResultMatch;
|
||||
@@ -1112,36 +1156,63 @@ Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
|
||||
(-FTFixedToFloat(context->transform.yx) * (x) + \
|
||||
FTFixedToFloat(context->transform.yy) * (y))
|
||||
|
||||
/*
|
||||
* See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics()
|
||||
* http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
|
||||
*/
|
||||
/* ascent */
|
||||
ax = 0;
|
||||
ay = -(jfloat) (FT_MulFixFloatShift6(
|
||||
((jlong) scalerInfo->face->ascender),
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale));
|
||||
/* descent */
|
||||
dx = 0;
|
||||
dy = -(jfloat) (FT_MulFixFloatShift6(
|
||||
((jlong) scalerInfo->face->descender),
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale));
|
||||
/* baseline */
|
||||
bx = by = 0;
|
||||
if (context->fixedSizeIndex == -1) {
|
||||
/*
|
||||
* See FreeType source code:
|
||||
* src/base/ftobjs.c ft_recompute_scaled_metrics()
|
||||
* http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
|
||||
*/
|
||||
/* ascent */
|
||||
ax = 0;
|
||||
ay = -(jfloat) (FT_MulFixFloatShift6(
|
||||
((jlong) scalerInfo->face->ascender),
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale));
|
||||
/* descent */
|
||||
dx = 0;
|
||||
dy = -(jfloat) (FT_MulFixFloatShift6(
|
||||
((jlong) scalerInfo->face->descender),
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale));
|
||||
/* baseline */
|
||||
bx = by = 0;
|
||||
|
||||
/* leading */
|
||||
lx = 0;
|
||||
ly = (jfloat) (FT_MulFixFloatShift6(
|
||||
(jlong) scalerInfo->face->height,
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale))
|
||||
+ ay - dy;
|
||||
/* max advance */
|
||||
mx = (jfloat) FT26Dot6ToFloat(
|
||||
scalerInfo->face->size->metrics.max_advance +
|
||||
OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height) +
|
||||
BOLD_MODIFIER(scalerInfo->face->units_per_EM,
|
||||
scalerInfo->face->size->metrics.y_scale));
|
||||
my = 0;
|
||||
/* leading */
|
||||
lx = 0;
|
||||
ly = (jfloat) (FT_MulFixFloatShift6(
|
||||
(jlong) scalerInfo->face->height,
|
||||
(jlong) scalerInfo->face->size->metrics.y_scale))
|
||||
+ ay - dy;
|
||||
/* max advance */
|
||||
mx = (jfloat) FT26Dot6ToFloat(
|
||||
scalerInfo->face->size->metrics.max_advance +
|
||||
OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height) +
|
||||
BOLD_MODIFIER(scalerInfo->face->units_per_EM,
|
||||
scalerInfo->face->size->metrics.y_scale));
|
||||
my = 0;
|
||||
} else {
|
||||
/* Just manually scale metrics for non-scalable fonts */
|
||||
FT_Fixed scale = FT_DivFix(context->ptsz,
|
||||
scalerInfo->face->available_sizes[context->fixedSizeIndex].size);
|
||||
/* ascent */
|
||||
ax = 0;
|
||||
ay = -(jfloat) FT_MulFixFloatShift6(
|
||||
scalerInfo->face->size->metrics.ascender, scale);
|
||||
/* descent */
|
||||
dx = 0;
|
||||
dy = -(jfloat) FT_MulFixFloatShift6(
|
||||
scalerInfo->face->size->metrics.descender, scale);
|
||||
/* baseline */
|
||||
bx = by = 0;
|
||||
|
||||
/* leading */
|
||||
lx = 0;
|
||||
ly = (jfloat) FT_MulFixFloatShift6(
|
||||
scalerInfo->face->size->metrics.height, scale) + ay - dy;
|
||||
/* max advance */
|
||||
/* no bold/italic transformations for non-scalable fonts */
|
||||
mx = (jfloat) FT_MulFixFloatShift6(
|
||||
scalerInfo->face->size->metrics.max_advance, scale);
|
||||
my = 0;
|
||||
}
|
||||
|
||||
metrics = (*env)->NewObject(env,
|
||||
sunFontIDs.strikeMetricsClass,
|
||||
@@ -1331,6 +1402,181 @@ static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
|
||||
}
|
||||
|
||||
|
||||
/* Get enclosing axis-aligned rectangle of transformed bitmap bounds */
|
||||
static FT_BBox getTransformedBitmapBoundingBox(FT_GlyphSlot ftglyph,
|
||||
const FT_Matrix* transform) {
|
||||
FT_Vector corners[4];
|
||||
corners[0].x = corners[2].x = IntToFT26Dot6(ftglyph->bitmap_left);
|
||||
corners[0].y = corners[1].y = IntToFT26Dot6(ftglyph->bitmap_top);
|
||||
corners[1].x = corners[3].x = IntToFT26Dot6(ftglyph->bitmap_left +
|
||||
(FT_Int) ftglyph->bitmap.width);
|
||||
corners[2].y = corners[3].y = IntToFT26Dot6(ftglyph->bitmap_top -
|
||||
(FT_Int) ftglyph->bitmap.rows);
|
||||
|
||||
FT_Vector_Transform(corners, transform);
|
||||
FT_BBox bb = {corners[0].x, corners[0].y, corners[0].x, corners[0].y};
|
||||
int i;
|
||||
for (i = 1; i < 4; i++) {
|
||||
FT_Vector_Transform(corners + i, transform);
|
||||
if (corners[i].x < bb.xMin) bb.xMin = corners[i].x;
|
||||
if (corners[i].x > bb.xMax) bb.xMax = corners[i].x;
|
||||
if (corners[i].y < bb.yMin) bb.yMin = corners[i].y;
|
||||
if (corners[i].y > bb.yMax) bb.yMax = corners[i].y;
|
||||
}
|
||||
bb.xMin = FT26Dot6ToInt(bb.xMin);
|
||||
bb.yMin = FT26Dot6ToInt(bb.yMin);
|
||||
bb.xMax = FT26Dot6ToIntCeil(bb.xMax);
|
||||
bb.yMax = FT26Dot6ToIntCeil(bb.yMax);
|
||||
return bb;
|
||||
}
|
||||
|
||||
/* Generate SampledBGRABitmap, downscaling original image when necessary.
|
||||
* It may allocate memory for downscaled image,
|
||||
* so it must be freed with freeSampledBGRABitmap() */
|
||||
static SampledBGRABitmap createSampledBGRABitmap(FT_GlyphSlot ftglyph,
|
||||
int xDownscale,
|
||||
int yDownscale) {
|
||||
SampledBGRABitmap sampledBitmap;
|
||||
if (xDownscale == 1 && yDownscale == 1) { // No downscale, use original data
|
||||
sampledBitmap.data = ftglyph->bitmap.buffer;
|
||||
sampledBitmap.left = ftglyph->bitmap_left;
|
||||
sampledBitmap.top = ftglyph->bitmap_top;
|
||||
sampledBitmap.width = ftglyph->bitmap.width;
|
||||
sampledBitmap.height = ftglyph->bitmap.rows;
|
||||
sampledBitmap.rowBytes = ftglyph->bitmap.pitch;
|
||||
sampledBitmap.xDownscale = 1;
|
||||
sampledBitmap.yDownscale = 1;
|
||||
} else { // Generate downscaled bitmap
|
||||
sampledBitmap.left = ftglyph->bitmap_left / xDownscale;
|
||||
sampledBitmap.top = (ftglyph->bitmap_top + yDownscale - 1) / yDownscale;
|
||||
sampledBitmap.width =
|
||||
(ftglyph->bitmap_left + (FT_Pos) ftglyph->bitmap.width -
|
||||
sampledBitmap.left * xDownscale + xDownscale - 1) / xDownscale;
|
||||
sampledBitmap.height =
|
||||
(sampledBitmap.top * yDownscale - ftglyph->bitmap_top +
|
||||
(FT_Pos) ftglyph->bitmap.rows + yDownscale - 1) / yDownscale;
|
||||
sampledBitmap.data =
|
||||
malloc(4 * sampledBitmap.width * sampledBitmap.height);
|
||||
sampledBitmap.rowBytes = sampledBitmap.width * 4;
|
||||
sampledBitmap.xDownscale = xDownscale;
|
||||
sampledBitmap.yDownscale = yDownscale;
|
||||
int xOffset = sampledBitmap.left * xDownscale - ftglyph->bitmap_left;
|
||||
int yOffset = ftglyph->bitmap_top - sampledBitmap.top * yDownscale;
|
||||
int x, y;
|
||||
for (y = 0; y < sampledBitmap.height; y++) {
|
||||
for (x = 0; x < sampledBitmap.width; x++) {
|
||||
// Average pixels
|
||||
int b = 0, g = 0, r = 0, a = 0;
|
||||
int xFrom = x * xDownscale + xOffset,
|
||||
yFrom = y * yDownscale + yOffset,
|
||||
xTo = xFrom + xDownscale,
|
||||
yTo = yFrom + yDownscale;
|
||||
if (xFrom < 0) xFrom = 0;
|
||||
if (xTo > (int) ftglyph->bitmap.width) xTo = (int) ftglyph->bitmap.width;
|
||||
if (yFrom < 0) yFrom = 0;
|
||||
if (yTo > (int) ftglyph->bitmap.rows) yTo = (int) ftglyph->bitmap.rows;
|
||||
int i, j;
|
||||
for (j = yFrom; j < yTo; j++) {
|
||||
for (i = xFrom; i < xTo; i++) {
|
||||
int offset = j * ftglyph->bitmap.pitch + i * 4;
|
||||
b += ftglyph->bitmap.buffer[offset + 0];
|
||||
g += ftglyph->bitmap.buffer[offset + 1];
|
||||
r += ftglyph->bitmap.buffer[offset + 2];
|
||||
a += ftglyph->bitmap.buffer[offset + 3];
|
||||
}
|
||||
}
|
||||
int offset = y * sampledBitmap.rowBytes + x * 4;
|
||||
sampledBitmap.data[offset + 0] = b / xDownscale / yDownscale;
|
||||
sampledBitmap.data[offset + 1] = g / xDownscale / yDownscale;
|
||||
sampledBitmap.data[offset + 2] = r / xDownscale / yDownscale;
|
||||
sampledBitmap.data[offset + 3] = a / xDownscale / yDownscale;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sampledBitmap;
|
||||
}
|
||||
static void freeSampledBGRABitmap(SampledBGRABitmap* bitmap) {
|
||||
if (bitmap->xDownscale != 1 || bitmap->yDownscale != 1) {
|
||||
free(bitmap->data);
|
||||
}
|
||||
}
|
||||
/* Get color (returned via b, g, r and a variables, [0-256))
|
||||
* from specific pixel in bitmap.
|
||||
* Returns black-transparent (0,0,0,0) color when sampling out of bounds */
|
||||
static void sampleBGRABitmapGlyph(int* b, int* g, int* r, int* a,
|
||||
const SampledBGRABitmap* bitmap,
|
||||
int x, int y) {
|
||||
int column = x - bitmap->left, row = bitmap->top - y;
|
||||
if (column < 0 || column >= bitmap->width ||
|
||||
row < 0 || row >= bitmap->height) {
|
||||
*b = *g = *r = *a = 0;
|
||||
} else {
|
||||
int offset = row * bitmap->rowBytes + column * 4;
|
||||
*b = bitmap->data[offset + 0];
|
||||
*g = bitmap->data[offset + 1];
|
||||
*r = bitmap->data[offset + 2];
|
||||
*a = bitmap->data[offset + 3];
|
||||
}
|
||||
}
|
||||
static int bilinearColorMix(int c00, int c10, int c01, int c11,
|
||||
float x, float y) {
|
||||
float top = (float) c00 + x * (float) (c10 - c00);
|
||||
float bottom = (float) c01 + x * (float) (c11 - c01);
|
||||
return (int) (top + y * (bottom - top));
|
||||
}
|
||||
/* Transform ftglyph into pre-allocated glyphInfo with transform matrix */
|
||||
static void transformBGRABitmapGlyph(FT_GlyphSlot ftglyph, GlyphInfo* glyphInfo,
|
||||
const FT_Matrix* transform,
|
||||
const FT_BBox* dstBoundingBox,
|
||||
const jboolean linear) {
|
||||
FT_Matrix inv = *transform;
|
||||
FT_Matrix_Invert(&inv); // Transformed -> original bitmap space
|
||||
int invScaleX = (int) sqrt(FTFixedToFloat(FT_MulFix(inv.xx, inv.xx) +
|
||||
FT_MulFix(inv.xy, inv.xy)));
|
||||
int invScaleY = (int) sqrt(FTFixedToFloat(FT_MulFix(inv.yx, inv.yx) +
|
||||
FT_MulFix(inv.yy, inv.yy)));
|
||||
if (invScaleX < 1) invScaleX = 1;
|
||||
if (invScaleY < 1) invScaleY = 1;
|
||||
SampledBGRABitmap sampledBitmap =
|
||||
createSampledBGRABitmap(ftglyph, invScaleX, invScaleY);
|
||||
int x, y;
|
||||
for (y = 0; y < glyphInfo->height; y++) {
|
||||
for (x = 0; x < glyphInfo->width; x++) {
|
||||
FT_Vector position = {
|
||||
IntToFT26Dot6(dstBoundingBox->xMin + x),
|
||||
IntToFT26Dot6(dstBoundingBox->yMax - y)
|
||||
};
|
||||
FT_Vector_Transform(&position, &inv);
|
||||
int sampleX = FT26Dot6ToInt(position.x / invScaleX),
|
||||
sampleY = FT26Dot6ToInt(position.y / invScaleY);
|
||||
int b, g, r, a;
|
||||
sampleBGRABitmapGlyph(&b, &g, &r, &a,
|
||||
&sampledBitmap, sampleX, sampleY);
|
||||
if (linear) {
|
||||
int bX, gX, rX, aX, bY, gY, rY, aY, bXY, gXY, rXY, aXY;
|
||||
sampleBGRABitmapGlyph(&bX, &gX, &rX, &aX,
|
||||
&sampledBitmap, sampleX + 1, sampleY);
|
||||
sampleBGRABitmapGlyph(&bY, &gY, &rY, &aY,
|
||||
&sampledBitmap, sampleX, sampleY + 1);
|
||||
sampleBGRABitmapGlyph(&bXY, &gXY, &rXY, &aXY,
|
||||
&sampledBitmap, sampleX + 1, sampleY + 1);
|
||||
float fractX = FT26Dot6ToFloat((position.x / invScaleX) & 63),
|
||||
fractY = FT26Dot6ToFloat((position.y / invScaleY) & 63);
|
||||
b = bilinearColorMix(b, bX, bY, bXY, fractX, fractY);
|
||||
g = bilinearColorMix(g, gX, gY, gXY, fractX, fractY);
|
||||
r = bilinearColorMix(r, rX, rY, rXY, fractX, fractY);
|
||||
a = bilinearColorMix(a, aX, aY, aXY, fractX, fractY);
|
||||
}
|
||||
glyphInfo->image[y*glyphInfo->rowBytes + x * 4 + 0] = b;
|
||||
glyphInfo->image[y*glyphInfo->rowBytes + x * 4 + 1] = g;
|
||||
glyphInfo->image[y*glyphInfo->rowBytes + x * 4 + 2] = r;
|
||||
glyphInfo->image[y*glyphInfo->rowBytes + x * 4 + 3] = a;
|
||||
}
|
||||
}
|
||||
freeSampledBGRABitmap(&sampledBitmap);
|
||||
}
|
||||
|
||||
|
||||
/* JDK does not use glyph images for fonts with a
|
||||
* pixel size > 100 (see THRESHOLD in OutlineTextRenderer.java)
|
||||
* so if the glyph bitmap image dimension is > 1024 pixels,
|
||||
@@ -1426,7 +1672,9 @@ static jlong
|
||||
context->loadFlags |= FT_LOAD_NO_HINTING;
|
||||
}
|
||||
|
||||
if (!context->useSbits) {
|
||||
/* Don't disable bitmaps when working with fixed-size glyph,
|
||||
* this is most probably a BGRA glyph */
|
||||
if (!context->useSbits && context->fixedSizeIndex == -1) {
|
||||
context->loadFlags |= FT_LOAD_NO_BITMAP;
|
||||
}
|
||||
|
||||
@@ -1459,14 +1707,18 @@ static jlong
|
||||
library = ftglyph->library;
|
||||
FT_Library_SetLcdFilter_Proxy(library, context->lcdFilter);
|
||||
|
||||
/* After call to FT_Render_Glyph, glyph format will be changed from
|
||||
* FT_GLYPH_FORMAT_OUTLINE to FT_GLYPH_FORMAT_BITMAP, so save this value */
|
||||
int outlineGlyph = ftglyph->format == FT_GLYPH_FORMAT_OUTLINE;
|
||||
|
||||
/* apply styles */
|
||||
if (context->doBold) { /* if bold style */
|
||||
if (context->doBold && outlineGlyph) { /* if bold style */
|
||||
GlyphSlot_Embolden(ftglyph, context->transform);
|
||||
}
|
||||
|
||||
/* generate bitmap if it is not done yet
|
||||
e.g. if algorithmic styling is performed and style was added to outline */
|
||||
if (renderImage && (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
|
||||
if (renderImage && outlineGlyph) {
|
||||
FT_BBox bbox;
|
||||
FT_Outline_Get_CBox(&(ftglyph->outline), &bbox);
|
||||
int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6));
|
||||
@@ -1481,17 +1733,39 @@ static jlong
|
||||
}
|
||||
}
|
||||
|
||||
FT_Fixed manualScale = context->fixedSizeIndex == -1 ? ftFixed1 : FT_DivFix(
|
||||
context->ptsz, scalerInfo->face->available_sizes[context->fixedSizeIndex].size);
|
||||
FT_Matrix manualTransform;
|
||||
FT_BBox manualTransformBoundingBox;
|
||||
if (renderImage) {
|
||||
width = (UInt16) ftglyph->bitmap.width;
|
||||
rowBytes = width;
|
||||
if (context->fixedSizeIndex == -1) {
|
||||
width = (UInt16) ftglyph->bitmap.width;
|
||||
height = (UInt16) ftglyph->bitmap.rows;
|
||||
} else {
|
||||
/* Fixed size glyph, prepare matrix and
|
||||
* bounding box for manual transformation */
|
||||
manualTransform.xx = FT_MulFix(context->transform.xx, manualScale);
|
||||
manualTransform.xy = FT_MulFix(context->transform.xy, manualScale);
|
||||
manualTransform.yx = FT_MulFix(context->transform.yx, manualScale);
|
||||
manualTransform.yy = FT_MulFix(context->transform.yy, manualScale);
|
||||
manualTransformBoundingBox =
|
||||
getTransformedBitmapBoundingBox(ftglyph, &manualTransform);
|
||||
width = (UInt16) (manualTransformBoundingBox.xMax -
|
||||
manualTransformBoundingBox.xMin);
|
||||
height = (UInt16) (manualTransformBoundingBox.yMax -
|
||||
manualTransformBoundingBox.yMin);
|
||||
}
|
||||
if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
|
||||
glyphInfo = getNullGlyphImage();
|
||||
return ptr_to_jlong(glyphInfo);
|
||||
}
|
||||
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
|
||||
rowBytes = PADBYTES + width + PADBYTES;
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
|
||||
rowBytes = width * 4;
|
||||
} else {
|
||||
rowBytes = width;
|
||||
}
|
||||
height = (UInt16) ftglyph->bitmap.rows;
|
||||
if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
|
||||
glyphInfo = getNullGlyphImage();
|
||||
return ptr_to_jlong(glyphInfo);
|
||||
}
|
||||
} else {
|
||||
width = 0;
|
||||
rowBytes = 0;
|
||||
@@ -1512,8 +1786,13 @@ static jlong
|
||||
glyphInfo->height = height;
|
||||
|
||||
if (renderImage) {
|
||||
glyphInfo->topLeftX = (float) ftglyph->bitmap_left;
|
||||
glyphInfo->topLeftY = (float) -ftglyph->bitmap_top;
|
||||
if (context->fixedSizeIndex == -1) {
|
||||
glyphInfo->topLeftX = (float) ftglyph->bitmap_left;
|
||||
glyphInfo->topLeftY = (float) -ftglyph->bitmap_top;
|
||||
} else {
|
||||
glyphInfo->topLeftX = manualTransformBoundingBox.xMin;
|
||||
glyphInfo->topLeftY = -manualTransformBoundingBox.yMax;
|
||||
}
|
||||
|
||||
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD && width > 0) {
|
||||
glyphInfo->width = width/3;
|
||||
@@ -1524,7 +1803,7 @@ static jlong
|
||||
}
|
||||
}
|
||||
|
||||
if (context->fmType == TEXT_FM_ON) {
|
||||
if (context->fmType == TEXT_FM_ON && outlineGlyph) {
|
||||
float advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
|
||||
glyphInfo->advanceX =
|
||||
(float) (advh * FTFixedToFloat(context->transform.xx));
|
||||
@@ -1533,15 +1812,17 @@ static jlong
|
||||
} else {
|
||||
if (!ftglyph->advance.y) {
|
||||
glyphInfo->advanceX =
|
||||
(float) ROUND(FT26Dot6ToFloat(ftglyph->advance.x));
|
||||
(float) ROUND(FT26Dot6ToFloat(FT_MulFix(ftglyph->advance.x, manualScale)));
|
||||
glyphInfo->advanceY = 0;
|
||||
} else if (!ftglyph->advance.x) {
|
||||
glyphInfo->advanceX = 0;
|
||||
glyphInfo->advanceY =
|
||||
(float) ROUND(FT26Dot6ToFloat(-ftglyph->advance.y));
|
||||
(float) ROUND(FT26Dot6ToFloat(-FT_MulFix(ftglyph->advance.y, manualScale)));
|
||||
} else {
|
||||
glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
|
||||
glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
|
||||
glyphInfo->advanceX = FT26Dot6ToFloat(
|
||||
FT_MulFix(ftglyph->advance.x, manualScale));
|
||||
glyphInfo->advanceY = FT26Dot6ToFloat(
|
||||
-FT_MulFix(ftglyph->advance.y, manualScale));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1551,46 +1832,65 @@ static jlong
|
||||
glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
|
||||
//convert result to output format
|
||||
//output format is either 3 bytes per pixel (for subpixel modes)
|
||||
//4 bytes per pixel for BGRA glyphs
|
||||
// or 1 byte per pixel for AA and B&W
|
||||
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
|
||||
/* convert from 8 pixels per byte to 1 byte per pixel */
|
||||
CopyBW2Grey8(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) glyphInfo->image,
|
||||
width,
|
||||
width,
|
||||
height);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||||
/* byte per pixel to byte per pixel => just copy */
|
||||
memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4) {
|
||||
/* 4 bits per pixel to byte per pixel */
|
||||
CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
|
||||
if (context->fixedSizeIndex == -1) {
|
||||
// Standard format convertation without image transformation
|
||||
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
|
||||
/* convert from 8 pixels per byte to 1 byte per pixel */
|
||||
CopyBW2Grey8(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) glyphInfo->image,
|
||||
width,
|
||||
width,
|
||||
height);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
|
||||
/* 3 bytes per pixel to 3 bytes per pixel */
|
||||
CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) (glyphInfo->image+PADBYTES),
|
||||
rowBytes,
|
||||
width,
|
||||
height);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
|
||||
/* 3 bytes per pixel to 3 bytes per pixel */
|
||||
CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) glyphInfo->image,
|
||||
width*3,
|
||||
width,
|
||||
height);
|
||||
glyphInfo->rowBytes *=3;
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||||
/* byte per pixel to byte per pixel => just copy */
|
||||
memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4) {
|
||||
/* 4 bits per pixel to byte per pixel */
|
||||
CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) glyphInfo->image,
|
||||
width,
|
||||
width,
|
||||
height);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
|
||||
/* 3 bytes per pixel to 3 bytes per pixel */
|
||||
CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) (glyphInfo->image+PADBYTES),
|
||||
rowBytes,
|
||||
width,
|
||||
height);
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
|
||||
/* 3 bytes per pixel to 3 bytes per pixel */
|
||||
CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
|
||||
ftglyph->bitmap.pitch,
|
||||
(void *) glyphInfo->image,
|
||||
width*3,
|
||||
width,
|
||||
height);
|
||||
glyphInfo->rowBytes *=3;
|
||||
} else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
|
||||
/* 4 bytes per pixel to 4 bytes per pixel => just copy */
|
||||
memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
|
||||
} else {
|
||||
free(glyphInfo);
|
||||
glyphInfo = getNullGlyphImage();
|
||||
}
|
||||
} else {
|
||||
free(glyphInfo);
|
||||
glyphInfo = getNullGlyphImage();
|
||||
// Here we have to transform image manually
|
||||
// Only BGRA format is supported (should be enough)
|
||||
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
|
||||
transformBGRABitmapGlyph(ftglyph, glyphInfo,
|
||||
&manualTransform,
|
||||
&manualTransformBoundingBox,
|
||||
context->aaType != TEXT_AA_OFF);
|
||||
} else {
|
||||
free(glyphInfo);
|
||||
glyphInfo = getNullGlyphImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,12 @@ import sun.java2d.xr.*;
|
||||
*/
|
||||
|
||||
public class XRGlyphCache implements GlyphDisposedListener {
|
||||
/**
|
||||
* BGRA glyphs are rendered as images
|
||||
* and therefore don't belong to any glyph set
|
||||
*/
|
||||
public static final int BGRA_GLYPH_SET = -1;
|
||||
|
||||
XRBackend con;
|
||||
XRCompositeManager maskBuffer;
|
||||
HashMap<MutableInteger, XRGlyphCacheEntry> cacheMap = new HashMap<MutableInteger, XRGlyphCacheEntry>(256);
|
||||
@@ -47,6 +53,7 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
|
||||
int grayGlyphSet;
|
||||
int lcdGlyphSet;
|
||||
final EnumMap<XRGlyphCacheEntry.Type, Integer> glyphSetsByType;
|
||||
|
||||
int time = 0;
|
||||
int cachedPixels = 0;
|
||||
@@ -63,6 +70,11 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
grayGlyphSet = con.XRenderCreateGlyphSet(XRUtils.PictStandardA8);
|
||||
lcdGlyphSet = con.XRenderCreateGlyphSet(XRUtils.PictStandardARGB32);
|
||||
|
||||
glyphSetsByType = new EnumMap<>(XRGlyphCacheEntry.Type.class);
|
||||
glyphSetsByType.put(XRGlyphCacheEntry.Type.GRAYSCALE, grayGlyphSet);
|
||||
glyphSetsByType.put(XRGlyphCacheEntry.Type.LCD, lcdGlyphSet);
|
||||
glyphSetsByType.put(XRGlyphCacheEntry.Type.BGRA, BGRA_GLYPH_SET);
|
||||
|
||||
StrikeCache.addGlyphDisposedListener(this);
|
||||
}
|
||||
|
||||
@@ -104,7 +116,7 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
return cacheMap.get(tmp);
|
||||
}
|
||||
|
||||
public XRGlyphCacheEntry[] cacheGlyphs(GlyphList glyphList) {
|
||||
public XRGlyphCacheEntry[] cacheGlyphs(GlyphList glyphList, int parentXid) {
|
||||
time++;
|
||||
|
||||
XRGlyphCacheEntry[] entries = new XRGlyphCacheEntry[glyphList.getNumGlyphs()];
|
||||
@@ -114,7 +126,8 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
for (int i = 0; i < glyphList.getNumGlyphs(); i++) {
|
||||
XRGlyphCacheEntry glyph;
|
||||
|
||||
if (imgPtrs[i] == 0L) {
|
||||
if (imgPtrs[i] == 0L ||
|
||||
imgPtrs[i] == StrikeCache.invisibleGlyphPtr) {
|
||||
continue;
|
||||
}
|
||||
// Find uncached glyphs and queue them for upload
|
||||
@@ -124,7 +137,7 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
cacheMap.put(new MutableInteger(glyph.getGlyphID()), glyph);
|
||||
|
||||
if (uncachedGlyphs == null) {
|
||||
uncachedGlyphs = new ArrayList<XRGlyphCacheEntry>();
|
||||
uncachedGlyphs = new ArrayList<>();
|
||||
}
|
||||
uncachedGlyphs.add(glyph);
|
||||
}
|
||||
@@ -134,13 +147,15 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
|
||||
// Add glyphs to cache
|
||||
if (uncachedGlyphs != null) {
|
||||
uploadGlyphs(entries, uncachedGlyphs, glyphList, null);
|
||||
uploadGlyphs(entries, uncachedGlyphs, glyphList, parentXid);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
protected void uploadGlyphs(XRGlyphCacheEntry[] glyphs, ArrayList<XRGlyphCacheEntry> uncachedGlyphs, GlyphList gl, int[] glIndices) {
|
||||
protected void uploadGlyphs(XRGlyphCacheEntry[] glyphs,
|
||||
ArrayList<XRGlyphCacheEntry> uncachedGlyphs,
|
||||
GlyphList gl, int parentXid) {
|
||||
for (XRGlyphCacheEntry glyph : uncachedGlyphs) {
|
||||
cachedPixels += glyph.getPixelCnt();
|
||||
}
|
||||
@@ -149,67 +164,59 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
clearCache(glyphs);
|
||||
}
|
||||
|
||||
boolean containsLCDGlyphs = containsLCDGlyphs(uncachedGlyphs);
|
||||
List<XRGlyphCacheEntry>[] seperatedGlyphList = seperateGlyphTypes(uncachedGlyphs, containsLCDGlyphs);
|
||||
List<XRGlyphCacheEntry> grayGlyphList = seperatedGlyphList[0];
|
||||
List<XRGlyphCacheEntry> lcdGlyphList = seperatedGlyphList[1];
|
||||
EnumMap<XRGlyphCacheEntry.Type, List<XRGlyphCacheEntry>>
|
||||
glyphListsByType = separateGlyphTypes(uncachedGlyphs);
|
||||
|
||||
uploadGlyphs(grayGlyphSet, gl,
|
||||
glyphListsByType.get(XRGlyphCacheEntry.Type.GRAYSCALE));
|
||||
uploadGlyphs(lcdGlyphSet, gl,
|
||||
glyphListsByType.get(XRGlyphCacheEntry.Type.LCD));
|
||||
List<XRGlyphCacheEntry> bgraGlyphs = glyphListsByType.getOrDefault(
|
||||
XRGlyphCacheEntry.Type.BGRA, List.of());
|
||||
if (!bgraGlyphs.isEmpty()) {
|
||||
con.addBGRAGlyphImages(parentXid, bgraGlyphs);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadGlyphs(int glyphSet, GlyphList glyphList,
|
||||
List<XRGlyphCacheEntry> cacheEntries) {
|
||||
if (cacheEntries == null || cacheEntries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Some XServers crash when uploading multiple glyphs at once. TODO:
|
||||
* Implement build-switch in local case for distributors who know their
|
||||
* XServer is fixed
|
||||
*/
|
||||
if (batchGlyphUpload) {
|
||||
if (grayGlyphList != null && grayGlyphList.size() > 0) {
|
||||
con.XRenderAddGlyphs(grayGlyphSet, gl, grayGlyphList, generateGlyphImageStream(grayGlyphList));
|
||||
}
|
||||
if (lcdGlyphList != null && lcdGlyphList.size() > 0) {
|
||||
con.XRenderAddGlyphs(lcdGlyphSet, gl, lcdGlyphList, generateGlyphImageStream(lcdGlyphList));
|
||||
}
|
||||
con.XRenderAddGlyphs(glyphSet, glyphList, cacheEntries,
|
||||
generateGlyphImageStream(cacheEntries));
|
||||
} else {
|
||||
ArrayList<XRGlyphCacheEntry> tmpList = new ArrayList<XRGlyphCacheEntry>(1);
|
||||
ArrayList<XRGlyphCacheEntry> tmpList = new ArrayList<>(1);
|
||||
tmpList.add(null);
|
||||
|
||||
for (XRGlyphCacheEntry entry : uncachedGlyphs) {
|
||||
for (XRGlyphCacheEntry entry : cacheEntries) {
|
||||
tmpList.set(0, entry);
|
||||
|
||||
if (entry.getGlyphSet() == grayGlyphSet) {
|
||||
con.XRenderAddGlyphs(grayGlyphSet, gl, tmpList, generateGlyphImageStream(tmpList));
|
||||
} else {
|
||||
con.XRenderAddGlyphs(lcdGlyphSet, gl, tmpList, generateGlyphImageStream(tmpList));
|
||||
}
|
||||
con.XRenderAddGlyphs(glyphSet, glyphList, tmpList,
|
||||
generateGlyphImageStream(tmpList));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seperates lcd and grayscale glyphs queued for upload, and sets the
|
||||
* appropriate glyphset for the cache entries.
|
||||
* Separates bgra, lcd and grayscale glyphs queued for upload, and sets the
|
||||
* appropriate glyph set for the cache entries.
|
||||
*/
|
||||
protected List<XRGlyphCacheEntry>[] seperateGlyphTypes(List<XRGlyphCacheEntry> glyphList, boolean containsLCDGlyphs) {
|
||||
ArrayList<XRGlyphCacheEntry> lcdGlyphs = null;
|
||||
ArrayList<XRGlyphCacheEntry> grayGlyphs = null;
|
||||
|
||||
protected EnumMap<XRGlyphCacheEntry.Type, List<XRGlyphCacheEntry>>
|
||||
separateGlyphTypes(List<XRGlyphCacheEntry> glyphList) {
|
||||
EnumMap<XRGlyphCacheEntry.Type, List<XRGlyphCacheEntry>> glyphLists =
|
||||
new EnumMap<>(XRGlyphCacheEntry.Type.class);
|
||||
for (XRGlyphCacheEntry cacheEntry : glyphList) {
|
||||
if (cacheEntry.isGrayscale(containsLCDGlyphs)) {
|
||||
if (grayGlyphs == null) {
|
||||
grayGlyphs = new ArrayList<>(glyphList.size());
|
||||
}
|
||||
cacheEntry.setGlyphSet(grayGlyphSet);
|
||||
grayGlyphs.add(cacheEntry);
|
||||
} else {
|
||||
if (lcdGlyphs == null) {
|
||||
lcdGlyphs = new ArrayList<>(glyphList.size());
|
||||
}
|
||||
cacheEntry.setGlyphSet(lcdGlyphSet);
|
||||
lcdGlyphs.add(cacheEntry);
|
||||
}
|
||||
XRGlyphCacheEntry.Type cacheEntryType = cacheEntry.getType();
|
||||
cacheEntry.setGlyphSet(glyphSetsByType.get(cacheEntryType));
|
||||
glyphLists.computeIfAbsent(cacheEntryType, ignore ->
|
||||
new ArrayList<>(glyphList.size())).add(cacheEntry);
|
||||
}
|
||||
// Arrays and generics don't play well together
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
List<XRGlyphCacheEntry>[] tmp =
|
||||
(List<XRGlyphCacheEntry>[]) (new List[] { grayGlyphs, lcdGlyphs });
|
||||
return tmp;
|
||||
return glyphLists;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,20 +233,7 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
protected boolean containsLCDGlyphs(List<XRGlyphCacheEntry> entries) {
|
||||
boolean containsLCDGlyphs = false;
|
||||
|
||||
for (XRGlyphCacheEntry entry : entries) {
|
||||
containsLCDGlyphs = !(entry.getSourceRowBytes() == entry.getWidth());
|
||||
|
||||
if (containsLCDGlyphs) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void clearCache(XRGlyphCacheEntry[] glyps) {
|
||||
protected void clearCache(XRGlyphCacheEntry[] glyphs) {
|
||||
/*
|
||||
* Glyph uploading is so slow anyway, we can afford some inefficiency
|
||||
* here, as the cache should usually be quite small. TODO: Implement
|
||||
@@ -252,8 +246,10 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
}
|
||||
});
|
||||
|
||||
for (XRGlyphCacheEntry glyph : glyps) {
|
||||
glyph.setPinned();
|
||||
for (XRGlyphCacheEntry glyph : glyphs) {
|
||||
if (glyph != null) {
|
||||
glyph.setPinned();
|
||||
}
|
||||
}
|
||||
|
||||
GrowableIntArray deleteGlyphList = new GrowableIntArray(1, 10);
|
||||
@@ -268,8 +264,10 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
}
|
||||
}
|
||||
|
||||
for (XRGlyphCacheEntry glyph : glyps) {
|
||||
glyph.setUnpinned();
|
||||
for (XRGlyphCacheEntry glyph : glyphs) {
|
||||
if (glyph != null) {
|
||||
glyph.setUnpinned();
|
||||
}
|
||||
}
|
||||
|
||||
freeGlyphs(deleteGlyphList);
|
||||
@@ -278,6 +276,8 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
private void freeGlyphs(GrowableIntArray glyphIdList) {
|
||||
GrowableIntArray removedLCDGlyphs = new GrowableIntArray(1, 10);
|
||||
GrowableIntArray removedGrayscaleGlyphs = new GrowableIntArray(1, 10);
|
||||
long[] removedBGRAGlyphPtrs = null;
|
||||
int removedBGRAGlyphPtrsCount = 0;
|
||||
|
||||
for (int i=0; i < glyphIdList.getSize(); i++) {
|
||||
int glyphId = glyphIdList.getInt(i);
|
||||
@@ -290,8 +290,19 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
|
||||
if (entry.getGlyphSet() == grayGlyphSet) {
|
||||
removedGrayscaleGlyphs.addInt(glyphId);
|
||||
} else {
|
||||
} else if (entry.getGlyphSet() == lcdGlyphSet) {
|
||||
removedLCDGlyphs.addInt(glyphId);
|
||||
} else if (entry.getGlyphSet() == BGRA_GLYPH_SET) {
|
||||
if (removedBGRAGlyphPtrs == null) {
|
||||
removedBGRAGlyphPtrs = new long[10];
|
||||
} else if (removedBGRAGlyphPtrsCount >= removedBGRAGlyphPtrs.length) {
|
||||
long[] n = new long[removedBGRAGlyphPtrs.length * 2];
|
||||
System.arraycopy(removedBGRAGlyphPtrs, 0, n, 0,
|
||||
removedBGRAGlyphPtrs.length);
|
||||
removedBGRAGlyphPtrs = n;
|
||||
}
|
||||
removedBGRAGlyphPtrs[removedBGRAGlyphPtrsCount++] =
|
||||
entry.getBgraGlyphInfoPtr();
|
||||
}
|
||||
|
||||
entry.setGlyphID(0);
|
||||
@@ -304,5 +315,10 @@ public class XRGlyphCache implements GlyphDisposedListener {
|
||||
if (removedLCDGlyphs.getSize() > 0) {
|
||||
con.XRenderFreeGlyphs(lcdGlyphSet, removedLCDGlyphs.getSizedArray());
|
||||
}
|
||||
|
||||
if (removedBGRAGlyphPtrsCount > 0) {
|
||||
con.freeBGRAGlyphImages(removedBGRAGlyphPtrs,
|
||||
removedBGRAGlyphPtrsCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import java.io.*;
|
||||
*/
|
||||
|
||||
public class XRGlyphCacheEntry {
|
||||
long glyphInfoPtr;
|
||||
long glyphInfoPtr, bgraGlyphInfoPtr;
|
||||
|
||||
int lastUsed;
|
||||
boolean pinned;
|
||||
@@ -47,11 +47,19 @@ public class XRGlyphCacheEntry {
|
||||
public XRGlyphCacheEntry(long glyphInfoPtr, GlyphList gl) {
|
||||
this.glyphInfoPtr = glyphInfoPtr;
|
||||
|
||||
/* TODO: Does it make sence to cache results? */
|
||||
/* TODO: Does it make sense to cache results? */
|
||||
xOff = Math.round(getXAdvance());
|
||||
yOff = Math.round(getYAdvance());
|
||||
}
|
||||
|
||||
public long getBgraGlyphInfoPtr() {
|
||||
return bgraGlyphInfoPtr;
|
||||
}
|
||||
|
||||
public void setBgraGlyphInfoPtr(long bgraGlyphInfoPtr) {
|
||||
this.bgraGlyphInfoPtr = bgraGlyphInfoPtr;
|
||||
}
|
||||
|
||||
public int getXOff() {
|
||||
return xOff;
|
||||
}
|
||||
@@ -132,9 +140,9 @@ public class XRGlyphCacheEntry {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int rowBytes = getSourceRowBytes();
|
||||
int paddedWidth = getPaddedWidth(uploadAsLCD);
|
||||
int paddedWidth = getPaddedWidth();
|
||||
|
||||
if (!uploadAsLCD) {
|
||||
if (getType() == Type.GRAYSCALE) {
|
||||
for (int line = 0; line < height; line++) {
|
||||
for(int x = 0; x < paddedWidth; x++) {
|
||||
if(x < width) {
|
||||
@@ -176,22 +184,27 @@ public class XRGlyphCacheEntry {
|
||||
return glyphInfoPtr;
|
||||
}
|
||||
|
||||
public boolean isGrayscale(boolean listContainsLCDGlyphs) {
|
||||
return getSourceRowBytes() == getWidth() && !(getWidth() == 0 && getHeight() == 0 && listContainsLCDGlyphs);
|
||||
}
|
||||
|
||||
public int getPaddedWidth(boolean listContainsLCDGlyphs) {
|
||||
public Type getType() {
|
||||
int rowBytes = getSourceRowBytes();
|
||||
int width = getWidth();
|
||||
return isGrayscale(listContainsLCDGlyphs) ? (int) Math.ceil(width / 4.0) * 4 : width;
|
||||
// 0x0 -> LCD is just for backward compatibiity
|
||||
if (width == 0 || getHeight() == 0) return Type.LCD;
|
||||
if (width == rowBytes) return Type.GRAYSCALE;
|
||||
if (width * 4 == rowBytes) return Type.BGRA;
|
||||
return Type.LCD;
|
||||
}
|
||||
|
||||
public int getDestinationRowBytes(boolean listContainsLCDGlyphs) {
|
||||
boolean grayscale = isGrayscale(listContainsLCDGlyphs);
|
||||
return grayscale ? getPaddedWidth(grayscale) : getWidth() * 4;
|
||||
public int getPaddedWidth() {
|
||||
return getType() == Type.GRAYSCALE ?
|
||||
(int) Math.ceil(getWidth() / 4.0) * 4 : getWidth();
|
||||
}
|
||||
|
||||
public int getGlyphDataLenth(boolean listContainsLCDGlyphs) {
|
||||
return getDestinationRowBytes(listContainsLCDGlyphs) * getHeight();
|
||||
public int getDestinationRowBytes() {
|
||||
return getType() == Type.GRAYSCALE ? getPaddedWidth() : getWidth() * 4;
|
||||
}
|
||||
|
||||
public int getGlyphDataLenth() {
|
||||
return getDestinationRowBytes() * getHeight();
|
||||
}
|
||||
|
||||
public void setPinned() {
|
||||
@@ -217,4 +230,13 @@ public class XRGlyphCacheEntry {
|
||||
public boolean isPinned() {
|
||||
return pinned;
|
||||
}
|
||||
|
||||
|
||||
public enum Type {
|
||||
GRAYSCALE,
|
||||
LCD,
|
||||
BGRA
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -83,13 +83,20 @@ public class XRTextRenderer extends GlyphListPipe {
|
||||
advY += 0.5f;
|
||||
}
|
||||
|
||||
XRGlyphCacheEntry[] cachedGlyphs = glyphCache.cacheGlyphs(gl);
|
||||
XRGlyphCacheEntry[] cachedGlyphs =
|
||||
glyphCache.cacheGlyphs(gl, x11sd.getXid());
|
||||
boolean containsLCDGlyphs = false;
|
||||
int activeGlyphSet = cachedGlyphs[0].getGlyphSet();
|
||||
/* Do not initialize it to cachedGlyphs[0].getGlyphSet(),
|
||||
* as it may cause NPE */
|
||||
int activeGlyphSet = 0;
|
||||
|
||||
int eltIndex = -1;
|
||||
gl.startGlyphIteration();
|
||||
float[] positions = gl.getPositions();
|
||||
/* Accumulated advances are used to adjust glyph positions
|
||||
* when mixing BGRA and standard glyphs as they have
|
||||
* completely different methods of rendering. */
|
||||
float accumulatedXEltAdvanceX = 0, accumulatedXEltAdvanceY = 0;
|
||||
for (int i = 0; i < gl.getNumGlyphs(); i++) {
|
||||
gl.setGlyphIndex(i);
|
||||
XRGlyphCacheEntry cacheEntry = cachedGlyphs[i];
|
||||
@@ -97,9 +104,19 @@ public class XRTextRenderer extends GlyphListPipe {
|
||||
continue;
|
||||
}
|
||||
|
||||
eltList.getGlyphs().addInt(cacheEntry.getGlyphID());
|
||||
int glyphSet = cacheEntry.getGlyphSet();
|
||||
|
||||
if (glyphSet == XRGlyphCache.BGRA_GLYPH_SET) {
|
||||
/* BGRA glyphs store pointers to BGRAGlyphInfo
|
||||
* struct instead of glyph index */
|
||||
eltList.getGlyphs().addInt(
|
||||
(int) (cacheEntry.getBgraGlyphInfoPtr() >> 32));
|
||||
eltList.getGlyphs().addInt(
|
||||
(int) cacheEntry.getBgraGlyphInfoPtr());
|
||||
} else {
|
||||
eltList.getGlyphs().addInt(cacheEntry.getGlyphID());
|
||||
}
|
||||
|
||||
containsLCDGlyphs |= (glyphSet == glyphCache.lcdGlyphSet);
|
||||
|
||||
int posX = 0, posY = 0;
|
||||
@@ -108,7 +125,11 @@ public class XRTextRenderer extends GlyphListPipe {
|
||||
|| cacheEntry.getYAdvance() != ((float) cacheEntry.getYOff())
|
||||
|| glyphSet != activeGlyphSet
|
||||
|| eltIndex < 0
|
||||
|| eltList.getCharCnt(eltIndex) == MAX_ELT_GLYPH_COUNT) {
|
||||
/* We don't care about number of glyphs when
|
||||
* rendering BGRA glyphs because they are not rendered
|
||||
* using XRenderCompositeText. */
|
||||
|| (glyphSet != XRGlyphCache.BGRA_GLYPH_SET &&
|
||||
eltList.getCharCnt(eltIndex) == MAX_ELT_GLYPH_COUNT)) {
|
||||
|
||||
eltIndex = eltList.getNextIndex();
|
||||
eltList.setCharCnt(eltIndex, 1);
|
||||
@@ -141,17 +162,31 @@ public class XRTextRenderer extends GlyphListPipe {
|
||||
advY += (cacheEntry.getYAdvance() - cacheEntry.getYOff());
|
||||
}
|
||||
|
||||
// Offset of the current glyph is the difference
|
||||
// to the last glyph and this one
|
||||
eltList.setXOff(eltIndex, (posX - oldPosX));
|
||||
eltList.setYOff(eltIndex, (posY - oldPosY));
|
||||
|
||||
oldPosX = posX;
|
||||
oldPosY = posY;
|
||||
if (glyphSet == XRGlyphCache.BGRA_GLYPH_SET) {
|
||||
// BGRA glyphs use absolute positions
|
||||
eltList.setXOff(eltIndex,
|
||||
(int) (accumulatedXEltAdvanceX + posX));
|
||||
eltList.setYOff(eltIndex,
|
||||
(int) (accumulatedXEltAdvanceY + posY));
|
||||
} else {
|
||||
// Offset of the current glyph is the difference
|
||||
// to the last glyph and this one
|
||||
eltList.setXOff(eltIndex, (posX - oldPosX));
|
||||
eltList.setYOff(eltIndex, (posY - oldPosY));
|
||||
oldPosX = posX;
|
||||
oldPosY = posY;
|
||||
}
|
||||
|
||||
} else {
|
||||
eltList.setCharCnt(eltIndex, eltList.getCharCnt(eltIndex) + 1);
|
||||
}
|
||||
if (glyphSet == XRGlyphCache.BGRA_GLYPH_SET) {
|
||||
advX += cacheEntry.getXAdvance();
|
||||
advY += cacheEntry.getYAdvance();
|
||||
} else {
|
||||
accumulatedXEltAdvanceX += cacheEntry.getXAdvance();
|
||||
accumulatedXEltAdvanceY += cacheEntry.getYAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
int maskFormat = containsLCDGlyphs ? XRUtils.PictStandardARGB32 : XRUtils.PictStandardA8;
|
||||
|
||||
@@ -94,6 +94,11 @@ public interface XRBackend {
|
||||
|
||||
public void XRenderFreeGlyphs(int glyphSet, int[] gids);
|
||||
|
||||
public void addBGRAGlyphImages(int drawable,
|
||||
List<XRGlyphCacheEntry> cacheEntries);
|
||||
|
||||
public void freeBGRAGlyphImages(long[] glyphInfoPointers, int glyphCount);
|
||||
|
||||
public void XRenderCompositeText(byte op, int src, int dst,
|
||||
int maskFormatID,
|
||||
int xSrc, int ySrc, int xDst, int yDst,
|
||||
|
||||
@@ -250,6 +250,26 @@ public class XRBackendNative implements XRBackend {
|
||||
private static native void XRFreeGlyphsNative(int glyphSet,
|
||||
int[] gids, int idCnt);
|
||||
|
||||
public void addBGRAGlyphImages(int drawable,
|
||||
List<XRGlyphCacheEntry> cacheEntries) {
|
||||
long[] glyphInfoPtrs = getGlyphInfoPtrs(cacheEntries);
|
||||
addBGRAGlyphImagesNative(drawable, glyphInfoPtrs,
|
||||
glyphInfoPtrs.length, FMTPTR_ARGB32);
|
||||
/* addBGRAGlyphImagesNative replaced values in
|
||||
* glyphInfoPtrs with pointers to BGRAGlyphInfo structs, save them */
|
||||
int i = 0;
|
||||
for (XRGlyphCacheEntry cacheEntry : cacheEntries) {
|
||||
cacheEntry.setBgraGlyphInfoPtr(glyphInfoPtrs[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
private native void addBGRAGlyphImagesNative(int drawable,
|
||||
long[] glyphInfoPtrs,
|
||||
int glyphCnt, long format32);
|
||||
|
||||
public native void freeBGRAGlyphImages(long[] glyphInfoPointers,
|
||||
int glyphCount);
|
||||
|
||||
private static native void
|
||||
XRenderCompositeTextNative(int op, int src, int dst,
|
||||
int srcX, int srcY, long maskFormat,
|
||||
|
||||
@@ -59,6 +59,14 @@ typedef struct _XRadialGradient {
|
||||
} XRadialGradient;
|
||||
#endif
|
||||
|
||||
/* BGRA glyph that is rendered using XRenderComposite instead of
|
||||
* XRenderCompositeText32. Used for colored glyphs */
|
||||
typedef struct _BGRAGlyphInfo {
|
||||
GlyphInfo* glyphInfo;
|
||||
Pixmap pixmap;
|
||||
Picture picture;
|
||||
} BGRAGlyphInfo;
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define BUILD_TRANSFORM_MATRIX(TRANSFORM, M00, M01, M02, M10, M11, M12) \
|
||||
@@ -879,6 +887,74 @@ Java_sun_java2d_xr_XRBackendNative_XRFreeGlyphsNative
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_xr_XRBackendNative_addBGRAGlyphImagesNative
|
||||
(JNIEnv* env, jclass clazz, jint drawable,
|
||||
jlongArray javaGlyphInfoPointersArray, jint glyphCnt, jlong format32) {
|
||||
jlong* glyphInfoPointers;
|
||||
if ((glyphInfoPointers = (jlong *)
|
||||
(*env)->GetPrimitiveArrayCritical(env, javaGlyphInfoPointersArray, NULL)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
XRenderPictFormat* format = (XRenderPictFormat*) jlong_to_ptr(format32);
|
||||
XRenderPictureAttributes pictureAttributes;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < glyphCnt; i++) {
|
||||
GlyphInfo* glyphInfo = (GlyphInfo*) jlong_to_ptr(glyphInfoPointers[i]);
|
||||
|
||||
Pixmap pixmap = XCreatePixmap(awt_display, (Drawable) drawable,
|
||||
glyphInfo->width, glyphInfo->height, 32);
|
||||
GC gc = XCreateGC(awt_display, (Drawable) pixmap, 0L, NULL);
|
||||
XImage* image = XCreateImage(awt_display, NULL, 32, ZPixmap, 0,
|
||||
(char*) glyphInfo->image,
|
||||
glyphInfo->width, glyphInfo->height,
|
||||
32, glyphInfo->rowBytes);
|
||||
XPutImage(awt_display, pixmap, gc, image, 0, 0, 0, 0,
|
||||
glyphInfo->width, glyphInfo->height);
|
||||
image->data = NULL;
|
||||
XDestroyImage(image);
|
||||
XFreeGC(awt_display, gc);
|
||||
Picture picture = XRenderCreatePicture(awt_display, pixmap, format,
|
||||
0, &pictureAttributes);
|
||||
|
||||
BGRAGlyphInfo* bgraGlyphInfo =
|
||||
(BGRAGlyphInfo*) malloc(sizeof(BGRAGlyphInfo));
|
||||
bgraGlyphInfo->glyphInfo = glyphInfo;
|
||||
bgraGlyphInfo->pixmap = pixmap;
|
||||
bgraGlyphInfo->picture = picture;
|
||||
|
||||
glyphInfoPointers[i] = (jlong) bgraGlyphInfo;
|
||||
}
|
||||
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, javaGlyphInfoPointersArray,
|
||||
glyphInfoPointers, JNI_ABORT);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_xr_XRBackendNative_freeBGRAGlyphImages
|
||||
(JNIEnv* env, jclass clazz,
|
||||
jlongArray javaGlyphInfoPointersArray, jint glyphCnt) {
|
||||
jlong* glyphInfoPointers;
|
||||
if ((glyphInfoPointers = (jlong *)
|
||||
(*env)->GetPrimitiveArrayCritical(env, javaGlyphInfoPointersArray, NULL)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < glyphCnt; i++) {
|
||||
BGRAGlyphInfo* bgraGlyphInfo =
|
||||
(BGRAGlyphInfo*) jlong_to_ptr(glyphInfoPointers[i]);
|
||||
XRenderFreePicture(awt_display, bgraGlyphInfo->picture);
|
||||
XFreePixmap(awt_display, bgraGlyphInfo->pixmap);
|
||||
free(bgraGlyphInfo);
|
||||
}
|
||||
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, javaGlyphInfoPointersArray,
|
||||
glyphInfoPointers, JNI_ABORT);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_java2d_xr_XRBackendNative_XRenderCreateGlyphSetNative
|
||||
(JNIEnv *env, jclass cls, jlong format) {
|
||||
@@ -965,19 +1041,48 @@ Java_sun_java2d_xr_XRBackendNative_XRenderCompositeTextNative
|
||||
xids[i] = ids[i];
|
||||
}
|
||||
|
||||
for (i=0; i < eltCnt; i++) {
|
||||
xelts[i].nchars = elts[i*4 + 0];
|
||||
xelts[i].xOff = elts[i*4 + 1];
|
||||
xelts[i].yOff = elts[i*4 + 2];
|
||||
xelts[i].glyphset = (GlyphSet) elts[i*4 + 3];
|
||||
xelts[i].chars = &xids[charCnt];
|
||||
|
||||
charCnt += xelts[i].nchars;
|
||||
int totalXElts = 0;
|
||||
for (i = 0; i < eltCnt; i++) {
|
||||
int nchars = elts[i*4];
|
||||
int xOff = elts[i*4 + 1];
|
||||
int yOff = elts[i*4 + 2];
|
||||
int glyphset = (GlyphSet) elts[i*4 + 3];
|
||||
if (glyphset == -1) { // BGRA glyph, render as image
|
||||
float x = (float) xOff;
|
||||
float y = (float) yOff;
|
||||
int ch;
|
||||
for (ch = 0; ch < nchars; ch++) {
|
||||
BGRAGlyphInfo* bgraGlyphInfo = (BGRAGlyphInfo*)
|
||||
(((jlong) xids[charCnt + ch * 2] << 32) |
|
||||
(((jlong) xids[charCnt + ch * 2 + 1]) & 0xFFFFFFFF));
|
||||
GlyphInfo* glyph = bgraGlyphInfo->glyphInfo;
|
||||
XRenderComposite(awt_display, PictOpOver,
|
||||
bgraGlyphInfo->picture,
|
||||
(Picture) 0, (Picture) dst,
|
||||
0, 0, 0, 0,
|
||||
(int) (x + glyph->topLeftX),
|
||||
(int) (y + glyph->topLeftY),
|
||||
glyph->width, glyph->height);
|
||||
x += glyph->advanceX;
|
||||
y += glyph->advanceY;
|
||||
}
|
||||
charCnt += nchars * 2;
|
||||
} else { // Standard XRender glyph
|
||||
xelts[totalXElts].nchars = nchars;
|
||||
xelts[totalXElts].xOff = xOff;
|
||||
xelts[totalXElts].yOff = yOff;
|
||||
xelts[totalXElts].glyphset = glyphset;
|
||||
xelts[totalXElts].chars = &xids[charCnt];
|
||||
charCnt += nchars;
|
||||
totalXElts++;
|
||||
}
|
||||
}
|
||||
|
||||
XRenderCompositeText32(awt_display, op, (Picture) src, (Picture) dst,
|
||||
(XRenderPictFormat *) jlong_to_ptr(maskFmt),
|
||||
sx, sy, 0, 0, xelts, eltCnt);
|
||||
if (totalXElts > 0) {
|
||||
XRenderCompositeText32(awt_display, op, (Picture) src, (Picture) dst,
|
||||
(XRenderPictFormat *) jlong_to_ptr(maskFmt),
|
||||
sx, sy, 0, 0, xelts, totalXElts);
|
||||
}
|
||||
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, glyphIDArray, ids, JNI_ABORT);
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, eltArray, elts, JNI_ABORT);
|
||||
|
||||
Reference in New Issue
Block a user