From: Jonathan Kew bug 715798 pt 1 - support Apple Color Emoji font in cairo-quartz backend. r=jrmuizel diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c --- a/gfx/cairo/cairo/src/cairo-quartz-font.c +++ b/gfx/cairo/cairo/src/cairo-quartz-font.c @@ -85,16 +85,20 @@ typedef struct { int descent; int leading; } quartz_CGFontMetrics; static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; +/* CTFontCreateWithGraphicsFont is not public until 10.5. */ +typedef const struct __CTFontDescriptor *CTFontDescriptorRef; +static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL; + static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; static void quartz_font_ensure_symbols(void) { if (_cairo_quartz_font_symbol_lookup_done) return; @@ -122,16 +126,18 @@ quartz_font_ensure_symbols(void) CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading"); CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); + if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && CGFontGetGlyphBBoxesPtr && CGFontGetGlyphsForUnicharsPtr && CGFontGetUnitsPerEmPtr && CGFontGetGlyphAdvancesPtr && CGFontGetGlyphPathPtr && (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) _cairo_quartz_font_symbols_present = TRUE; @@ -145,16 +151,17 @@ typedef struct _cairo_quartz_scaled_font struct _cairo_quartz_scaled_font { cairo_scaled_font_t base; }; struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; + CTFontRef ctFont; }; /* * font face backend */ static cairo_status_t _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, @@ -229,16 +236,20 @@ static cairo_status_t return CAIRO_STATUS_SUCCESS; } static void _cairo_quartz_font_face_destroy (void *abstract_face) { cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; + if (font_face->ctFont) { + CFRelease (font_face->ctFont); + } + CGFontRelease (font_face->cgFont); } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; static cairo_status_t _cairo_quartz_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, @@ -353,16 +364,22 @@ cairo_quartz_font_face_create_for_cgfont if (!font_face) { cairo_status_t ignore_status; ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } font_face->cgFont = CGFontRetain (font); + if (CTFontCreateWithGraphicsFontPtr) { + font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL); + } else { + font_face->ctFont = NULL; + } + _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); return &font_face->base; } /* * scaled font backend */ @@ -772,16 +789,24 @@ static const cairo_scaled_font_backend_t CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) { cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); return ffont->cgFont; } +CTFontRef +_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font) +{ + cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); + + return ffont->ctFont; +} + #ifndef __LP64__ /* * compat with old ATSUI backend */ /** * cairo_quartz_font_face_create_for_atsu_font_id * @font_id: an ATSUFontID for the font. diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h --- a/gfx/cairo/cairo/src/cairo-quartz-private.h +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h @@ -45,16 +45,19 @@ #include "cairo-surface-clipper-private.h" #ifdef CGFLOAT_DEFINED typedef CGFloat cairo_quartz_float_t; #else typedef float cairo_quartz_float_t; #endif +/* define CTFontRef for pre-10.5 SDKs */ +typedef const struct __CTFont *CTFontRef; + typedef struct cairo_quartz_surface { cairo_surface_t base; CGContextRef cgContext; CGAffineTransform cgContextBaseCTM; void *imageData; cairo_surface_t *imageSurfaceEquiv; @@ -99,15 +102,18 @@ CGImageRef cairo_bool_t interpolate, CGColorSpaceRef colorSpaceOverride, CGDataProviderReleaseDataCallback releaseCallback, void *releaseInfo); CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); +CTFontRef +_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont); + #else # error Cairo was not compiled with support for the quartz backend #endif /* CAIRO_HAS_QUARTZ_SURFACE */ #endif /* CAIRO_QUARTZ_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -130,16 +130,19 @@ static void (*CGContextClipToMaskPtr) (C static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL; static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; +/* CTFontDrawGlyphs is not available until 10.7 */ +static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL; + static SInt32 _cairo_quartz_osx_version = 0x0; static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; /* * Utility functions */ @@ -167,16 +170,18 @@ static void quartz_ensure_symbols(void) CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage"); CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType"); CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts"); CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath"); CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); + CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); + if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { // assume 10.5 _cairo_quartz_osx_version = 0x1050; } _cairo_quartz_symbol_lookup_done = TRUE; } @@ -605,20 +610,23 @@ static inline void dst->d = src->yy; dst->tx = src->x0; dst->ty = src->y0; } typedef struct { bool isClipping; CGGlyph *cg_glyphs; - CGSize *cg_advances; + union { + CGSize *cg_advances; + CGPoint *cg_positions; + } u; size_t nglyphs; CGAffineTransform textTransform; - CGFontRef font; + cairo_scaled_font_t *scaled_font; CGPoint origin; } unbounded_show_glyphs_t; typedef struct { CGPathRef cgPath; cairo_fill_rule_t fill_rule; } unbounded_stroke_fill_t; @@ -686,36 +694,43 @@ static void CGContextBeginPath (cgc); CGContextAddPath (cgc, op->u.stroke_fill.cgPath); if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING) CGContextFillPath (cgc); else CGContextEOFillPath (cgc); } else if (op->op == UNBOUNDED_SHOW_GLYPHS) { - CGContextSetFont (cgc, op->u.show_glyphs.font); - CGContextSetFontSize (cgc, 1.0); - CGContextSetTextMatrix (cgc, CGAffineTransformIdentity); - CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y); - CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform); - if (op->u.show_glyphs.isClipping) { /* Note that the comment in show_glyphs about kCGTextClip * and the text transform still applies here; however, the * cg_advances we have were already transformed, so we * don't have to do anything. */ CGContextSetTextDrawingMode (cgc, kCGTextClip); CGContextSaveGState (cgc); } - - CGContextShowGlyphsWithAdvances (cgc, - op->u.show_glyphs.cg_glyphs, - op->u.show_glyphs.cg_advances, - op->u.show_glyphs.nglyphs); - + CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y); + CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform); + if (CTFontDrawGlyphsPtr) { + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font), + op->u.show_glyphs.cg_glyphs, + op->u.show_glyphs.u.cg_positions, + op->u.show_glyphs.nglyphs, + cgc); + } else { + CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font)); + CGContextSetFontSize (cgc, 1.0); + CGContextSetTextMatrix (cgc, CGAffineTransformIdentity); + + CGContextShowGlyphsWithAdvances (cgc, + op->u.show_glyphs.cg_glyphs, + op->u.show_glyphs.u.cg_advances, + op->u.show_glyphs.nglyphs); + + } if (op->u.show_glyphs.isClipping) { CGContextClearRect (cgc, clipBoxRound); CGContextRestoreGState (cgc); } } else if (op->op == UNBOUNDED_MASK) { CGAffineTransform ctm = CGContextGetCTM (cgc); CGContextSaveGState (cgc); CGContextConcatCTM (cgc, op->u.mask.maskTransform); @@ -2684,16 +2699,19 @@ static cairo_int_status_t cairo_clip_t *clip, int *remaining_glyphs) { CGAffineTransform textTransform, ctm, invTextTransform; #define STATIC_BUF_SIZE 64 CGGlyph glyphs_static[STATIC_BUF_SIZE]; CGSize cg_advances_static[STATIC_BUF_SIZE]; CGGlyph *cg_glyphs = &glyphs_static[0]; + /* We'll use the cg_advances array for either advances or positions, + depending which API we're using to actually draw. The types involved + have the same size, so this is safe. */ CGSize *cg_advances = &cg_advances_static[0]; cairo_rectangle_int_t glyph_extents; cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; cairo_quartz_drawing_state_t state; cairo_quartz_float_t xprev, yprev; int i; @@ -2796,41 +2814,62 @@ static cairo_int_status_t invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx, -scaled_font->scale_inverse.yx, scaled_font->scale_inverse.xy, -scaled_font->scale_inverse.yy, 0.0, 0.0); CGContextSetTextMatrix (state.context, CGAffineTransformIdentity); - /* Convert our glyph positions to glyph advances. We need n-1 advances, - * since the advance at index 0 is applied after glyph 0. */ - xprev = glyphs[0].x; - yprev = glyphs[0].y; - - cg_glyphs[0] = glyphs[0].index; - - for (i = 1; i < num_glyphs; i++) { - cairo_quartz_float_t xf = glyphs[i].x; - cairo_quartz_float_t yf = glyphs[i].y; - cg_glyphs[i] = glyphs[i].index; - cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform); - xprev = xf; - yprev = yf; - } - /* Translate to the first glyph's position before drawing */ ctm = CGContextGetCTM (state.context); CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y); CGContextConcatCTM (state.context, textTransform); - CGContextShowGlyphsWithAdvances (state.context, - cg_glyphs, - cg_advances, - num_glyphs); + if (CTFontDrawGlyphsPtr) { + /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use + * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap + * fonts like Apple Color Emoji will render properly. + * For this, we need to convert our glyph positions to Core Graphics's CGPoint. + * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */ + + CGPoint *cg_positions = (CGPoint*) cg_advances; + cairo_quartz_float_t origin_x = glyphs[0].x; + cairo_quartz_float_t origin_y = glyphs[0].y; + + for (i = 0; i < num_glyphs; i++) { + CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y); + cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform); + cg_glyphs[i] = glyphs[i].index; + } + + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font), + cg_glyphs, cg_positions, num_glyphs, state.context); + } else { + /* Convert our glyph positions to glyph advances. We need n-1 advances, + * since the advance at index 0 is applied after glyph 0. */ + xprev = glyphs[0].x; + yprev = glyphs[0].y; + + cg_glyphs[0] = glyphs[0].index; + + for (i = 1; i < num_glyphs; i++) { + cairo_quartz_float_t xf = glyphs[i].x; + cairo_quartz_float_t yf = glyphs[i].y; + cg_glyphs[i] = glyphs[i].index; + cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform); + xprev = xf; + yprev = yf; + } + + CGContextShowGlyphsWithAdvances (state.context, + cg_glyphs, + cg_advances, + num_glyphs); + } CGContextSetCTM (state.context, ctm); if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_LAYER) { _cairo_quartz_draw_image (&state, op); } else if (state.action == DO_SHADING) { CGContextConcatCTM (state.context, state.transform); @@ -2847,20 +2886,27 @@ BAIL: cgfref && !_cairo_operator_bounded_by_mask (op)) { unbounded_op_data_t ub; ub.op = UNBOUNDED_SHOW_GLYPHS; ub.u.show_glyphs.isClipping = isClipping; ub.u.show_glyphs.cg_glyphs = cg_glyphs; - ub.u.show_glyphs.cg_advances = cg_advances; + if (CTFontDrawGlyphsPtr) { + /* we're using Core Text API: the cg_advances array was + reused (above) for glyph positions */ + CGPoint *cg_positions = (CGPoint*) cg_advances; + ub.u.show_glyphs.u.cg_positions = cg_positions; + } else { + ub.u.show_glyphs.u.cg_advances = cg_advances; + } ub.u.show_glyphs.nglyphs = num_glyphs; ub.u.show_glyphs.textTransform = textTransform; - ub.u.show_glyphs.font = cgfref; + ub.u.show_glyphs.scaled_font = scaled_font; ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y); _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias); } if (cg_advances != &cg_advances_static[0]) { free (cg_advances);