JBR-410 Added emoji support for Linux

This commit is contained in:
Nikita Gubarkov
2021-06-02 09:19:28 +03:00
committed by Vitaly Provodin
parent e4ce4ce9c2
commit fd0d59b9cb
8 changed files with 691 additions and 188 deletions

View File

@@ -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) {

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);