8263583: Emoji rendering on macOS
Reviewed-by: serb, prr
This commit is contained in:
parent
1ab2776947
commit
236bd89dc3
src/java.desktop
macosx/native/libawt_lwawt
share
classes/sun
font
java2d
native
common/java2d/opengl
libfontmanager
unix
test/jdk/java/awt/font
@ -41,3 +41,5 @@
|
||||
+ (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env;
|
||||
|
||||
@end
|
||||
|
||||
bool IsEmojiFont(CTFontRef font);
|
||||
|
@ -583,3 +583,14 @@ JNI_COCOA_ENTER(env);
|
||||
CFRelease(fds);
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
static CFStringRef EMOJI_FONT_NAME = CFSTR("Apple Color Emoji");
|
||||
|
||||
bool IsEmojiFont(CTFontRef font)
|
||||
{
|
||||
CFStringRef name = CTFontCopyFullName(font);
|
||||
if (name == NULL) return false;
|
||||
bool isFixedColor = CFStringCompare(name, EMOJI_FONT_NAME, 0) == kCFCompareEqualTo;
|
||||
CFRelease(name);
|
||||
return isFixedColor;
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ JNI_COCOA_ENTER(env);
|
||||
// to indicate we should use CoreText to substitute the character
|
||||
CGGlyph glyph;
|
||||
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &awtStrike->fAltTx, awtStrike->fStyle, &glyph, 1, NULL, &advance);
|
||||
CFRelease(fallback);
|
||||
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
|
||||
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
|
||||
@ -188,7 +188,7 @@ JNI_COCOA_ENTER(env);
|
||||
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
|
||||
|
||||
CGRect bbox;
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, awtStrike->fStyle, &glyph, 1, &bbox);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &tx, awtStrike->fStyle, &glyph, 1, &bbox, NULL);
|
||||
CFRelease(fallback);
|
||||
|
||||
// the origin of this bounding box is relative to the bottom-left corner baseline
|
||||
|
@ -33,5 +33,12 @@ void
|
||||
CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
|
||||
const AWTStrike *strike,
|
||||
jint rawGlyphCodes[], const CFIndex len);
|
||||
|
||||
void
|
||||
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
|
||||
const CGAffineTransform *tx,
|
||||
const JRSFontRenderingStyle style,
|
||||
const CGGlyph glyphs[],
|
||||
size_t count,
|
||||
CGRect bboxes[],
|
||||
CGSize advances[]);
|
||||
#endif /* __CGGLYPHIMAGES_H */
|
||||
|
@ -309,6 +309,40 @@ CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CGGI_CopyImageFromCanvasToARGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
|
||||
{
|
||||
CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(canvas->context);
|
||||
bool littleEndian = (bitmapInfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little;
|
||||
|
||||
UInt32 *src = (UInt32 *)canvas->image->data;
|
||||
size_t srcRowWidth = canvas->image->width;
|
||||
|
||||
UInt8 *dest = (UInt8 *)info->image;
|
||||
size_t destRowWidth = info->width;
|
||||
|
||||
size_t height = info->height;
|
||||
|
||||
size_t y;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
size_t srcRow = y * srcRowWidth;
|
||||
if (littleEndian) {
|
||||
UInt16 destRowBytes = info->rowBytes;
|
||||
memcpy(dest, src + srcRow, destRowBytes);
|
||||
dest += destRowBytes;
|
||||
} else {
|
||||
size_t x;
|
||||
for (x = 0; x < destRowWidth; x++) {
|
||||
UInt32 p = src[srcRow + x];
|
||||
*dest++ = (p >> 24 & 0xFF); // blue (alpha-premultiplied)
|
||||
*dest++ = (p >> 16 & 0xFF); // green (alpha-premultiplied)
|
||||
*dest++ = (p >> 8 & 0xFF); // red (alpha-premultiplied)
|
||||
*dest++ = (p & 0xFF); // alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
|
||||
|
||||
@ -326,6 +360,14 @@ static CGGI_GlyphInfoDescriptor grey =
|
||||
{ 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
|
||||
static CGGI_GlyphInfoDescriptor rgb =
|
||||
{ 3, &CGGI_CopyImageFromCanvasToRGBInfo };
|
||||
static CGGI_GlyphInfoDescriptor argb =
|
||||
{ 4, &CGGI_CopyImageFromCanvasToARGBInfo };
|
||||
|
||||
static inline CGGI_GlyphInfoDescriptor*
|
||||
CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CTFontRef font)
|
||||
{
|
||||
return IsEmojiFont(font) ? &argb : mode->glyphDescriptor;
|
||||
}
|
||||
|
||||
static inline CGGI_RenderingMode
|
||||
CGGI_GetRenderingMode(const AWTStrike *strike)
|
||||
@ -459,11 +501,11 @@ CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the canvas by blitting white only into the region of interest
|
||||
* Clear the canvas by blitting white (or transparent background for color glyphs) only into the region of interest
|
||||
* (the rect which we will copy out of once the glyph is struck).
|
||||
*/
|
||||
static inline void
|
||||
CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
|
||||
CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info, bool transparent)
|
||||
{
|
||||
vImage_Buffer canvasRectToClear;
|
||||
canvasRectToClear.data = canvas->image->data;
|
||||
@ -474,13 +516,16 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
|
||||
|
||||
// clean the canvas
|
||||
#ifdef CGGI_DEBUG
|
||||
Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
|
||||
Pixel_8888 background = { 0xE0, 0xE0, 0xE0, 0xE0 };
|
||||
#else
|
||||
Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
Pixel_8888 background = { transparent ? 0 : 0xFF,
|
||||
transparent ? 0 : 0xFF,
|
||||
transparent ? 0 : 0xFF,
|
||||
transparent ? 0 : 0xFF };
|
||||
#endif
|
||||
|
||||
// clear canvas background and set foreground color
|
||||
vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
|
||||
// clear canvas background
|
||||
vImageBufferFill_ARGB8888(&canvasRectToClear, background, kvImageNoFlags);
|
||||
}
|
||||
|
||||
|
||||
@ -493,9 +538,9 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
|
||||
static inline GlyphInfo *
|
||||
CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
|
||||
const AWTStrike *strike,
|
||||
const CGGI_RenderingMode *mode)
|
||||
const CGGI_GlyphInfoDescriptor *glyphDescriptor)
|
||||
{
|
||||
size_t pixelSize = mode->glyphDescriptor->pixelSize;
|
||||
size_t pixelSize = glyphDescriptor->pixelSize;
|
||||
|
||||
// adjust the bounding box to be 1px bigger on each side than what
|
||||
// CGFont-whatever suggests - because it gives a bounding box that
|
||||
@ -560,7 +605,7 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
|
||||
static inline void
|
||||
CGGI_CreateImageForGlyph
|
||||
(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
|
||||
GlyphInfo *info, const CGGI_RenderingMode *mode)
|
||||
GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike, CTFontRef font)
|
||||
{
|
||||
if (isnan(info->topLeftX) || isnan(info->topLeftY)) {
|
||||
// Explicitly set glyphInfo width/height to be 0 to ensure
|
||||
@ -569,20 +614,48 @@ CGGI_CreateImageForGlyph
|
||||
info->height = 0;
|
||||
|
||||
// copy the "empty" glyph from the canvas into the info
|
||||
(*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
|
||||
(*glyphDescriptor->copyFxnPtr)(canvas, info);
|
||||
return;
|
||||
}
|
||||
|
||||
// clean the canvas
|
||||
CGGI_ClearCanvas(canvas, info);
|
||||
CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb);
|
||||
|
||||
// strike the glyph in the upper right corner
|
||||
CGContextShowGlyphsAtPoint(canvas->context,
|
||||
-info->topLeftX,
|
||||
canvas->image->height + info->topLeftY,
|
||||
&glyph, 1);
|
||||
CGFloat x = -info->topLeftX;
|
||||
CGFloat y = canvas->image->height + info->topLeftY;
|
||||
|
||||
if (glyphDescriptor == &argb) {
|
||||
// Emoji glyphs are not rendered by CGContextShowGlyphsAtPoint.
|
||||
// Also, it's not possible to use transformation matrix to get the emoji glyph
|
||||
// rendered for the desired font size - actual-size font object is needed.
|
||||
// The logic here must match the logic in CGGlyphImages_GetGlyphMetrics,
|
||||
// which calculates glyph metrics.
|
||||
|
||||
CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context);
|
||||
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
|
||||
CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);
|
||||
|
||||
CGFloat normFactor = 1.0 / fontSize;
|
||||
CGAffineTransform normalizedMatrix = CGAffineTransformScale(matrix, normFactor, normFactor);
|
||||
CGContextSetTextMatrix(canvas->context, normalizedMatrix);
|
||||
|
||||
CGPoint userPoint = CGPointMake(x, y);
|
||||
CGAffineTransform normalizedMatrixInv = CGAffineTransformInvert(normalizedMatrix);
|
||||
CGPoint textPoint = CGPointApplyAffineTransform(userPoint, normalizedMatrixInv);
|
||||
|
||||
CTFontDrawGlyphs(sizedFont, &glyph, &textPoint, 1, canvas->context);
|
||||
|
||||
CFRelease(sizedFont);
|
||||
// restore context's original state
|
||||
CGContextSetTextMatrix(canvas->context, matrix);
|
||||
CGContextSetFontSize(canvas->context, 1); // CTFontDrawGlyphs tampers with it
|
||||
} else {
|
||||
CGContextShowGlyphsAtPoint(canvas->context, x, y, &glyph, 1);
|
||||
}
|
||||
|
||||
// copy the glyph from the canvas into the info
|
||||
(*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
|
||||
(*glyphDescriptor->copyFxnPtr)(canvas, info);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -615,13 +688,13 @@ CGGI_CreateImageForUnicode
|
||||
JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
|
||||
|
||||
CGRect bbox;
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
|
||||
|
||||
CGSize advance;
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &tx, style, &glyph, 1, &bbox, &advance);
|
||||
|
||||
CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback);
|
||||
|
||||
// create the Sun2D GlyphInfo we are going to strike into
|
||||
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
|
||||
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor);
|
||||
|
||||
// fix the context size, just in case the substituted character is unexpectedly large
|
||||
CGGI_SizeCanvas(canvas, info->width, info->height, mode);
|
||||
@ -634,7 +707,7 @@ CGGI_CreateImageForUnicode
|
||||
CFRelease(cgFallback);
|
||||
|
||||
// clean the canvas - align, strike, and copy the glyph from the canvas into the info
|
||||
CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
|
||||
CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback);
|
||||
|
||||
// restore the state of the world
|
||||
CGContextRestoreGState(canvas->context);
|
||||
@ -677,11 +750,14 @@ CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
|
||||
CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
|
||||
JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
|
||||
|
||||
CTFontRef mainFont = (CTFontRef)strike->fAWTFont->fFont;
|
||||
CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, mainFont);
|
||||
|
||||
CFIndex i;
|
||||
for (i = 0; i < len; i++) {
|
||||
GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
|
||||
if (info != NULL) {
|
||||
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
|
||||
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mainFontDescriptor, strike, mainFont);
|
||||
} else {
|
||||
info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
|
||||
glyphInfos[i] = ptr_to_jlong(info);
|
||||
@ -774,8 +850,9 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
|
||||
CGAffineTransform tx = strike->fTx;
|
||||
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
|
||||
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
|
||||
CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
|
||||
CTFontRef fontRef = (CTFontRef)font->fFont;
|
||||
CGGlyphImages_GetGlyphMetrics(fontRef, &tx, bboxCGMode, glyphs, len, bboxes, advances);
|
||||
CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fontRef);
|
||||
|
||||
size_t maxWidth = 1;
|
||||
size_t maxHeight = 1;
|
||||
@ -792,7 +869,7 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
|
||||
CGSize advance = advances[i];
|
||||
CGRect bbox = bboxes[i];
|
||||
|
||||
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
|
||||
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor);
|
||||
|
||||
if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
|
||||
if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
|
||||
@ -888,3 +965,59 @@ CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates bounding boxes (for given transform) and advance (for untransformed 1pt-size font) for specified glyphs.
|
||||
*/
|
||||
void
|
||||
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
|
||||
const CGAffineTransform *tx,
|
||||
const JRSFontRenderingStyle style,
|
||||
const CGGlyph glyphs[],
|
||||
size_t count,
|
||||
CGRect bboxes[],
|
||||
CGSize advances[]) {
|
||||
if (IsEmojiFont(font)) {
|
||||
// Glyph metrics for emoji font are not strictly proportional to font size,
|
||||
// so we need to construct real-sized font object to calculate them.
|
||||
// The logic here must match the logic in CGGI_CreateImageForGlyph,
|
||||
// which performs glyph drawing.
|
||||
|
||||
CGFloat fontSize = sqrt(fabs(tx->a * tx->d - tx->b * tx->c));
|
||||
CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);
|
||||
|
||||
if (bboxes) {
|
||||
// JRSFontGetBoundingBoxesForGlyphsAndStyle works incorrectly for AppleColorEmoji font:
|
||||
// it uses bottom left corner of the glyph's bounding box as a fixed point of transformation
|
||||
// instead of glyph's origin point (used at drawing). So, as a workaround,
|
||||
// we request a bounding box for the untransformed glyph, and apply the transform ourselves.
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(sizedFont, &CGAffineTransformIdentity, style, glyphs, count, bboxes);
|
||||
CGAffineTransform txNormalized = CGAffineTransformMake(tx->a / fontSize,
|
||||
tx->b / fontSize,
|
||||
tx->c / fontSize,
|
||||
tx->d / fontSize,
|
||||
0, 0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
bboxes[i] = CGRectApplyAffineTransform(bboxes[i], txNormalized);
|
||||
}
|
||||
}
|
||||
|
||||
if (advances) {
|
||||
CTFontGetAdvancesForGlyphs(sizedFont, kCTFontDefaultOrientation, glyphs, advances, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Calling code will scale the result back
|
||||
advances[i].width /= fontSize;
|
||||
advances[i].height /= fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(sizedFont);
|
||||
} else {
|
||||
if (bboxes) {
|
||||
JRSFontGetBoundingBoxesForGlyphsAndStyle(font, tx, style, glyphs, count, bboxes);
|
||||
}
|
||||
if (advances) {
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count);
|
||||
}
|
||||
}
|
||||
}
|
@ -324,6 +324,19 @@ void MTLTR_FreeGlyphCaches() {
|
||||
}
|
||||
}
|
||||
|
||||
static MTLPaint* storedPaint = nil;
|
||||
|
||||
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 @@ MTLTR_DrawGrayscaleGlyphViaCache(MTLContext *mtlc,
|
||||
} 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 @@ MTLTR_DrawLCDGlyphViaCache(MTLContext *mtlc, BMTLSDOps *dstOps,
|
||||
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 @@ MTLTR_DrawGrayscaleGlyphNoCache(MTLContext *mtlc,
|
||||
} 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 @@ MTLTR_DrawLCDGlyphNoCache(MTLContext *mtlc, BMTLSDOps *dstOps,
|
||||
} else if (glyphMode == MODE_USE_CACHE_LCD) {
|
||||
[mtlc.encoderManager endEncoder];
|
||||
lcdCacheEncoder = nil;
|
||||
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
|
||||
DisableColorGlyphPainting(mtlc);
|
||||
}
|
||||
|
||||
if (blitTexture == nil) {
|
||||
@ -584,6 +605,51 @@ MTLTR_DrawLCDGlyphNoCache(MTLContext *mtlc, BMTLSDOps *dstOps,
|
||||
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))
|
||||
@ -614,7 +680,7 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
|
||||
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) {
|
||||
@ -624,8 +690,6 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
|
||||
break;
|
||||
}
|
||||
|
||||
grayscale = (ginfo->rowBytes == ginfo->width);
|
||||
|
||||
if (usePositions) {
|
||||
jfloat posx = NEXT_FLOAT(positions);
|
||||
jfloat posy = NEXT_FLOAT(positions);
|
||||
@ -649,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 (grayscale) {
|
||||
if (ginfo->rowBytes == ginfo->width) {
|
||||
// grayscale or monochrome glyph data
|
||||
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
|
||||
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
|
||||
@ -660,6 +724,10 @@ 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) {
|
||||
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];
|
||||
@ -718,6 +786,8 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
|
||||
} else if (glyphMode == MODE_USE_CACHE_LCD) {
|
||||
[mtlc.encoderManager endEncoder];
|
||||
lcdCacheEncoder = nil;
|
||||
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
|
||||
DisableColorGlyphPainting(mtlc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.font;
|
||||
|
||||
import sun.java2d.SurfaceData;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.Raster;
|
||||
|
||||
/**
|
||||
* SurfaceData view for a color glyph from glyph cache
|
||||
*/
|
||||
class ColorGlyphSurfaceData extends SurfaceData {
|
||||
ColorGlyphSurfaceData() {
|
||||
super(State.UNTRACKABLE);
|
||||
initOps();
|
||||
}
|
||||
|
||||
private native void initOps();
|
||||
|
||||
native void setCurrentGlyph(long imgPtr);
|
||||
|
||||
@Override
|
||||
public SurfaceData getReplacement() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration getDeviceConfiguration() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Raster getRaster(int x, int y, int w, int h) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDestination() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -25,10 +25,10 @@
|
||||
|
||||
package sun.font;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.loops.FontInfo;
|
||||
|
||||
/*
|
||||
@ -52,7 +52,7 @@ import sun.java2d.loops.FontInfo;
|
||||
* GlyphList gl = GlyphList.getInstance();
|
||||
* try {
|
||||
* gl.setFromString(info, str, x, y);
|
||||
* int strbounds[] = gl.getBounds();
|
||||
* gl.startGlyphIteration();
|
||||
* int numglyphs = gl.getNumGlyphs();
|
||||
* for (int i = 0; i < numglyphs; i++) {
|
||||
* gl.setGlyphIndex(i);
|
||||
@ -155,6 +155,7 @@ public final class GlyphList {
|
||||
private static final GlyphList reusableGL = new GlyphList();
|
||||
private static final AtomicBoolean inUse = new AtomicBoolean();
|
||||
|
||||
private ColorGlyphSurfaceData glyphSurfaceData;
|
||||
|
||||
void ensureCapacity(int len) {
|
||||
/* Note len must not be -ve! only setFromChars should be capable
|
||||
@ -276,13 +277,9 @@ public final class GlyphList {
|
||||
glyphindex = -1;
|
||||
}
|
||||
|
||||
public int[] getBounds() {
|
||||
/* We co-opt the 5 element array that holds per glyph metrics in order
|
||||
* to return the bounds. So a caller must copy the data out of the
|
||||
* array before calling any other methods on this GlyphList
|
||||
*/
|
||||
public void startGlyphIteration() {
|
||||
if (glyphindex >= 0) {
|
||||
throw new InternalError("calling getBounds after setGlyphIndex");
|
||||
throw new InternalError("glyph iteration restarted");
|
||||
}
|
||||
if (metrics == null) {
|
||||
metrics = new int[5];
|
||||
@ -291,7 +288,18 @@ public final class GlyphList {
|
||||
* Add 0.5f for consistent rounding to pixel position. */
|
||||
gposx = x + 0.5f;
|
||||
gposy = y + 0.5f;
|
||||
fillBounds(metrics);
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called after 'startGlyphIteration'.
|
||||
* Returns overall bounds for glyphs starting from the next glyph
|
||||
* in iteration till the glyph with specified index.
|
||||
* The underlying storage for bounds is shared with metrics,
|
||||
* so this method (and the array it returns) shouldn't be used between
|
||||
* 'setGlyphIndex' call and matching 'getMetrics' call.
|
||||
*/
|
||||
public int[] getBounds(int endGlyphIndex) {
|
||||
fillBounds(metrics, endGlyphIndex);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@ -436,7 +444,7 @@ public final class GlyphList {
|
||||
/* We re-do all this work as we iterate through the glyphs
|
||||
* but it seems unavoidable without re-working the Java TextRenderers.
|
||||
*/
|
||||
private void fillBounds(int[] bounds) {
|
||||
private void fillBounds(int[] bounds, int endGlyphIndex) {
|
||||
/* Faster to access local variables in the for loop? */
|
||||
int xOffset = StrikeCache.topLeftXOffset;
|
||||
int yOffset = StrikeCache.topLeftYOffset;
|
||||
@ -445,7 +453,8 @@ public final class GlyphList {
|
||||
int xAdvOffset = StrikeCache.xAdvanceOffset;
|
||||
int yAdvOffset = StrikeCache.yAdvanceOffset;
|
||||
|
||||
if (len == 0) {
|
||||
int startGlyphIndex = glyphindex + 1;
|
||||
if (startGlyphIndex >= endGlyphIndex) {
|
||||
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0;
|
||||
return;
|
||||
}
|
||||
@ -453,12 +462,12 @@ public final class GlyphList {
|
||||
bx0 = by0 = Float.POSITIVE_INFINITY;
|
||||
bx1 = by1 = Float.NEGATIVE_INFINITY;
|
||||
|
||||
int posIndex = 0;
|
||||
float glx = x + 0.5f;
|
||||
float gly = y + 0.5f;
|
||||
int posIndex = startGlyphIndex<<1;
|
||||
float glx = gposx;
|
||||
float gly = gposy;
|
||||
char gw, gh;
|
||||
float gx, gy, gx0, gy0, gx1, gy1;
|
||||
for (int i=0; i<len; i++) {
|
||||
for (int i=startGlyphIndex; i<endGlyphIndex; i++) {
|
||||
if (images[i] == 0L) {
|
||||
continue;
|
||||
}
|
||||
@ -491,4 +500,24 @@ public final class GlyphList {
|
||||
bounds[2] = (int)Math.floor(bx1);
|
||||
bounds[3] = (int)Math.floor(by1);
|
||||
}
|
||||
|
||||
public static boolean canContainColorGlyphs() {
|
||||
return FontUtilities.isMacOSX;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public SurfaceData getColorGlyphData() {
|
||||
if (glyphSurfaceData == null) {
|
||||
glyphSurfaceData = new ColorGlyphSurfaceData();
|
||||
}
|
||||
glyphSurfaceData.setCurrentGlyph(images[glyphindex]);
|
||||
return glyphSurfaceData;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ import sun.java2d.loops.FontInfo;
|
||||
import sun.java2d.loops.DrawGlyphList;
|
||||
import sun.java2d.loops.DrawGlyphListAA;
|
||||
import sun.java2d.loops.DrawGlyphListLCD;
|
||||
import sun.java2d.loops.DrawGlyphListColor;
|
||||
import sun.java2d.pipe.LoopPipe;
|
||||
import sun.java2d.pipe.ShapeDrawPipe;
|
||||
import sun.java2d.pipe.ParallelogramPipe;
|
||||
@ -892,6 +893,8 @@ public abstract class SurfaceData
|
||||
loops.drawGlyphListLoop = DrawGlyphList.locate(src, comp, dst);
|
||||
loops.drawGlyphListAALoop = DrawGlyphListAA.locate(src, comp, dst);
|
||||
loops.drawGlyphListLCDLoop = DrawGlyphListLCD.locate(src, comp, dst);
|
||||
loops.drawGlyphListColorLoop =
|
||||
DrawGlyphListColor.locate(src, comp, dst);
|
||||
/*
|
||||
System.out.println("drawLine: "+loops.drawLineLoop);
|
||||
System.out.println("fillRect: "+loops.fillRectLoop);
|
||||
|
@ -68,7 +68,8 @@ public class DrawGlyphList extends GraphicsPrimitive {
|
||||
|
||||
|
||||
public native void DrawGlyphList(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList srcData);
|
||||
GlyphList srcData,
|
||||
int fromGlyph, int toGlyph);
|
||||
|
||||
// This instance is used only for lookup.
|
||||
static {
|
||||
@ -94,16 +95,14 @@ public class DrawGlyphList extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public void DrawGlyphList(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList gl) {
|
||||
GlyphList gl, int fromGlyph, int toGlyph) {
|
||||
|
||||
int[] strbounds = gl.getBounds(); // Don't delete, bug 4895493
|
||||
int num = gl.getNumGlyphs();
|
||||
Region clip = sg2d.getCompClip();
|
||||
int cx1 = clip.getLoX();
|
||||
int cy1 = clip.getLoY();
|
||||
int cx2 = clip.getHiX();
|
||||
int cy2 = clip.getHiY();
|
||||
for (int i = 0; i < num; i++) {
|
||||
for (int i = fromGlyph; i < toGlyph; i++) {
|
||||
gl.setGlyphIndex(i);
|
||||
int[] metrics = gl.getMetrics();
|
||||
int gx1 = metrics[0];
|
||||
@ -151,10 +150,10 @@ public class DrawGlyphList extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public void DrawGlyphList(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList glyphs)
|
||||
GlyphList glyphs, int fromGlyph, int toGlyph)
|
||||
{
|
||||
tracePrimitive(target);
|
||||
target.DrawGlyphList(sg2d, dest, glyphs);
|
||||
target.DrawGlyphList(sg2d, dest, glyphs, fromGlyph, toGlyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,8 @@ public class DrawGlyphListAA extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public native void DrawGlyphListAA(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList srcData);
|
||||
GlyphList srcData,
|
||||
int fromGlyph, int toGlyph);
|
||||
|
||||
static {
|
||||
GraphicsPrimitiveMgr.registerGeneral(
|
||||
@ -92,16 +93,14 @@ public class DrawGlyphListAA extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public void DrawGlyphListAA(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList gl)
|
||||
GlyphList gl, int fromGlyph, int toGlyph)
|
||||
{
|
||||
gl.getBounds(); // Don't delete, bug 4895493
|
||||
int num = gl.getNumGlyphs();
|
||||
Region clip = sg2d.getCompClip();
|
||||
int cx1 = clip.getLoX();
|
||||
int cy1 = clip.getLoY();
|
||||
int cx2 = clip.getHiX();
|
||||
int cy2 = clip.getHiY();
|
||||
for (int i = 0; i < num; i++) {
|
||||
for (int i = fromGlyph; i < toGlyph; i++) {
|
||||
gl.setGlyphIndex(i);
|
||||
int[] metrics = gl.getMetrics();
|
||||
int gx1 = metrics[0];
|
||||
@ -149,10 +148,11 @@ public class DrawGlyphListAA extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public void DrawGlyphListAA(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList glyphs)
|
||||
GlyphList glyphs,
|
||||
int fromGlyph, int toGlyph)
|
||||
{
|
||||
tracePrimitive(target);
|
||||
target.DrawGlyphListAA(sg2d, dest, glyphs);
|
||||
target.DrawGlyphListAA(sg2d, dest, glyphs, fromGlyph, toGlyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.loops;
|
||||
|
||||
import sun.font.GlyphList;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.pipe.Region;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Draws color glyphs onto destination surface
|
||||
*/
|
||||
public class DrawGlyphListColor extends GraphicsPrimitive {
|
||||
|
||||
public final static String methodSignature =
|
||||
"DrawGlyphListColor(...)".toString();
|
||||
|
||||
public final static int primTypeID = makePrimTypeID();
|
||||
|
||||
public static DrawGlyphListColor locate(SurfaceType srctype,
|
||||
CompositeType comptype,
|
||||
SurfaceType dsttype)
|
||||
{
|
||||
return (DrawGlyphListColor)
|
||||
GraphicsPrimitiveMgr.locate(primTypeID,
|
||||
srctype, comptype, dsttype);
|
||||
}
|
||||
|
||||
protected DrawGlyphListColor(SurfaceType srctype,
|
||||
CompositeType comptype,
|
||||
SurfaceType dsttype)
|
||||
{
|
||||
super(methodSignature, primTypeID, srctype, comptype, dsttype);
|
||||
}
|
||||
|
||||
|
||||
public void DrawGlyphListColor(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList srcData,
|
||||
int fromGlyph, int toGlyph) {
|
||||
// actual implementation is in the 'General' subclass
|
||||
}
|
||||
|
||||
// This instance is used only for lookup.
|
||||
static {
|
||||
GraphicsPrimitiveMgr.registerGeneral(
|
||||
new DrawGlyphListColor(null, null, null));
|
||||
}
|
||||
|
||||
public GraphicsPrimitive makePrimitive(SurfaceType srctype,
|
||||
CompositeType comptype,
|
||||
SurfaceType dsttype) {
|
||||
return new General(srctype, comptype, dsttype);
|
||||
}
|
||||
|
||||
private static class General extends DrawGlyphListColor {
|
||||
private final Blit blit;
|
||||
|
||||
public General(SurfaceType srctype,
|
||||
CompositeType comptype,
|
||||
SurfaceType dsttype)
|
||||
{
|
||||
super(srctype, comptype, dsttype);
|
||||
blit = Blit.locate(SurfaceType.IntArgbPre,
|
||||
CompositeType.SrcOverNoEa, dsttype);
|
||||
}
|
||||
|
||||
public void DrawGlyphListColor(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList gl, int fromGlyph, int toGlyph) {
|
||||
|
||||
Region clip = sg2d.getCompClip();
|
||||
int cx1 = clip.getLoX();
|
||||
int cy1 = clip.getLoY();
|
||||
int cx2 = clip.getHiX();
|
||||
int cy2 = clip.getHiY();
|
||||
for (int i = fromGlyph; i < toGlyph; i++) {
|
||||
gl.setGlyphIndex(i);
|
||||
int[] metrics = gl.getMetrics();
|
||||
int x = metrics[0];
|
||||
int y = metrics[1];
|
||||
int w = metrics[2];
|
||||
int h = metrics[3];
|
||||
int gx1 = x;
|
||||
int gy1 = y;
|
||||
int gx2 = x + w;
|
||||
int gy2 = y + h;
|
||||
if (gx1 < cx1) gx1 = cx1;
|
||||
if (gy1 < cy1) gy1 = cy1;
|
||||
if (gx2 > cx2) gx2 = cx2;
|
||||
if (gy2 > cy2) gy2 = cy2;
|
||||
if (gx2 > gx1 && gy2 > gy1) {
|
||||
blit.Blit(gl.getColorGlyphData(), dest, AlphaComposite.SrcOver, clip,
|
||||
gx1 - x, gy1 - y, gx1, gy1, gx2 - gx1, gy2 - gy1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GraphicsPrimitive traceWrap() {
|
||||
return new TraceDrawGlyphListColor(this);
|
||||
}
|
||||
|
||||
private static class TraceDrawGlyphListColor extends DrawGlyphListColor {
|
||||
DrawGlyphListColor target;
|
||||
|
||||
public TraceDrawGlyphListColor(DrawGlyphListColor target) {
|
||||
super(target.getSourceType(),
|
||||
target.getCompositeType(),
|
||||
target.getDestType());
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public GraphicsPrimitive traceWrap() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void DrawGlyphListColor(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList glyphs, int fromGlyph, int toGlyph)
|
||||
{
|
||||
tracePrimitive(target);
|
||||
target.DrawGlyphListColor(sg2d, dest, glyphs, fromGlyph, toGlyph);
|
||||
}
|
||||
}
|
||||
}
|
@ -68,7 +68,8 @@ public class DrawGlyphListLCD extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public native void DrawGlyphListLCD(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList srcData);
|
||||
GlyphList srcData,
|
||||
int fromGlyph, int toGlyph);
|
||||
|
||||
public GraphicsPrimitive traceWrap() {
|
||||
return new TraceDrawGlyphListLCD(this);
|
||||
@ -89,10 +90,11 @@ public class DrawGlyphListLCD extends GraphicsPrimitive {
|
||||
}
|
||||
|
||||
public void DrawGlyphListLCD(SunGraphics2D sg2d, SurfaceData dest,
|
||||
GlyphList glyphs)
|
||||
GlyphList glyphs,
|
||||
int fromGlyph, int toGlyph)
|
||||
{
|
||||
tracePrimitive(target);
|
||||
target.DrawGlyphListLCD(sg2d, dest, glyphs);
|
||||
target.DrawGlyphListLCD(sg2d, dest, glyphs, fromGlyph, toGlyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -365,9 +365,10 @@ public final class GeneralRenderer {
|
||||
* reset the glyphs to non-AA after construction.
|
||||
*/
|
||||
static void doDrawGlyphList(SurfaceData sData, PixelWriter pw,
|
||||
GlyphList gl, Region clip)
|
||||
GlyphList gl, int fromGlyph, int toGlyph,
|
||||
Region clip)
|
||||
{
|
||||
int[] bounds = gl.getBounds();
|
||||
int[] bounds = gl.getBounds(toGlyph);
|
||||
clip.clipBoxToBounds(bounds);
|
||||
int cx1 = bounds[0];
|
||||
int cy1 = bounds[1];
|
||||
@ -378,8 +379,7 @@ public final class GeneralRenderer {
|
||||
(WritableRaster) sData.getRaster(cx1, cy1, cx2 - cx1, cy2 - cy1);
|
||||
pw.setRaster(dstRast);
|
||||
|
||||
int num = gl.getNumGlyphs();
|
||||
for (int i = 0; i < num; i++) {
|
||||
for (int i = fromGlyph; i < toGlyph; i++) {
|
||||
gl.setGlyphIndex(i);
|
||||
int[] metrics = gl.getMetrics();
|
||||
int gx1 = metrics[0];
|
||||
@ -971,10 +971,11 @@ class XorDrawGlyphListANY extends DrawGlyphList {
|
||||
}
|
||||
|
||||
public void DrawGlyphList(SunGraphics2D sg2d, SurfaceData sData,
|
||||
GlyphList gl)
|
||||
GlyphList gl, int fromGlyph, int toGlyph)
|
||||
{
|
||||
PixelWriter pw = GeneralRenderer.createXorPixelWriter(sg2d, sData);
|
||||
GeneralRenderer.doDrawGlyphList(sData, pw, gl, sg2d.getCompClip());
|
||||
GeneralRenderer.doDrawGlyphList(sData, pw, gl, fromGlyph, toGlyph,
|
||||
sg2d.getCompClip());
|
||||
}
|
||||
}
|
||||
|
||||
@ -986,10 +987,11 @@ class XorDrawGlyphListAAANY extends DrawGlyphListAA {
|
||||
}
|
||||
|
||||
public void DrawGlyphListAA(SunGraphics2D sg2d, SurfaceData sData,
|
||||
GlyphList gl)
|
||||
GlyphList gl, int fromGlyph, int toGlyph)
|
||||
{
|
||||
PixelWriter pw = GeneralRenderer.createXorPixelWriter(sg2d, sData);
|
||||
GeneralRenderer.doDrawGlyphList(sData, pw, gl, sg2d.getCompClip());
|
||||
GeneralRenderer.doDrawGlyphList(sData, pw, gl, fromGlyph, toGlyph,
|
||||
sg2d.getCompClip());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,4 +52,5 @@ public class RenderLoops {
|
||||
public DrawGlyphList drawGlyphListLoop;
|
||||
public DrawGlyphListAA drawGlyphListAALoop;
|
||||
public DrawGlyphListLCD drawGlyphListLCDLoop;
|
||||
public DrawGlyphListColor drawGlyphListColorLoop;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package sun.java2d.pipe;
|
||||
|
||||
import sun.awt.SunHints;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.font.GlyphList;
|
||||
|
||||
@ -37,8 +38,7 @@ public class AATextRenderer extends GlyphListLoopPipe
|
||||
implements LoopBasedPipe
|
||||
{
|
||||
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
|
||||
sg2d.loops.drawGlyphListAALoop.DrawGlyphListAA(sg2d, sg2d.surfaceData,
|
||||
gl);
|
||||
drawGlyphList(sg2d, gl, SunHints.INTVAL_TEXT_ANTIALIAS_ON);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,20 +40,51 @@ public abstract class GlyphListLoopPipe extends GlyphListPipe
|
||||
{
|
||||
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl,
|
||||
int aaHint) {
|
||||
switch (aaHint) {
|
||||
case SunHints.INTVAL_TEXT_ANTIALIAS_OFF:
|
||||
sg2d.loops.drawGlyphListLoop.
|
||||
DrawGlyphList(sg2d, sg2d.surfaceData, gl);
|
||||
return;
|
||||
case SunHints.INTVAL_TEXT_ANTIALIAS_ON:
|
||||
sg2d.loops.drawGlyphListAALoop.
|
||||
DrawGlyphListAA(sg2d, sg2d.surfaceData, gl);
|
||||
return;
|
||||
case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
|
||||
case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
|
||||
sg2d.loops.drawGlyphListLCDLoop.
|
||||
DrawGlyphListLCD(sg2d,sg2d.surfaceData, gl);
|
||||
return;
|
||||
int prevLimit = 0;
|
||||
boolean isColor = false;
|
||||
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);
|
||||
prevLimit = i;
|
||||
isColor = newIsColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
drawGlyphListSegment(sg2d, gl, prevLimit, len, aaHint, isColor);
|
||||
}
|
||||
|
||||
private void drawGlyphListSegment(SunGraphics2D sg2d, GlyphList gl,
|
||||
int fromglyph, int toGlyph,
|
||||
int aaHint, boolean isColor) {
|
||||
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:
|
||||
sg2d.loops.drawGlyphListLoop.
|
||||
DrawGlyphList(sg2d, sg2d.surfaceData,
|
||||
gl, fromglyph, toGlyph);
|
||||
return;
|
||||
case SunHints.INTVAL_TEXT_ANTIALIAS_ON:
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,10 +25,9 @@
|
||||
|
||||
package sun.java2d.pipe;
|
||||
|
||||
import java.awt.font.GlyphVector;
|
||||
import sun.awt.SunHints;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.font.GlyphList;
|
||||
import static sun.awt.SunHints.*;
|
||||
|
||||
/**
|
||||
* A delegate pipe of SG2D for drawing LCD text with
|
||||
@ -38,7 +37,7 @@ import static sun.awt.SunHints.*;
|
||||
public class LCDTextRenderer extends GlyphListLoopPipe {
|
||||
|
||||
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
|
||||
sg2d.loops.drawGlyphListLCDLoop.
|
||||
DrawGlyphListLCD(sg2d, sg2d.surfaceData, gl);
|
||||
drawGlyphList(sg2d, gl, SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package sun.java2d.pipe;
|
||||
|
||||
import sun.awt.SunHints;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.font.GlyphList;
|
||||
|
||||
@ -37,6 +38,6 @@ public class SolidTextRenderer extends GlyphListLoopPipe
|
||||
implements LoopBasedPipe
|
||||
{
|
||||
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
|
||||
sg2d.loops.drawGlyphListLoop.DrawGlyphList(sg2d, sg2d.surfaceData, gl);
|
||||
drawGlyphList(sg2d, gl, SunHints.INTVAL_TEXT_ANTIALIAS_OFF);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ public class TextRenderer extends GlyphListPipe {
|
||||
int cy2 = clipRegion.getHiY();
|
||||
Object ctx = null;
|
||||
try {
|
||||
int[] bounds = gl.getBounds();
|
||||
gl.startGlyphIteration();
|
||||
int[] bounds = gl.getBounds(num);
|
||||
Rectangle r = new Rectangle(bounds[0], bounds[1],
|
||||
bounds[2] - bounds[0],
|
||||
bounds[3] - bounds[1]);
|
||||
@ -76,7 +77,7 @@ public class TextRenderer extends GlyphListPipe {
|
||||
}
|
||||
if (gx2 > cx2) gx2 = cx2;
|
||||
if (gy2 > cy2) gy2 = cy2;
|
||||
if (gx2 > gx1 && gy2 > gy1 &&
|
||||
if (gx2 > gx1 && gy2 > gy1 && !gl.isColorGlyph(i) &&
|
||||
outpipe.needTile(ctx, gx1, gy1, gx2 - gx1, gy2 - gy1))
|
||||
{
|
||||
byte[] alpha = gl.getGrayBits();
|
||||
|
@ -63,7 +63,8 @@ typedef enum {
|
||||
MODE_USE_CACHE_GRAY,
|
||||
MODE_USE_CACHE_LCD,
|
||||
MODE_NO_CACHE_GRAY,
|
||||
MODE_NO_CACHE_LCD
|
||||
MODE_NO_CACHE_LCD,
|
||||
MODE_NO_CACHE_COLOR
|
||||
} GlyphMode;
|
||||
static GlyphMode glyphMode = MODE_NOT_INITED;
|
||||
|
||||
@ -526,6 +527,7 @@ OGLTR_DisableGlyphModeState()
|
||||
j2d_glDisable(GL_TEXTURE_2D);
|
||||
break;
|
||||
|
||||
case MODE_NO_CACHE_COLOR:
|
||||
case MODE_NO_CACHE_GRAY:
|
||||
case MODE_USE_CACHE_GRAY:
|
||||
case MODE_NOT_INITED:
|
||||
@ -978,6 +980,33 @@ OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps,
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
static jboolean
|
||||
OGLTR_DrawColorGlyphNoCache(OGLContext *oglc, GlyphInfo *ginfo, jint x, jint y)
|
||||
{
|
||||
if (glyphMode != MODE_NO_CACHE_COLOR) {
|
||||
OGLTR_DisableGlyphModeState();
|
||||
RESET_PREVIOUS_OP();
|
||||
glyphMode = MODE_NO_CACHE_COLOR;
|
||||
}
|
||||
|
||||
// see OGLBlitSwToSurface() in OGLBlitLoops.c
|
||||
// for more info on the following two lines
|
||||
j2d_glRasterPos2i(0, 0);
|
||||
j2d_glBitmap(0, 0, 0, 0, (GLfloat) x, (GLfloat) (-y), NULL);
|
||||
|
||||
// in OpenGL image data is assumed to contain lines from bottom to top
|
||||
j2d_glPixelZoom(1, -1);
|
||||
|
||||
j2d_glDrawPixels(ginfo->width, ginfo->height, GL_BGRA, GL_UNSIGNED_BYTE,
|
||||
ginfo->image);
|
||||
|
||||
// restoring state
|
||||
j2d_glPixelZoom(1, 1);
|
||||
|
||||
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))
|
||||
@ -1028,7 +1057,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
|
||||
for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
|
||||
jint x, y;
|
||||
jfloat glyphx, glyphy;
|
||||
jboolean grayscale, ok;
|
||||
jboolean ok;
|
||||
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
|
||||
|
||||
if (ginfo == NULL) {
|
||||
@ -1038,8 +1067,6 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
|
||||
break;
|
||||
}
|
||||
|
||||
grayscale = (ginfo->rowBytes == ginfo->width);
|
||||
|
||||
if (usePositions) {
|
||||
jfloat posx = NEXT_FLOAT(positions);
|
||||
jfloat posy = NEXT_FLOAT(positions);
|
||||
@ -1060,7 +1087,7 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (grayscale) {
|
||||
if (ginfo->rowBytes == ginfo->width) {
|
||||
// grayscale or monochrome glyph data
|
||||
if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
|
||||
ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
|
||||
@ -1069,6 +1096,9 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
|
||||
} else {
|
||||
ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);
|
||||
}
|
||||
} else if (ginfo->rowBytes == ginfo->width * 4) {
|
||||
// color glyph data
|
||||
ok = OGLTR_DrawColorGlyphNoCache(oglc, ginfo, x, y);
|
||||
} else {
|
||||
// LCD-optimized glyph data
|
||||
jint rowBytesOffset = 0;
|
||||
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni_util.h"
|
||||
#include "fontscalerdefs.h"
|
||||
#include "SurfaceData.h"
|
||||
|
||||
typedef struct _GlyphOps {
|
||||
SurfaceDataOps sdOps;
|
||||
GlyphInfo* glyph;
|
||||
} GlyphOps;
|
||||
|
||||
static jint Glyph_Lock(JNIEnv *env,
|
||||
SurfaceDataOps *ops,
|
||||
SurfaceDataRasInfo *pRasInfo,
|
||||
jint lockflags)
|
||||
{
|
||||
SurfaceDataBounds bounds;
|
||||
GlyphInfo *glyph;
|
||||
if (lockflags &
|
||||
(SD_LOCK_WRITE | SD_LOCK_LUT | SD_LOCK_INVCOLOR | SD_LOCK_INVGRAY)) {
|
||||
JNU_ThrowInternalError(env, "Unsupported mode for glyph image surface");
|
||||
return SD_FAILURE;
|
||||
}
|
||||
glyph = ((GlyphOps*)ops)->glyph;
|
||||
bounds.x1 = 0;
|
||||
bounds.y1 = 0;
|
||||
bounds.x2 = glyph->width;
|
||||
bounds.y2 = glyph->height;
|
||||
SurfaceData_IntersectBounds(&pRasInfo->bounds, &bounds);
|
||||
return SD_SUCCESS;
|
||||
}
|
||||
|
||||
static void Glyph_GetRasInfo(JNIEnv *env,
|
||||
SurfaceDataOps *ops,
|
||||
SurfaceDataRasInfo *pRasInfo)
|
||||
{
|
||||
GlyphInfo *glyph = ((GlyphOps*)ops)->glyph;
|
||||
|
||||
pRasInfo->rasBase = glyph->image;
|
||||
pRasInfo->pixelBitOffset = 0;
|
||||
pRasInfo->pixelStride = 4;
|
||||
pRasInfo->scanStride = glyph->rowBytes;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_font_ColorGlyphSurfaceData_initOps(JNIEnv *env,
|
||||
jobject sData)
|
||||
{
|
||||
GlyphOps *ops =
|
||||
(GlyphOps*) SurfaceData_InitOps(env, sData, sizeof(GlyphOps));
|
||||
if (ops == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env,
|
||||
"Initialization of ColorGlyphSurfaceData failed");
|
||||
return;
|
||||
}
|
||||
ops->sdOps.Lock = Glyph_Lock;
|
||||
ops->sdOps.GetRasInfo = Glyph_GetRasInfo;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_font_ColorGlyphSurfaceData_setCurrentGlyph(JNIEnv *env,
|
||||
jobject sData,
|
||||
jlong imgPtr)
|
||||
{
|
||||
GlyphOps *ops = (GlyphOps*) SurfaceData_GetOps(env, sData);
|
||||
if (ops == NULL) {
|
||||
return;
|
||||
}
|
||||
ops->glyph = (GlyphInfo*) jlong_to_ptr(imgPtr);
|
||||
}
|
@ -50,7 +50,8 @@
|
||||
#define FLOOR_ASSIGN(l, r)\
|
||||
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
|
||||
|
||||
GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
int g;
|
||||
size_t bytesNeeded;
|
||||
@ -61,7 +62,7 @@ GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
|
||||
jfloat x = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListX);
|
||||
jfloat y = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListY);
|
||||
jint len = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
|
||||
jint len = toGlyph - fromGlyph;
|
||||
jlongArray glyphImages = (jlongArray)
|
||||
(*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphImages);
|
||||
jfloatArray glyphPositions =
|
||||
@ -84,13 +85,8 @@ GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
return (GlyphBlitVector*)NULL;
|
||||
}
|
||||
|
||||
/* Add 0.5 to x and y and then use floor (or an equivalent operation)
|
||||
* to round down the glyph positions to integral pixel positions.
|
||||
*/
|
||||
x += 0.5f;
|
||||
y += 0.5f;
|
||||
if (glyphPositions) {
|
||||
int n = -1;
|
||||
int n = fromGlyph * 2 - 1;
|
||||
|
||||
positions =
|
||||
(*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);
|
||||
@ -105,7 +101,7 @@ GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
jfloat px = x + positions[++n];
|
||||
jfloat py = y + positions[++n];
|
||||
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g]);
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g + fromGlyph]);
|
||||
gbv->glyphs[g].glyphInfo = ginfo;
|
||||
gbv->glyphs[g].pixels = ginfo->image;
|
||||
gbv->glyphs[g].width = ginfo->width;
|
||||
@ -118,7 +114,7 @@ GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
positions, JNI_ABORT);
|
||||
} else {
|
||||
for (g=0; g<len; g++) {
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g]);
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g + fromGlyph]);
|
||||
gbv->glyphs[g].glyphInfo = ginfo;
|
||||
gbv->glyphs[g].pixels = ginfo->image;
|
||||
gbv->glyphs[g].width = ginfo->width;
|
||||
@ -135,6 +131,12 @@ GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,
|
||||
JNI_ABORT);
|
||||
|
||||
if (!glyphPositions) {
|
||||
(*env)->SetFloatField(env, glyphlist, sunFontIDs.glyphListX, x);
|
||||
(*env)->SetFloatField(env, glyphlist, sunFontIDs.glyphListY, y);
|
||||
}
|
||||
|
||||
return gbv;
|
||||
}
|
||||
|
||||
@ -305,12 +307,13 @@ static void drawGlyphListLCD(JNIEnv *env, jobject self,
|
||||
/*
|
||||
* Class: sun_java2d_loops_DrawGlyphList
|
||||
* Method: DrawGlyphList
|
||||
* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V
|
||||
* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_loops_DrawGlyphList_DrawGlyphList
|
||||
(JNIEnv *env, jobject self,
|
||||
jobject sg2d, jobject sData, jobject glyphlist) {
|
||||
jobject sg2d, jobject sData, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
jint pixel, color;
|
||||
GlyphBlitVector* gbv;
|
||||
@ -320,7 +323,7 @@ Java_sun_java2d_loops_DrawGlyphList_DrawGlyphList
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {
|
||||
if ((gbv = setupBlitVector(env, glyphlist, fromGlyph, toGlyph)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -335,12 +338,13 @@ Java_sun_java2d_loops_DrawGlyphList_DrawGlyphList
|
||||
/*
|
||||
* Class: sun_java2d_loops_DrawGlyphListAA
|
||||
* Method: DrawGlyphListAA
|
||||
* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V
|
||||
* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA
|
||||
(JNIEnv *env, jobject self,
|
||||
jobject sg2d, jobject sData, jobject glyphlist) {
|
||||
jobject sg2d, jobject sData, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
jint pixel, color;
|
||||
GlyphBlitVector* gbv;
|
||||
@ -350,7 +354,7 @@ Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {
|
||||
if ((gbv = setupBlitVector(env, glyphlist, fromGlyph, toGlyph)) == NULL) {
|
||||
return;
|
||||
}
|
||||
pixel = GrPrim_Sg2dGetPixel(env, sg2d);
|
||||
@ -363,12 +367,13 @@ Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA
|
||||
/*
|
||||
* Class: sun_java2d_loops_DrawGlyphListLCD
|
||||
* Method: DrawGlyphListLCD
|
||||
* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V
|
||||
* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD
|
||||
(JNIEnv *env, jobject self,
|
||||
jobject sg2d, jobject sData, jobject glyphlist) {
|
||||
jobject sg2d, jobject sData, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
jint pixel, color, contrast;
|
||||
jboolean rgbOrder;
|
||||
@ -379,7 +384,8 @@ Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gbv = setupLCDBlitVector(env, glyphlist)) == NULL) {
|
||||
if ((gbv = setupLCDBlitVector(env, glyphlist, fromGlyph, toGlyph))
|
||||
== NULL) {
|
||||
return;
|
||||
}
|
||||
pixel = GrPrim_Sg2dGetPixel(env, sg2d);
|
||||
@ -481,7 +487,8 @@ Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD
|
||||
* rendered fractional metrics, there's typically more space between the
|
||||
* glyphs. Perhaps disabling X-axis grid-fitting will help with that.
|
||||
*/
|
||||
GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
int g;
|
||||
size_t bytesNeeded;
|
||||
@ -492,7 +499,7 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
|
||||
jfloat x = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListX);
|
||||
jfloat y = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListY);
|
||||
jint len = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
|
||||
jint len = toGlyph - fromGlyph;
|
||||
jlongArray glyphImages = (jlongArray)
|
||||
(*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphImages);
|
||||
jfloatArray glyphPositions =
|
||||
@ -531,7 +538,7 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
* heterogenous
|
||||
*/
|
||||
if (subPixPos && len > 0) {
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[0]);
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[fromGlyph]);
|
||||
if (ginfo == NULL) {
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
|
||||
imagePtrs, JNI_ABORT);
|
||||
@ -543,16 +550,9 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
subPixPos = JNI_FALSE;
|
||||
}
|
||||
}
|
||||
if (subPixPos) {
|
||||
x += 0.1666667f;
|
||||
y += 0.1666667f;
|
||||
} else {
|
||||
x += 0.5f;
|
||||
y += 0.5f;
|
||||
}
|
||||
|
||||
if (glyphPositions) {
|
||||
int n = -1;
|
||||
int n = fromGlyph * 2 - 1;
|
||||
|
||||
positions =
|
||||
(*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);
|
||||
@ -566,7 +566,7 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
for (g=0; g<len; g++) {
|
||||
jfloat px, py;
|
||||
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g]);
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g + fromGlyph]);
|
||||
if (ginfo == NULL) {
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
|
||||
imagePtrs, JNI_ABORT);
|
||||
@ -608,7 +608,12 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
*/
|
||||
if (subPixPos) {
|
||||
int frac;
|
||||
float pos = px + ginfo->topLeftX;
|
||||
float pos;
|
||||
|
||||
px += 0.1666667f - 0.5f;
|
||||
py += 0.1666667f - 0.5f;
|
||||
|
||||
pos = px + ginfo->topLeftX;
|
||||
FLOOR_ASSIGN(gbv->glyphs[g].x, pos);
|
||||
/* Calculate the fractional pixel position - ie the subpixel
|
||||
* position within the RGB/BGR triple. We are rounding to
|
||||
@ -647,7 +652,9 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
positions, JNI_ABORT);
|
||||
} else {
|
||||
for (g=0; g<len; g++) {
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g]);
|
||||
jfloat px = x;
|
||||
jfloat py = y;
|
||||
ginfo = (GlyphInfo*)((uintptr_t)imagePtrs[g + fromGlyph]);
|
||||
if (ginfo == NULL) {
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
|
||||
imagePtrs, JNI_ABORT);
|
||||
@ -662,7 +669,12 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
|
||||
if (subPixPos) {
|
||||
int frac;
|
||||
float pos = x + ginfo->topLeftX;
|
||||
float pos;
|
||||
|
||||
px += 0.1666667f - 0.5f;
|
||||
py += 0.1666667f - 0.5f;
|
||||
|
||||
pos = px + ginfo->topLeftX;
|
||||
FLOOR_ASSIGN(gbv->glyphs[g].x, pos);
|
||||
frac = (int)((pos - gbv->glyphs[g].x)*3);
|
||||
if (frac == 0) {
|
||||
@ -672,10 +684,11 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
gbv->glyphs[g].x += 1;
|
||||
}
|
||||
} else {
|
||||
FLOOR_ASSIGN(gbv->glyphs[g].x, x + ginfo->topLeftX);
|
||||
FLOOR_ASSIGN(gbv->glyphs[g].x, px + ginfo->topLeftX);
|
||||
gbv->glyphs[g].rowBytesOffset = 0;
|
||||
}
|
||||
FLOOR_ASSIGN(gbv->glyphs[g].y, y + ginfo->topLeftY);
|
||||
FLOOR_ASSIGN(gbv->glyphs[g].y, py + ginfo->topLeftY);
|
||||
|
||||
/* copy image data into this array at x/y locations */
|
||||
x += ginfo->advanceX;
|
||||
y += ginfo->advanceY;
|
||||
@ -684,6 +697,11 @@ GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
|
||||
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,
|
||||
JNI_ABORT);
|
||||
if (!glyphPositions) {
|
||||
(*env)->SetFloatField(env, glyphlist, sunFontIDs.glyphListX, x);
|
||||
(*env)->SetFloatField(env, glyphlist, sunFontIDs.glyphListY, y);
|
||||
}
|
||||
|
||||
return gbv;
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,10 @@ typedef struct {
|
||||
} GlyphBlitVector;
|
||||
|
||||
extern jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds);
|
||||
extern GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist);
|
||||
extern GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist);
|
||||
extern GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph);
|
||||
extern GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -173,9 +173,9 @@ static void initFontIDs(JNIEnv *env) {
|
||||
|
||||
CHECK_NULL(tmpClass = (*env)->FindClass(env, "sun/font/GlyphList"));
|
||||
CHECK_NULL(sunFontIDs.glyphListX =
|
||||
(*env)->GetFieldID(env, tmpClass, "x", "F"));
|
||||
(*env)->GetFieldID(env, tmpClass, "gposx", "F"));
|
||||
CHECK_NULL(sunFontIDs.glyphListY =
|
||||
(*env)->GetFieldID(env, tmpClass, "y", "F"));
|
||||
(*env)->GetFieldID(env, tmpClass, "gposy", "F"));
|
||||
CHECK_NULL(sunFontIDs.glyphListLen =
|
||||
(*env)->GetFieldID(env, tmpClass, "len", "I"));
|
||||
CHECK_NULL(sunFontIDs.glyphImages =
|
||||
|
@ -77,6 +77,7 @@ public class X11TextRenderer extends GlyphListPipe {
|
||||
Region clip = sg2d.getCompClip();
|
||||
long xgc = x11sd.getRenderGC(clip, SunGraphics2D.COMP_ISCOPY,
|
||||
null, sg2d.pixel);
|
||||
gl.startGlyphIteration();
|
||||
doDrawGlyphList(x11sd.getNativeOps(), xgc, clip, gl);
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
|
@ -83,7 +83,7 @@ public class XRTextRenderer extends GlyphListPipe {
|
||||
int activeGlyphSet = cachedGlyphs[0].getGlyphSet();
|
||||
|
||||
int eltIndex = -1;
|
||||
gl.getBounds();
|
||||
gl.startGlyphIteration();
|
||||
float[] positions = gl.getPositions();
|
||||
for (int i = 0; i < gl.getNumGlyphs(); i++) {
|
||||
gl.setGlyphIndex(i);
|
||||
|
@ -57,11 +57,13 @@ JNIEXPORT void JNICALL Java_sun_font_X11TextRenderer_doDrawGlyphList
|
||||
jlong dstData, jlong xgc, jobject clip,
|
||||
jobject glyphlist)
|
||||
{
|
||||
jint glyphCount;
|
||||
GlyphBlitVector* gbv;
|
||||
SurfaceDataBounds bounds;
|
||||
Region_GetBounds(env, clip, &bounds);
|
||||
|
||||
if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {
|
||||
glyphCount = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
|
||||
if ((gbv = setupBlitVector(env, glyphlist, 0, glyphCount)) == NULL) {
|
||||
return;
|
||||
}
|
||||
if (!RefineBounds(gbv, &bounds)) {
|
||||
|
108
test/jdk/java/awt/font/MacEmoji.java
Normal file
108
test/jdk/java/awt/font/MacEmoji.java
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @key headful
|
||||
* @bug 8263583
|
||||
* @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) {
|
||||
GraphicsConfiguration cfg
|
||||
= GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||
.getDefaultScreenDevice().getDefaultConfiguration();
|
||||
|
||||
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 (refImg.getRGB(x, y) != 0xFFFFFFFF) {
|
||||
rendered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user