Skip to content

Commit 690696b

Browse files
committed
JRE-118 Emoji support
port commits 5dcafa4d, 70e14949 from JBR 9
1 parent cbf4d30 commit 690696b

31 files changed

+683
-133
lines changed

src/java.desktop/macosx/classes/sun/font/CFont.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ String getNativeFontName() {
246246
return nativeFontName;
247247
}
248248

249+
@Override
250+
protected boolean isAAT() {
251+
// using CoreText layout in Harfbuzz code leads to wrong advances for emoji glyphs
252+
return !"AppleColorEmoji".equals(nativeFontName) && super.isAAT();
253+
}
254+
249255
// <rdar://problem/5321707> sun.font.Font2D caches the last used strike,
250256
// but does not check if the properties of the strike match the properties
251257
// of the incoming java.awt.Font object (size, style, etc).

src/java.desktop/macosx/classes/sun/font/CFontManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,9 @@ protected FontUIResource getFontConfigFUIR(
308308
@Override
309309
protected void populateFontFileNameMap(HashMap<String, String> fontToFileMap, HashMap<String, String> fontToFamilyNameMap,
310310
HashMap<String, ArrayList<String>> familyToFontListMap, Locale locale) {}
311+
312+
@Override
313+
public boolean areColorGlyphsSupported() {
314+
return true;
315+
}
311316
}

src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,19 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
156156
// to indicate we should use CoreText to substitute the character
157157
CGGlyph glyph;
158158
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
159-
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
159+
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
160+
if (CGGI_IsColorFont(cgFallback)) {
161+
CGAffineTransform matrix = awtStrike->fAltTx;
162+
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
163+
CTFontRef font = CTFontCreateWithGraphicsFont(cgFallback, fontSize, NULL, NULL);
164+
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
165+
CFRelease(font);
166+
advance.width /= fontSize;
167+
advance.height /= fontSize;
168+
} else {
169+
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
170+
}
171+
CFRelease(cgFallback);
160172
CFRelease(fallback);
161173
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
162174
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {

src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
3434
const AWTStrike *strike,
3535
jint rawGlyphCodes[], const CFIndex len);
3636

37+
bool CGGI_IsColorFont(CGFontRef font);
38+
3739
#endif /* __CGGLYPHIMAGES_H */

src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m

Lines changed: 131 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,40 @@ @implementation CGGI_GlyphCanvas
309309
}
310310
}
311311

312+
static void
313+
CGGI_CopyImageFromCanvasToARGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
314+
{
315+
CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(canvas->context);
316+
bool littleEndian = (bitmapInfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little;
317+
318+
UInt32 *src = (UInt32 *)canvas->image->data;
319+
size_t srcRowWidth = canvas->image->width;
320+
321+
UInt8 *dest = (UInt8 *)info->image;
322+
size_t destRowWidth = info->width;
323+
324+
size_t height = info->height;
325+
326+
size_t y;
327+
328+
for (y = 0; y < height; y++) {
329+
size_t srcRow = y * srcRowWidth;
330+
if (littleEndian) {
331+
UInt16 destRowBytes = info->rowBytes;
332+
memcpy(dest, src + srcRow, destRowBytes);
333+
dest += destRowBytes;
334+
} else {
335+
size_t x;
336+
for (x = 0; x < destRowWidth; x++) {
337+
UInt32 p = src[srcRow + x];
338+
*dest++ = (p >> 24 & 0xFF); // blue (alpha-premultiplied)
339+
*dest++ = (p >> 16 & 0xFF); // green (alpha-premultiplied)
340+
*dest++ = (p >> 8 & 0xFF); // red (alpha-premultiplied)
341+
*dest++ = (p & 0xFF); // alpha
342+
}
343+
}
344+
}
345+
}
312346

313347
#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
314348

@@ -326,6 +360,26 @@ @implementation CGGI_GlyphCanvas
326360
{ 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
327361
static CGGI_GlyphInfoDescriptor rgb =
328362
{ 3, &CGGI_CopyImageFromCanvasToRGBInfo };
363+
static CGGI_GlyphInfoDescriptor argb =
364+
{ 4, &CGGI_CopyImageFromCanvasToARGBInfo };
365+
366+
static CFStringRef EMOJI_FONT_NAME = CFSTR("Apple Color Emoji");
367+
368+
bool CGGI_IsColorFont(CGFontRef font)
369+
{
370+
CFStringRef name = CGFontCopyFullName(font);
371+
if (name == NULL) return false;
372+
bool isFixedColor = CFStringCompare(name, EMOJI_FONT_NAME, 0) == kCFCompareEqualTo;
373+
CFRelease(name);
374+
return isFixedColor;
375+
}
376+
377+
static inline CGGI_GlyphInfoDescriptor*
378+
CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CGFontRef font)
379+
{
380+
bool isFixedColor = CGGI_IsColorFont(font);
381+
return isFixedColor ? &argb : mode->glyphDescriptor;
382+
}
329383

330384
static inline CGGI_RenderingMode
331385
CGGI_GetRenderingMode(const AWTStrike *strike)
@@ -459,11 +513,11 @@ @implementation CGGI_GlyphCanvas
459513
}
460514

461515
/*
462-
* Clear the canvas by blitting white only into the region of interest
516+
* Clear the canvas by blitting white (or transparent background for color glyphs) only into the region of interest
463517
* (the rect which we will copy out of once the glyph is struck).
464518
*/
465519
static inline void
466-
CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
520+
CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info, bool transparent)
467521
{
468522
vImage_Buffer canvasRectToClear;
469523
canvasRectToClear.data = canvas->image->data;
@@ -474,28 +528,41 @@ @implementation CGGI_GlyphCanvas
474528

475529
// clean the canvas
476530
#ifdef CGGI_DEBUG
477-
Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
531+
Pixel_8888 background = { 0xE0, 0xE0, 0xE0, 0xE0 };
478532
#else
479-
Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
533+
Pixel_8888 background = { transparent ? 0 : 0xFF,
534+
transparent ? 0 : 0xFF,
535+
transparent ? 0 : 0xFF,
536+
transparent ? 0 : 0xFF };
480537
#endif
481538

482-
// clear canvas background and set foreground color
483-
vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
539+
// clear canvas background
540+
vImageBufferFill_ARGB8888(&canvasRectToClear, background, kvImageNoFlags);
484541
}
485542

486543

487544
#pragma mark --- GlyphInfo Creation & Copy Functions ---
488545

546+
static inline CGSize CGGI_ScaleAdvance(CGSize advance, const AWTStrike *strike) {
547+
advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
548+
if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
549+
advance.width = round(advance.width);
550+
advance.height = round(advance.height);
551+
}
552+
advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
553+
return advance;
554+
}
555+
489556
/*
490557
* Creates a GlyphInfo with exactly the correct size image and measurements.
491558
*/
492559
#define CGGI_GLYPH_BBOX_PADDING 2.0f
493560
static inline GlyphInfo *
494561
CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
495562
const AWTStrike *strike,
496-
const CGGI_RenderingMode *mode)
563+
const CGGI_GlyphInfoDescriptor *glyphDescriptor)
497564
{
498-
size_t pixelSize = mode->glyphDescriptor->pixelSize;
565+
size_t pixelSize = glyphDescriptor->pixelSize;
499566

500567
// adjust the bounding box to be 1px bigger on each side than what
501568
// CGFont-whatever suggests - because it gives a bounding box that
@@ -515,12 +582,7 @@ @implementation CGGI_GlyphCanvas
515582
width = 1;
516583
height = 1;
517584
}
518-
advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
519-
if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
520-
advance.width = round(advance.width);
521-
advance.height = round(advance.height);
522-
}
523-
advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
585+
advance = CGGI_ScaleAdvance(advance, strike);
524586

525587
#ifdef USE_IMAGE_ALIGNED_MEMORY
526588
// create separate memory
@@ -559,8 +621,8 @@ @implementation CGGI_GlyphCanvas
559621
*/
560622
static inline void
561623
CGGI_CreateImageForGlyph
562-
(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
563-
GlyphInfo *info, const CGGI_RenderingMode *mode)
624+
(CGFontRef cgFont, CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
625+
GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike)
564626
{
565627
if (isnan(info->topLeftX) || isnan(info->topLeftY)) {
566628
// Explicitly set glyphInfo width/height to be 0 to ensure
@@ -569,20 +631,53 @@ @implementation CGGI_GlyphCanvas
569631
info->height = 0;
570632

571633
// copy the "empty" glyph from the canvas into the info
572-
(*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
634+
(*glyphDescriptor->copyFxnPtr)(canvas, info);
573635
return;
574636
}
575637

576638
// clean the canvas
577-
CGGI_ClearCanvas(canvas, info);
639+
CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb);
578640

579641
// strike the glyph in the upper right corner
580-
CGContextShowGlyphsAtPoint(canvas->context,
581-
-info->topLeftX,
582-
canvas->image->height + info->topLeftY,
583-
&glyph, 1);
642+
CGFloat x = -info->topLeftX;
643+
CGFloat y = canvas->image->height + info->topLeftY;
644+
645+
if (glyphDescriptor == &argb) {
646+
CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context);
647+
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
648+
CTFontRef font = CTFontCreateWithGraphicsFont(cgFont, fontSize, NULL, NULL);
649+
650+
CGFloat normFactor = 1.0 / fontSize;
651+
CGAffineTransform normMatrix = CGAffineTransformScale(matrix, normFactor, normFactor);
652+
CGContextSetTextMatrix(canvas->context, normMatrix);
653+
654+
CGPoint userPoint = CGPointMake(x, y);
655+
CGAffineTransform invNormMatrix = CGAffineTransformInvert(normMatrix);
656+
CGPoint textPoint = CGPointApplyAffineTransform(userPoint, invNormMatrix);
657+
658+
CTFontDrawGlyphs(font, &glyph, &textPoint, 1, canvas->context);
659+
660+
// CTFontGetAdvancesForGlyphs returns rounded advance for emoji font, so it can be calculated only here
661+
// where CTFont instance with actual size is available
662+
CGSize advance;
663+
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
664+
advance.width /= fontSize;
665+
advance.height /= fontSize;
666+
advance = CGGI_ScaleAdvance(advance, strike);
667+
info->advanceX = advance.width;
668+
info->advanceY = advance.height;
669+
670+
CFRelease(font);
671+
// restore context's original state
672+
CGContextSetTextMatrix(canvas->context, matrix);
673+
CGContextSetFontSize(canvas->context, 1);
674+
} else {
675+
// old procedure is faster, so we use it by default
676+
CGContextShowGlyphsAtPoint(canvas->context, x, y, &glyph, 1);
677+
}
678+
584679
// copy the glyph from the canvas into the info
585-
(*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
680+
(*glyphDescriptor->copyFxnPtr)(canvas, info);
586681
}
587682

588683
/*
@@ -620,21 +715,23 @@ @implementation CGGI_GlyphCanvas
620715
CGSize advance;
621716
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
622717

718+
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
719+
CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, cgFallback);
720+
623721
// create the Sun2D GlyphInfo we are going to strike into
624-
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
722+
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor);
625723

626724
// fix the context size, just in case the substituted character is unexpectedly large
627725
CGGI_SizeCanvas(canvas, info->width, info->height, mode);
628726

629727
// align the transform for the real CoreText strike
630728
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
631729

632-
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
633730
CGContextSetFont(canvas->context, cgFallback);
634731
CFRelease(cgFallback);
635732

636733
// clean the canvas - align, strike, and copy the glyph from the canvas into the info
637-
CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
734+
CGGI_CreateImageForGlyph(cgFallback, canvas, glyph, info, glyphDescriptor, strike);
638735

639736
// restore the state of the world
640737
CGContextRestoreGState(canvas->context);
@@ -677,11 +774,14 @@ @implementation CGGI_GlyphCanvas
677774
CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
678775
JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
679776

777+
CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, strike->fAWTFont->fNativeCGFont);
778+
680779
CFIndex i;
681780
for (i = 0; i < len; i++) {
682781
GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
683782
if (info != NULL) {
684-
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
783+
CGGI_CreateImageForGlyph(strike->fAWTFont->fNativeCGFont,
784+
canvas, glyphs[i], info, mainFontDescriptor, strike);
685785
} else {
686786
info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
687787
glyphInfos[i] = ptr_to_jlong(info);
@@ -742,7 +842,7 @@ @implementation CGGI_GlyphCanvas
742842

743843
NSString* theKey = (mode->glyphDescriptor == &rgb) ?
744844
threadLocalLCDCanvasKey : threadLocalAACanvasKey;
745-
845+
746846
CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
747847
if (canvas == nil) {
748848
canvas = [[CGGI_GlyphCanvas alloc] init];
@@ -780,6 +880,8 @@ @implementation CGGI_GlyphCanvas
780880
size_t maxWidth = 1;
781881
size_t maxHeight = 1;
782882

883+
CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, strike->fAWTFont->fNativeCGFont);
884+
783885
CFIndex i;
784886
for (i = 0; i < len; i++)
785887
{
@@ -792,7 +894,7 @@ @implementation CGGI_GlyphCanvas
792894
CGSize advance = advances[i];
793895
CGRect bbox = bboxes[i];
794896

795-
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
897+
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor);
796898

797899
if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
798900
if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2000-2018 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sun.font;
18+
19+
import sun.java2d.SurfaceData;
20+
21+
import java.awt.GraphicsConfiguration;
22+
import java.awt.Rectangle;
23+
import java.awt.image.Raster;
24+
25+
class ColorGlyphSurfaceData extends SurfaceData {
26+
ColorGlyphSurfaceData() {
27+
super(State.UNTRACKABLE);
28+
initOps();
29+
}
30+
31+
private native void initOps();
32+
33+
native void setCurrentGlyph(long imgPtr);
34+
35+
@Override
36+
public SurfaceData getReplacement() {
37+
throw new UnsupportedOperationException();
38+
}
39+
40+
@Override
41+
public GraphicsConfiguration getDeviceConfiguration() {
42+
throw new UnsupportedOperationException();
43+
}
44+
45+
@Override
46+
public Raster getRaster(int x, int y, int w, int h) {
47+
throw new UnsupportedOperationException();
48+
}
49+
50+
@Override
51+
public Rectangle getBounds() {
52+
throw new UnsupportedOperationException();
53+
}
54+
55+
@Override
56+
public Object getDestination() {
57+
throw new UnsupportedOperationException();
58+
}
59+
}

0 commit comments

Comments
 (0)