JBR-2917 Added emoji support for Windows

JBR-3951 Pass real glyph type from native code instead of guessing it by rowBytes & width
This commit is contained in:
Nikita Gubarkov
2021-11-02 14:40:41 +03:00
committed by jbrbot
parent cf2318c99e
commit 16032b1aa7
17 changed files with 106 additions and 77 deletions

View File

@@ -627,6 +627,7 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
glyphInfo->cellInfo = NULL;
glyphInfo->subpixelResolutionX = subpixelResX;
glyphInfo->subpixelResolutionY = subpixelResY;
glyphInfo->format = (UInt8) pixelSize;
#ifdef USE_IMAGE_ALIGNED_MEMORY
glyphInfo->image = image;

View File

@@ -713,7 +713,7 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
J2dTraceLn2(J2D_TRACE_INFO, "Glyph width = %d height = %d", ginfo->width, ginfo->height);
J2dTraceLn1(J2D_TRACE_INFO, "rowBytes = %d", ginfo->rowBytes);
if (ginfo->rowBytes == ginfo->width) {
if (ginfo->format == sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE) {
// grayscale or monochrome glyph data
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
@@ -724,7 +724,7 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale no cache");
ok = MTLTR_DrawGrayscaleGlyphNoCache(mtlc, ginfo, x, y, dstOps);
}
} else if (ginfo->rowBytes == ginfo->width * 4) {
} else if (ginfo->format == sun_font_StrikeCache_PIXEL_FORMAT_BGRA) {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList color glyph no cache");
ok = MTLTR_DrawColorGlyphNoCache(mtlc, ginfo, x, y, dstOps);
flushBeforeLCD = JNI_FALSE;

View File

@@ -307,7 +307,8 @@ public class FileFontStrike extends PhysicalStrike {
intPtSize = (int) pts;
useNatives = (rotation == 0 || rotation > 0 && useNativesForRotatedText) && pts >= 3.0 && pts <= 100.0 &&
(getImageWithAdvance || desc.fmHint == INTVAL_FRACTIONALMETRICS_ON) &&
!((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize);
!((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize) &&
!((TrueTypeFont)fileFont).hasCOLRTable();
}
if (FontUtilities.isLogging() && FontUtilities.isWindows) {
FontUtilities.logInfo("Strike for " + fileFont + " at size = " + intPtSize +
@@ -839,15 +840,13 @@ public class FileFontStrike extends PhysicalStrike {
private int getGlyphImageMinX(long ptr, int origMinX) {
int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);
byte format = StrikeCache.unsafe.getByte(ptr+StrikeCache.formatOffset);
if (format != StrikeCache.PIXEL_FORMAT_LCD) return origMinX;
int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);
int rowBytes =
StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);
if (rowBytes == width) {
return origMinX;
}
long pixelData =
StrikeCache.unsafe.getAddress(ptr + StrikeCache.pixelDataOffset);

View File

@@ -502,15 +502,20 @@ public final class GlyphList {
}
public static boolean canContainColorGlyphs() {
return FontUtilities.isMacOSX || FontUtilities.isLinux;
return true;
}
/**
* @return {@link StrikeCache#PIXEL_FORMAT_GREYSCALE} for greyscale,
* {@link StrikeCache#PIXEL_FORMAT_LCD} for LCD and {@link StrikeCache#PIXEL_FORMAT_BGRA} for BGRA glyph
*/
public byte getPixelFormat(int glyphIndex) {
return StrikeCache.unsafe.getByte(images[glyphIndex] +
StrikeCache.formatOffset);
}
public boolean isColorGlyph(int glyphIndex) {
int width = StrikeCache.unsafe.getChar(images[glyphIndex] +
StrikeCache.widthOffset);
int rowBytes = StrikeCache.unsafe.getChar(images[glyphIndex] +
StrikeCache.rowBytesOffset);
return rowBytes == width * 4;
return getPixelFormat(glyphIndex) == StrikeCache.PIXEL_FORMAT_BGRA;
}
public SurfaceData getColorGlyphData() {

View File

@@ -27,6 +27,7 @@ package sun.font;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.lang.annotation.Native;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
@@ -116,8 +117,14 @@ public final class StrikeCache {
static int managedOffset;
static int subpixelResolutionXOffset;
static int subpixelResolutionYOffset;
static int formatOffset;
static long invisibleGlyphPtr;
@Native public static final byte PIXEL_FORMAT_UNKNOWN = -1;
@Native public static final byte PIXEL_FORMAT_GREYSCALE = 1;
@Native public static final byte PIXEL_FORMAT_LCD = 3;
@Native public static final byte PIXEL_FORMAT_BGRA = 4;
/* Native method used to return information used for unsafe
* access to native data.
* return values as follows:-
@@ -136,6 +143,7 @@ public final class StrikeCache {
* arr[12] = offset of managed
* arr[13] = offset of subpixelResolutionX
* arr[14] = offset of subpixelResolutionY
* arr[15] = offset of format
*/
static native void getGlyphCacheDescription(long[] infoArray);
@@ -146,7 +154,7 @@ public final class StrikeCache {
@SuppressWarnings("removal")
private static void initStatic() {
long[] nativeInfo = new long[15];
long[] nativeInfo = new long[16];
getGlyphCacheDescription(nativeInfo);
//Can also get address size from Unsafe class :-
//nativeAddressSize = unsafe.addressSize();
@@ -165,6 +173,7 @@ public final class StrikeCache {
managedOffset = (int) nativeInfo[12];
subpixelResolutionXOffset = (int) nativeInfo[13];
subpixelResolutionYOffset = (int) nativeInfo[14];
formatOffset = (int) nativeInfo[15];
if (nativeAddressSize < 4) {
throw new InternalError("Unexpected address size for font data: " +

View File

@@ -97,6 +97,7 @@ public class TrueTypeFont extends FileFont {
public static final int v1ttTag = 0x00010000; // 'v1tt' - Version 1 TT font
public static final int trueTag = 0x74727565; // 'true' - Version 2 TT font
public static final int ottoTag = 0x4f54544f; // 'otto' - OpenType font
public static final int COLRTag = 0x434f4c52; // 'COLR'
/* -- ID's used in the 'name' table */
public static final int MAC_PLATFORM_ID = 1;
@@ -884,6 +885,10 @@ public class TrueTypeFont extends FileFont {
return null;
}
boolean hasCOLRTable() {
return getDirectoryEntry(COLRTag) != null;
}
/* Used to determine if this size has embedded bitmaps, which
* for CJK fonts should be used in preference to LCD glyphs.
*/

View File

@@ -26,6 +26,7 @@
package sun.java2d.pipe;
import sun.awt.SunHints;
import sun.font.StrikeCache;
import sun.java2d.SunGraphics2D;
import sun.font.GlyphList;
@@ -41,50 +42,49 @@ public abstract class GlyphListLoopPipe extends GlyphListPipe
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl,
int aaHint) {
int prevLimit = 0;
boolean isColor = false;
byte pixelFormat = StrikeCache.PIXEL_FORMAT_UNKNOWN;
int len = gl.getNumGlyphs();
gl.startGlyphIteration();
if (GlyphList.canContainColorGlyphs()) {
for (int i = 0; i < len; i++) {
boolean newIsColor = gl.isColorGlyph(i);
if (newIsColor != isColor) {
drawGlyphListSegment(sg2d, gl, prevLimit, i, aaHint,
isColor);
byte newFormat = gl.getPixelFormat(i);
if (newFormat != pixelFormat) {
drawGlyphListSegment(sg2d, gl,
prevLimit, i, aaHint, pixelFormat);
prevLimit = i;
isColor = newIsColor;
pixelFormat = newFormat;
}
}
}
drawGlyphListSegment(sg2d, gl, prevLimit, len, aaHint, isColor);
drawGlyphListSegment(sg2d, gl, prevLimit, len, aaHint, pixelFormat);
}
private void drawGlyphListSegment(SunGraphics2D sg2d, GlyphList gl,
int fromglyph, int toGlyph,
int aaHint, boolean isColor) {
int aaHint, byte pixelFormat) {
if (fromglyph >= toGlyph) return;
if (isColor) {
sg2d.loops.drawGlyphListColorLoop.
DrawGlyphListColor(sg2d, sg2d.surfaceData,
gl, fromglyph, toGlyph);
} else {
switch (aaHint) {
case SunHints.INTVAL_TEXT_ANTIALIAS_OFF:
switch (pixelFormat) {
case StrikeCache.PIXEL_FORMAT_GREYSCALE:
if (aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_OFF) {
sg2d.loops.drawGlyphListLoop.
DrawGlyphList(sg2d, sg2d.surfaceData,
gl, fromglyph, toGlyph);
return;
case SunHints.INTVAL_TEXT_ANTIALIAS_ON:
} else {
sg2d.loops.drawGlyphListAALoop.
DrawGlyphListAA(sg2d, sg2d.surfaceData,
gl, fromglyph, toGlyph);
return;
case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
sg2d.loops.drawGlyphListLCDLoop.
DrawGlyphListLCD(sg2d, sg2d.surfaceData,
gl, fromglyph, toGlyph);
return;
}
}
return;
case StrikeCache.PIXEL_FORMAT_LCD:
sg2d.loops.drawGlyphListLCDLoop.
DrawGlyphListLCD(sg2d, sg2d.surfaceData,
gl, fromglyph, toGlyph);
return;
case StrikeCache.PIXEL_FORMAT_BGRA:
sg2d.loops.drawGlyphListColorLoop.
DrawGlyphListColor(sg2d, sg2d.surfaceData,
gl, fromglyph, toGlyph);
return;
}
}
}

View File

@@ -27,6 +27,7 @@
#define FontScalerDefsIncludesDefined
#include "AccelGlyphCache.h"
#include "sun_font_StrikeCache.h"
#ifdef __cplusplus
extern "C" {
@@ -74,7 +75,8 @@ typedef struct GlyphInfo {
UInt16 width;
UInt16 height;
UInt16 rowBytes;
UInt8 managed;
UInt8 managed;
UInt8 format; // sun_font_StrikeCache_PIXEL_FORMAT_*
UInt8 subpixelResolutionX;
UInt8 subpixelResolutionY;
float topLeftX;

View File

@@ -1306,7 +1306,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
continue;
}
if (ginfo->rowBytes == ginfo->width) {
if (ginfo->format == sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE) {
if (oglc->grayRenderHints == NULL) {
OGLContext_InitGrayRenderHints(env, oglc);
}
@@ -1326,7 +1326,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
} else {
ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y, subimage);
}
} else if (ginfo->rowBytes == ginfo->width * 4) {
} else if (ginfo->format == sun_font_StrikeCache_PIXEL_FORMAT_BGRA) {
// color glyph data
ok = OGLTR_DrawColorGlyphNoCache(oglc, ginfo, x, y);
} else {

View File

@@ -578,8 +578,7 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
free(gbv);
return (GlyphBlitVector*)NULL;
}
/* rowBytes==width tests if its a B&W or LCD glyph */
if (ginfo->width == ginfo->rowBytes) {
if (ginfo->format != sun_font_StrikeCache_PIXEL_FORMAT_LCD) {
subPixPos = JNI_FALSE;
}
}

View File

@@ -145,6 +145,7 @@ typedef struct FTScalerContext {
int ptsz; /* size in points */
int fixedSizeIndex;/* -1 for scalable fonts and index inside
* scalerInfo->face->available_sizes otherwise */
jboolean colorFont;
} FTScalerContext;
/* SampledBGRABitmap contains (possibly) downscaled image data
@@ -865,10 +866,10 @@ static void setupTransform(FT_Matrix* target, FTScalerContext *context) {
}
static void setDefaultScalerSettings(FTScalerContext *context) {
if (context->aaType == TEXT_AA_OFF) {
context->loadFlags = FT_LOAD_TARGET_MONO;
} else if (context->aaType == TEXT_AA_ON) {
if (context->aaType == TEXT_AA_ON || context->colorFont) {
context->loadFlags = FT_LOAD_TARGET_NORMAL;
} else if (context->aaType == TEXT_AA_OFF) {
context->loadFlags = FT_LOAD_TARGET_MONO;
} else {
context->lcdFilter = FT_LCD_FILTER_LIGHT;
if (context->aaType == TEXT_AA_LCD_HRGB ||
@@ -894,11 +895,13 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
FT_UInt dpi = (FT_UInt) getScreenResolution(env);
if (FT_IS_SCALABLE(scalerInfo->face)) { // Standard scalable face
context->colorFont = FT_HAS_COLOR(scalerInfo->face) ? TRUE : FALSE;
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)
context->colorFont = TRUE;
const int ptsz = context->ptsz;
// Best size is smallest, but not smaller than requested
int bestSizeIndex = 0;
@@ -1035,16 +1038,10 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
if (logFC && fcAutohintSet) fprintf(stderr, "FC_AUTOHINT(%d) ", fcAutohint);
if (context->aaType == TEXT_AA_ON) { // Greyscale AA
if (context->aaType == TEXT_AA_ON || context->colorFont) { // Greyscale AA or color glyph
setupLoadRenderFlags(context, fcHintStyle, fcAutohint, fcAutohintSet, FT_LOAD_DEFAULT, FT_RENDER_MODE_NORMAL);
} 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);
setupLoadRenderFlags(context, fcHintStyle, fcAutohint, fcAutohintSet, FT_LOAD_TARGET_MONO, FT_RENDER_MODE_MONO);
} else {
int fcRGBA = FC_RGBA_UNKNOWN;
if (fcAntialiasSet && fcAntialias) {
@@ -1083,10 +1080,6 @@ 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;
@@ -1702,9 +1695,12 @@ static jlong
context->loadFlags |= FT_LOAD_NO_HINTING;
}
/* Don't disable bitmaps when working with fixed-size glyph,
* this is most probably a BGRA glyph */
if (!context->useSbits && context->fixedSizeIndex == -1) {
if (context->colorFont) {
context->loadFlags |= FT_LOAD_COLOR;
}
/* Don't disable bitmaps for color glyphs */
if (!context->useSbits && !context->colorFont) {
context->loadFlags |= FT_LOAD_NO_BITMAP;
}
@@ -1714,10 +1710,10 @@ static jlong
Or we can disable hinting. */
/* select appropriate hinting mode */
if (context->aaType == TEXT_AA_OFF) {
target = FT_LOAD_TARGET_MONO;
} else if (context->aaType == TEXT_AA_ON) {
if (context->aaType == TEXT_AA_ON || context->colorFont) {
target = FT_LOAD_TARGET_NORMAL;
} else if (context->aaType == TEXT_AA_OFF) {
target = FT_LOAD_TARGET_MONO;
} else if (context->aaType == TEXT_AA_LCD_HRGB ||
context->aaType == TEXT_AA_LCD_HBGR) {
target = FT_LOAD_TARGET_LCD;
@@ -1742,7 +1738,7 @@ static jlong
int outlineGlyph = ftglyph->format == FT_GLYPH_FORMAT_OUTLINE;
/* apply styles */
if (context->doBold && outlineGlyph) { /* if bold style */
if (context->doBold && outlineGlyph && !context->colorFont) { /* if bold style */
GlyphSlot_Embolden(ftglyph, context->transform);
}
@@ -1757,7 +1753,7 @@ static jlong
* We do this by rendering the glyph multiple times with
* different subpixel offsets, which results in
* subpixelResolutionX * subpixelResolutionY images per glyph. */
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY &&
if (!context->colorFont && ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY &&
context->aaType == TEXT_AA_ON && context->fmType == TEXT_FM_ON) {
subpixelResolutionX = subpixelGlyphResolution.x;
subpixelResolutionY = subpixelGlyphResolution.y;
@@ -1843,6 +1839,11 @@ static jlong
glyphInfo->subpixelResolutionX = subpixelResolutionX;
glyphInfo->subpixelResolutionY = subpixelResolutionY;
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) glyphInfo->format = sun_font_StrikeCache_PIXEL_FORMAT_BGRA;
else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD ||
ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) glyphInfo->format = sun_font_StrikeCache_PIXEL_FORMAT_LCD;
else glyphInfo->format = sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE;
if (renderImage) {
if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD && width > 0) {
glyphInfo->width = width/3;

View File

@@ -314,7 +314,7 @@ Java_sun_font_StrikeCache_getGlyphCacheDescription
GlyphInfo *info;
size_t baseAddr;
if ((*env)->GetArrayLength(env, results) < 15) {
if ((*env)->GetArrayLength(env, results) < 16) {
return;
}
@@ -343,6 +343,7 @@ Java_sun_font_StrikeCache_getGlyphCacheDescription
nresults[12] = (size_t)&(info->managed)-baseAddr;
nresults[13] = (size_t)&(info->subpixelResolutionX)-baseAddr;
nresults[14] = (size_t)&(info->subpixelResolutionY)-baseAddr;
nresults[15] = (size_t)&(info->format)-baseAddr;
(*env)->ReleasePrimitiveArrayCritical(env, results, nresults, 0);
}

View File

@@ -199,13 +199,11 @@ public class XRGlyphCacheEntry {
}
public Type getType() {
int rowBytes = getSourceRowBytes();
int width = getWidth();
// 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;
byte format = StrikeCache.unsafe.getByte(glyphInfoPtr + StrikeCache.formatOffset);
if (format == StrikeCache.PIXEL_FORMAT_GREYSCALE) return Type.GRAYSCALE;
else if (format == StrikeCache.PIXEL_FORMAT_LCD) return Type.LCD;
else if (format == StrikeCache.PIXEL_FORMAT_BGRA) return Type.BGRA;
else throw new IllegalStateException("Unknown glyph format: " + format);
}
public int getPaddedWidth() {

View File

@@ -289,6 +289,9 @@ JNIEXPORT jlong JNICALL AWTFontGenerateImage(AWTFont pFont, AWTChar2b* xChar) {
glyphInfo->cellInfo = NULL;
glyphInfo->width = width;
glyphInfo->height = height;
glyphInfo->subpixelResolutionX = 1;
glyphInfo->subpixelResolutionY = 1;
glyphInfo->format = sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE;
glyphInfo->topLeftX = xcs.lbearing;
glyphInfo->topLeftY = -xcs.ascent;
glyphInfo->advanceX = xcs.width;

View File

@@ -792,7 +792,7 @@ D3DTR_DrawGlyphList(D3DContext *d3dc, D3DSDOps *dstOps,
break;
}
grayscale = (ginfo->rowBytes == ginfo->width);
grayscale = (ginfo->format == sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE);
if (usePositions) {
jfloat posx = NEXT_FLOAT(positions);

View File

@@ -447,6 +447,9 @@ Java_sun_font_FileFontStrike__1getGlyphImageFromWindows
glyphInfo->width -= 1; // must subtract 1
}
glyphInfo->height = height;
glyphInfo->subpixelResolutionX = 1;
glyphInfo->subpixelResolutionY = 1;
glyphInfo->format = sun_font_StrikeCache_PIXEL_FORMAT_LCD;
glyphInfo->advanceX = advanceX;
glyphInfo->advanceY = advanceY;
glyphInfo->topLeftX = (float)(topLeftX-1);

View File

@@ -264,6 +264,9 @@ Java_sun_font_FileFontStrike__1getGlyphImageFromWindowsUsingDirectWrite
glyphInfo->rowBytes = glyphBytesWidth;
glyphInfo->width = glyphWidth;
glyphInfo->height = glyphHeight;
glyphInfo->subpixelResolutionX = 1;
glyphInfo->subpixelResolutionY = 1;
glyphInfo->format = sun_font_StrikeCache_PIXEL_FORMAT_LCD;
glyphInfo->advanceX = rotation == 0 ? advance : rotation == 2 ? -advance : 0;
glyphInfo->advanceY = rotation == 3 ? advance : rotation == 1 ? -advance : 0;
glyphInfo->topLeftX = bbRect.left - xTransformed;