Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8269806: Emoji rendering on Linux #4798

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ccb864e
8269806: Emoji rendering on Linux
YaaZ Jul 15, 2021
5d99aee
8269806: Fix builds with old Freetype (before 2.5)
YaaZ Mar 24, 2022
9dbed75
8269806: Fix emoji rendering with -Dsun.java2d.xrender=false and AA=OFF
YaaZ Mar 30, 2022
f01750b
8269806: Fix glyph advance rounding with FM=OFF
YaaZ Jun 24, 2022
e8b1ada
8269806: Fix rendering of colored glyphs on big font sizes
YaaZ Jun 25, 2022
37f1376
Revert "8269806: Fix rendering of colored glyphs on big font sizes"
YaaZ Jul 13, 2022
4097e18
8269806: Added emoji support for Windows
YaaZ Jul 13, 2022
30067e8
8269806: Fix emoji, ZWJ, variation selectors and font fallback
YaaZ Jul 14, 2022
f8fc165
Fix GlyphRenderData CRLF
YaaZ Jul 15, 2022
3eb8915
Fix other CRLF
YaaZ Jul 15, 2022
29b68f1
Fix pointer to long conversion
YaaZ Jul 15, 2022
1a2b4d6
Optimize bitmap creation in GlyphRenderData
YaaZ Jul 21, 2022
71a6f34
Merge master
YaaZ Jul 21, 2022
07150fb
Disable colored outlines on Linux to be able to build with old system…
YaaZ Jul 23, 2022
83d5da5
Fix rotated text metrics
YaaZ Aug 1, 2022
c2506a3
Fix italic and bold styles for colored outline glyphs
YaaZ Aug 5, 2022
ed77423
Merge branch 'master' into JDK-8269806
YaaZ Oct 25, 2022
8c5b285
Merge branch 'master' into JDK-8269806
YaaZ Nov 30, 2022
4c07805
Fix pointer to jlong conversion on x86
YaaZ Dec 1, 2022
4d4fadb
Skip tests if required font is absent
YaaZ Jan 25, 2023
578e558
Remove ftcolor.c
YaaZ Jan 31, 2023
728f1b5
Merge branch 'master' into JDK-8269806
YaaZ Jan 31, 2023
778f423
Make tests headful.
YaaZ Jan 31, 2023
b3bbc1b
Add braces to ifs.
YaaZ Jan 31, 2023
342dcde
Replace unicodeToUnits with Character.toChars in CCharToGlyphMapper
YaaZ Feb 1, 2023
57c0b57
Dynamic loading of ftcolor.h symbols on Linux.
YaaZ Feb 1, 2023
47f9bb1
Merge branch 'master' into JDK-8269806
YaaZ Apr 5, 2023
df4f0e8
🥸
YaaZ Apr 3, 2023
8a9c83a
Removed getGlyphVectorOutline - it's unused and broken
YaaZ Apr 3, 2023
d84ac24
Get rid of charsToGlyphs[NS] boilerplate.
YaaZ Apr 3, 2023
c1b8f9b
Moved tests to separate directory.
YaaZ Apr 3, 2023
624af03
EmojiFont CRLF -> LF
YaaZ Apr 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 26 additions & 3 deletions src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
Expand Up @@ -88,6 +88,31 @@ public synchronized boolean charsToGlyphsNS(int count,
return false;
}

private static int unicodeToUnits(int unicode, int dstOffset, char[] dst) {
if (unicode >= 0x10000) {
YaaZ marked this conversation as resolved.
Show resolved Hide resolved
int base = unicode - 0x10000;
dst[dstOffset] = (char)((base >>> 10) + HI_SURROGATE_START);
dst[dstOffset+1] = (char)((base % 0x400) + LO_SURROGATE_START);
return 2;
} else {
dst[dstOffset] = (char) unicode;
return 1;
}
}

public int charToVariationGlyph(int unicode, int variationSelector) {
if (variationSelector == 0) return charToGlyph(unicode);
YaaZ marked this conversation as resolved.
Show resolved Hide resolved
final char[] unicodeArray = new char[4];
final int[] glyphArray = new int[4];

int size = unicodeToUnits(unicode, 0, unicodeArray);
size += unicodeToUnits(variationSelector, size, unicodeArray);

nativeCharsToGlyphs(fFont.getNativeFontPtr(), size, unicodeArray, glyphArray);

return glyphArray[0];
}

public synchronized int charToGlyph(char unicode) {
final int glyph = cache.get(unicode);
if (glyph != 0) return glyph;
Expand All @@ -105,9 +130,7 @@ public synchronized int charToGlyph(int unicode) {
if (unicode >= 0x10000) {
int[] glyphs = new int[2];
char[] surrogates = new char[2];
int base = unicode - 0x10000;
surrogates[0] = (char)((base >>> 10) + HI_SURROGATE_START);
surrogates[1] = (char)((base % 0x400) + LO_SURROGATE_START);
unicodeToUnits(unicode, 0, surrogates);
charsToGlyphs(2, surrogates, glyphs);
return glyphs[0];
} else {
Expand Down
Expand Up @@ -51,10 +51,28 @@ public boolean canDisplay(char ch) {
return glyph != missingGlyph;
}

private int convertToGlyph(int unicode) {
@Override
public int charToVariationGlyph(int unicode, int variationSelector) {
if (variationSelector == 0) return charToGlyph(unicode);
else {
int glyph = convertToGlyph(unicode, variationSelector);
if (glyph == missingGlyph) glyph = charToGlyph(unicode);
return glyph;
}
}

@Override
protected int convertToGlyph(int unicode) {
int glyph = convertToGlyph(unicode, 0);
// setCachedGlyphCode(unicode, glyph);
YaaZ marked this conversation as resolved.
Show resolved Hide resolved
return glyph;
}

@Override
protected int convertToGlyph(int unicode, int variationSelector) {
for (int slot = 0; slot < font.numSlots; slot++) {
CharToGlyphMapper mapper = getSlotMapper(slot);
int glyphCode = mapper.charToGlyph(unicode);
int glyphCode = mapper.charToVariationGlyph(unicode, variationSelector);
// The CFont Mappers will return a negative code
// for fonts that will fill the glyph from fallbacks
// - cascading font in OSX-speak. But we need to be
Expand Down
12 changes: 12 additions & 0 deletions src/java.desktop/macosx/classes/sun/font/CStrike.java
Expand Up @@ -60,6 +60,12 @@ private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,
double x,
double y);

private static native void getNativeGlyphRenderData(long nativeStrikePtr,
int glyphCode,
double x,
double y,
GlyphRenderData result);

// returns the bounding rect for a glyph
private static native void getNativeGlyphImageBounds(long nativeStrikePtr,
int glyphCode,
Expand Down Expand Up @@ -219,6 +225,12 @@ GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
throw new Error("not implemented yet");
}

GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
GlyphRenderData result = new GlyphRenderData();
getNativeGlyphRenderData(getNativeStrikePtr(), glyphCode, x, y, result);
return result;
}

// called from the Sun2D renderer
long getGlyphImagePtr(int glyphCode) {
synchronized (glyphInfoCache) {
Expand Down
4 changes: 4 additions & 0 deletions src/java.desktop/macosx/classes/sun/font/NativeStrike.java
Expand Up @@ -83,4 +83,8 @@ GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
return null;
}

GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
return null;
}

}
208 changes: 184 additions & 24 deletions src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m
Expand Up @@ -205,20 +205,11 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
JNI_COCOA_EXIT(env);
}

/*
* Class: sun_font_CStrike
* Method: getNativeGlyphOutline
* Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
*/
JNIEXPORT jobject JNICALL
Java_sun_font_CStrike_getNativeGlyphOutline
(JNIEnv *env, jclass clazz,
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos)
jobject getGlyphOutline(JNIEnv *env, AWTStrike *awtStrike,
CTFontRef font, CGGlyph glyph,
jdouble xPos, jdouble yPos)
{
jobject generalPath = NULL;

JNI_COCOA_ENTER(env);

AWTPathRef path = NULL;
jfloatArray pointCoords = NULL;
jbyteArray pointTypes = NULL;
Expand All @@ -228,21 +219,11 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont

AWT_FONT_CLEANUP_SETUP;

AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtfont = awtStrike->fAWTFont;

AWT_FONT_CLEANUP_CHECK(awtfont);

// inverting the shear order and sign to compensate for the flipped coordinate system
CGAffineTransform tx = awtStrike->fTx;
tx.tx += xPos;
tx.ty += yPos;

// get the right font and glyph for this "Java GlyphCode"

CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);

// get the advance of this glyph
CGSize advance;
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
Expand All @@ -255,7 +236,6 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
tx = awtStrike->fTx;
tx = CGAffineTransformConcat(tx, sInverseTX);
AWTGetGlyphOutline(&glyph, (NSFont *)font, &advance, &tx, 0, 1, &path);
CFRelease(font);

pointCoords = (*env)->NewFloatArray(env, path->fNumberOfDataElements);
AWT_FONT_CLEANUP_CHECK(pointCoords);
Expand Down Expand Up @@ -289,10 +269,190 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
}

AWT_FONT_CLEANUP_FINISH;
JNI_COCOA_EXIT(env);
return generalPath;
}

/*
* Class: sun_font_CStrike
* Method: getNativeGlyphOutline
* Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
*/
JNIEXPORT jobject JNICALL
Java_sun_font_CStrike_getNativeGlyphOutline
(JNIEnv *env, jclass clazz,
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos) {
jobject generalPath = NULL;

JNI_COCOA_ENTER(env);
AWT_FONT_CLEANUP_SETUP;

AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtfont = awtStrike->fAWTFont;
AWT_FONT_CLEANUP_CHECK(awtfont);

// get the right font and glyph for this "Java GlyphCode"
CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);

generalPath = getGlyphOutline(env, awtStrike, font, glyph, xPos, yPos);

cleanup:
CFRelease(font);

AWT_FONT_CLEANUP_FINISH;
JNI_COCOA_EXIT(env);

return generalPath;
}

// OpenType data is Big-Endian
#define GET_BE_INT32(data, i) CFSwapInt32BigToHost(*(const UInt32*) ((const UInt8*) (data) + (i)))
#define GET_BE_INT16(data, i) CFSwapInt16BigToHost(*(const UInt16*) ((const UInt8*) (data) + (i)))

static bool addBitmapRenderData(JNIEnv *env, AWTStrike *awtStrike,
CTFontRef font, CGGlyph glyph,
jdouble xPos, jdouble yPos, jobject result) {
bool success = false;

DECLARE_CLASS_RETURN(jc_GlyphRenderData, "sun/font/GlyphRenderData", false);
DECLARE_METHOD_RETURN(GlyphRenderDataAddBitmap, jc_GlyphRenderData, "addBitmap", "(DDDDDDIIII[I)V", false);

AWT_FONT_CLEANUP_SETUP;

CTFontDescriptorRef descriptor = NULL;
CGFontRef cgFont = CTFontCopyGraphicsFont(font, &descriptor);

CFDataRef sbixTable = CGFontCopyTableForTag(cgFont, kCTFontTableSbix);
if (sbixTable == NULL) goto cleanup;

// Parse sbix table
CFIndex sbixSize = CFDataGetLength(sbixTable);
if (sbixSize < 8) goto cleanup; // Corrupted table
const UInt8* sbix = CFDataGetBytePtr(sbixTable);
UInt32 numStrikes = GET_BE_INT32(sbix, 4);
if (8 + 4 * numStrikes > sbixSize) goto cleanup; // Corrupted table
// Find last strike which has data for our glyph
// Last is usually the biggest
const UInt8* glyphData = NULL;
UInt32 size;
UInt16 ppem, ppi;
for (int i = numStrikes - 1; i >= 0; i--) {
const UInt8* strike = sbix + GET_BE_INT32(sbix, 8 + 4 * i);
if (strike + 12 + 4 * glyph > sbix + sbixSize) goto cleanup; // Corrupted table
UInt32 offset = GET_BE_INT32(strike, 4 + 4 * glyph);
size = GET_BE_INT32(strike, 8 + 4 * glyph) - offset;
if (size > 0) {
ppem = GET_BE_INT16(strike, 0);
ppi = GET_BE_INT16(strike, 2);
glyphData = strike + offset;
break;
}
}
if (glyphData == NULL) goto cleanup;
if (glyphData + 4 > sbix + sbixSize) goto cleanup; // Corrupted table

// Read glyph data
FourCharCode graphicType = GET_BE_INT32(glyphData, 4);
glyphData += 8;
size -= 8;
if (glyphData + size > sbix + sbixSize) goto cleanup; // Corrupted table

// Decode glyph image
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, glyphData, size, NULL);
CGImageRef image = NULL;
if (graphicType == 'jpg ') {
image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, false, kCGRenderingIntentDefault);
} else if (graphicType == 'png ') {
image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, false, kCGRenderingIntentDefault);
}
CGDataProviderRelease(dataProvider);

if (image != NULL) {
CGBitmapInfo info = CGImageGetBitmapInfo(image);
size_t bits = CGImageGetBitsPerPixel(image);
jint colorModel = -1;
if (info & (kCGImageAlphaPremultipliedLast | kCGImageAlphaLast)) colorModel = 0; // RGBA
else if (info & (kCGImageAlphaPremultipliedFirst | kCGImageAlphaFirst)) colorModel = 1; // ARGB
if (colorModel != -1 && (info & kCGBitmapFloatComponents) == 0 && bits == 32) {
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t pitch = CGImageGetBytesPerRow(image) / 4;
dataProvider = CGImageGetDataProvider(image);
CFDataRef data = CGDataProviderCopyData(dataProvider);

jbyteArray array = (*env)->NewIntArray(env, pitch * height);
(*env)->SetIntArrayRegion(env, array, 0, pitch * height, (const jint*) CFDataGetBytePtr(data));
CFRelease(data);

double pointSize = 72.0 * ppem / ppi;
double scale = 1.0 / pointSize;
font = CTFontCreateWithGraphicsFont(cgFont, pointSize, NULL, descriptor);
CGRect bbox = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, &glyph, NULL, 1);
CFRelease(font);
double tx = bbox.origin.x + xPos * pointSize / awtStrike->fSize;
double ty = -bbox.origin.y - (double) height + yPos * pointSize / awtStrike->fSize;

jdouble m00 = awtStrike->fTx.a * scale, m10 = awtStrike->fTx.b * scale;
jdouble m01 = -awtStrike->fTx.c * scale, m11 = -awtStrike->fTx.d * scale;
jdouble m02 = m00 * tx + m01 * ty, m12 = m10 * tx + m11 * ty;

(*env)->CallVoidMethod(env, result, GlyphRenderDataAddBitmap,
m00, m10, m01, m11, m02, m12,
width, height, pitch, 0, array);
success = true;
}
CGImageRelease(image);
}

// Cleanup
cleanup:
if (sbixTable) CFRelease(sbixTable);
if (cgFont) CFRelease(cgFont);
if (descriptor) CFRelease(descriptor);

AWT_FONT_CLEANUP_FINISH;
return success;
}

/*
* Class: sun_font_CStrike
* Method: getNativeGlyphRenderData
* Signature: (JIDDLsun/font/GlyphRenderData;)V
*/
JNIEXPORT void JNICALL
Java_sun_font_CStrike_getNativeGlyphRenderData
(JNIEnv *env, jclass clazz,
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos, jobject result)
{
JNI_COCOA_ENTER(env);

DECLARE_CLASS(jc_GlyphRenderData, "sun/font/GlyphRenderData");
DECLARE_FIELD(GlyphRenderDataOutline, jc_GlyphRenderData, "outline", "Ljava/awt/geom/GeneralPath;")

AWT_FONT_CLEANUP_SETUP;

AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtfont = awtStrike->fAWTFont;
AWT_FONT_CLEANUP_CHECK(awtfont);

// get the right font and glyph for this "Java GlyphCode"
CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);

if (!addBitmapRenderData(env, awtStrike, font, glyph, xPos, yPos, result)) {
jobject gp = getGlyphOutline(env, awtStrike, font, glyph, xPos, yPos);
if (gp != NULL) {
(*env)->SetObjectField(env, result, GlyphRenderDataOutline, gp);
}
}

cleanup:
CFRelease(font);

AWT_FONT_CLEANUP_FINISH;
JNI_COCOA_EXIT(env);
}

/*
* Class: sun_font_CStrike
* Method: getGlyphImagePtrsNative
Expand Down
Expand Up @@ -585,6 +585,7 @@ @implementation CGGI_GlyphCanvas
glyphInfo->height = height;
glyphInfo->rowBytes = width * pixelSize;
glyphInfo->cellInfo = NULL;
glyphInfo->format = (UInt8) pixelSize;

#ifdef USE_IMAGE_ALIGNED_MEMORY
glyphInfo->image = image;
Expand Down
Expand Up @@ -35,6 +35,10 @@
#define HI_SURROGATE_END 0xDBFF
#define LO_SURROGATE_START 0xDC00
#define LO_SURROGATE_END 0xDFFF
#define VS_START 0xFE00
#define VS_END 0xFE0F
#define VSS_START 0xE0100
#define VSS_END 0xE01FF

/*
* Transform Unicode characters into glyphs.
Expand Down