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

8263583: Emoji rendering on macOS #3007

Closed
wants to merge 4 commits into from
Closed
Changes from 2 commits
Commits
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
@@ -324,6 +324,19 @@ void MTLTR_FreeGlyphCaches() {
}
}

MTLPaint* storedPaint = nil;
Copy link
Contributor

@prrace prrace May 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be static ?

Copy link
Contributor Author

@JB-Dmitry JB-Dmitry May 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 'static', thanks.


static void EnableColorGlyphPainting(MTLContext *mtlc) {
storedPaint = mtlc.paint;
mtlc.paint = [[MTLPaint alloc] init];
}

static void DisableColorGlyphPainting(MTLContext *mtlc) {
[mtlc.paint release];
mtlc.paint = storedPaint;
storedPaint = nil;
}

static jboolean
MTLTR_DrawGrayscaleGlyphViaCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
@@ -337,6 +350,8 @@ void MTLTR_FreeGlyphCaches() {
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
MTLTR_EnableGlyphVertexCache(mtlc, dstOps);
glyphMode = MODE_USE_CACHE_GRAY;
@@ -383,6 +398,8 @@ void MTLTR_FreeGlyphCaches() {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}

if (glyphCacheLCD == NULL) {
@@ -455,6 +472,8 @@ void MTLTR_FreeGlyphCaches() {
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
glyphMode = MODE_NO_CACHE_GRAY;
@@ -517,6 +536,8 @@ void MTLTR_FreeGlyphCaches() {
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}

if (blitTexture == nil) {
@@ -586,6 +607,51 @@ void MTLTR_FreeGlyphCaches() {
return JNI_TRUE;
}

static jboolean
MTLTR_DrawColorGlyphNoCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
id<MTLTexture> dest = dstOps->pTexture;
const void *src = ginfo->image;
jint w = ginfo->width;
jint h = ginfo->height;
jint rowBytes = ginfo->rowBytes;
unsigned int imageSize = rowBytes * h;

J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawColorGlyphNoCache");

if (glyphMode != MODE_NO_CACHE_COLOR) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
}
glyphMode = MODE_NO_CACHE_COLOR;
EnableColorGlyphPainting(mtlc);
}

MTLPooledTextureHandle* texHandle = [mtlc.texturePool getTexture:w height:h format:MTLPixelFormatBGRA8Unorm];
if (texHandle == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLTR_DrawColorGlyphNoCache: can't obtain temporary texture object from pool");
return JNI_FALSE;
}

[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];

[texHandle.texture replaceRegion:MTLRegionMake2D(0, 0, w, h)
mipmapLevel:0
withBytes:src
bytesPerRow:rowBytes];

drawTex2Tex(mtlc, texHandle.texture, dest, JNI_FALSE, dstOps->isOpaque, INTERPOLATION_NEAREST_NEIGHBOR,
0, 0, w, h, x, y, x + w, y + h);

return JNI_TRUE;
}

// see DrawGlyphList.c for more on this macro...
#define FLOOR_ASSIGN(l, r) \
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
@@ -616,7 +682,7 @@ void MTLTR_FreeGlyphCaches() {
J2dTraceLn(J2D_TRACE_INFO, "Entered for loop for glyph list");
jint x, y;
jfloat glyphx, glyphy;
jboolean grayscale, ok;
jboolean ok;
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));

if (ginfo == NULL) {
@@ -626,8 +692,6 @@ void MTLTR_FreeGlyphCaches() {
break;
}

grayscale = (ginfo->rowBytes == ginfo->width);

if (usePositions) {
jfloat posx = NEXT_FLOAT(positions);
jfloat posy = NEXT_FLOAT(positions);
@@ -651,7 +715,7 @@ void MTLTR_FreeGlyphCaches() {

J2dTraceLn2(J2D_TRACE_INFO, "Glyph width = %d height = %d", ginfo->width, ginfo->height);
J2dTraceLn1(J2D_TRACE_INFO, "rowBytes = %d", ginfo->rowBytes);
if (grayscale) {
if (ginfo->rowBytes == ginfo->width) {
// grayscale or monochrome glyph data
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
@@ -662,6 +726,10 @@ void MTLTR_FreeGlyphCaches() {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale no cache");
ok = MTLTR_DrawGrayscaleGlyphNoCache(mtlc, ginfo, x, y, dstOps);
}
} else if (ginfo->rowBytes == ginfo->width * 4) {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList color glyph no cache");
ok = MTLTR_DrawColorGlyphNoCache(mtlc, ginfo, x, y, dstOps);
flushBeforeLCD = JNI_FALSE;
} else {
if (!flushBeforeLCD) {
[mtlc.encoderManager endEncoder];
@@ -720,6 +788,8 @@ void MTLTR_FreeGlyphCaches() {
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
}

@@ -23,33 +23,48 @@

/*
* @test
* @key headful
* @bug 8263583
* @summary Checks that emoji characters are rendered
* @summary Checks that emoji character has a non-empty and identical
* representation when rendered to different types of images,
* including an accelerated (OpenGL or Metal) surface.
* @requires (os.family == "mac")
* @run main/othervm -Dsun.java2d.uiScale=1 MacEmoji
*/

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.util.List;

public class MacEmoji {
private static final int IMG_WIDTH = 20;
private static final int IMG_HEIGHT = 20;

public static void main(String[] args) {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT,
BufferedImage.TYPE_INT_RGB);
GraphicsConfiguration cfg
= GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();

Graphics2D g = img.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
g.setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
g.drawString("\uD83D\uDE00" /* U+1F600 'GRINNING FACE' */, 2, 15);
g.dispose();
VolatileImage vImg = cfg.createCompatibleVolatileImage(IMG_WIDTH,
IMG_HEIGHT);
BufferedImage refImg;
int attempt = 0;
do {
if (++attempt > 10) {
throw new RuntimeException("Failed to render to VolatileImage");
}
if (vImg.validate(cfg) == VolatileImage.IMAGE_INCOMPATIBLE) {
throw new RuntimeException("Unexpected validation failure");
}
drawEmoji(vImg);
refImg = vImg.getSnapshot();
} while (vImg.contentsLost());

boolean rendered = false;
for (int x = 0; x < IMG_WIDTH; x++) {
for (int y = 0; y < IMG_HEIGHT; y++) {
if (img.getRGB(x, y) != 0xFFFFFFFF) {
if (refImg.getRGB(x, y) != 0xFFFFFFFF) {
rendered = true;
break;
}
@@ -58,5 +73,36 @@ public static void main(String[] args) {
if (!rendered) {
throw new RuntimeException("Emoji character wasn't rendered");
}

List<Integer> imageTypes = List.of(
BufferedImage.TYPE_INT_RGB,
BufferedImage.TYPE_INT_ARGB,
BufferedImage.TYPE_INT_ARGB_PRE,
BufferedImage.TYPE_INT_BGR,
BufferedImage.TYPE_3BYTE_BGR,
BufferedImage.TYPE_4BYTE_ABGR,
BufferedImage.TYPE_4BYTE_ABGR_PRE
);
for (Integer type : imageTypes) {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type);
drawEmoji(img);
for (int x = 0; x < IMG_WIDTH; x++) {
for (int y = 0; y < IMG_HEIGHT; y++) {
if (refImg.getRGB(x, y) != img.getRGB(x, y)) {
throw new RuntimeException(
"Rendering differs for image type " + type);
}
}
}
}
}

private static void drawEmoji(Image img) {
Graphics g = img.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
g.setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
g.drawString("\uD83D\uDE00" /* U+1F600 'GRINNING FACE' */, 2, 15);
g.dispose();
}
}