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