@@ -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 };
327361static 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
330384static inline CGGI_RenderingMode
331385CGGI_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 */
465519static 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
493560static inline GlyphInfo *
494561CGGI_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 */
560622static inline void
561623CGGI_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 ;
0 commit comments