1 From: Jonathan Kew <jkew@mozilla.com>
2 bug 715798 pt 1 - support Apple Color Emoji font in cairo-quartz backend. r=jrmuizel
4 diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c
5 --- a/gfx/cairo/cairo/src/cairo-quartz-font.c
6 +++ b/gfx/cairo/cairo/src/cairo-quartz-font.c
7 @@ -85,16 +85,20 @@ typedef struct {
10 } quartz_CGFontMetrics;
11 static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL;
12 static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL;
13 static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
14 static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
16 +/* CTFontCreateWithGraphicsFont is not public until 10.5. */
17 +typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
18 +static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
20 static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
21 static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
24 quartz_font_ensure_symbols(void)
26 if (_cairo_quartz_font_symbol_lookup_done)
28 @@ -122,16 +126,18 @@ quartz_font_ensure_symbols(void)
29 CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
30 CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
31 CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent");
32 CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading");
34 CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
35 CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
37 + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
39 if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
40 CGFontGetGlyphBBoxesPtr &&
41 CGFontGetGlyphsForUnicharsPtr &&
42 CGFontGetUnitsPerEmPtr &&
43 CGFontGetGlyphAdvancesPtr &&
44 CGFontGetGlyphPathPtr &&
45 (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
46 _cairo_quartz_font_symbols_present = TRUE;
47 @@ -145,16 +151,17 @@ typedef struct _cairo_quartz_scaled_font
48 struct _cairo_quartz_scaled_font {
49 cairo_scaled_font_t base;
52 struct _cairo_quartz_font_face {
53 cairo_font_face_t base;
64 _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
65 @@ -229,16 +236,20 @@ static cairo_status_t
66 return CAIRO_STATUS_SUCCESS;
70 _cairo_quartz_font_face_destroy (void *abstract_face)
72 cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
74 + if (font_face->ctFont) {
75 + CFRelease (font_face->ctFont);
78 CGFontRelease (font_face->cgFont);
81 static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
84 _cairo_quartz_font_face_scaled_font_create (void *abstract_face,
85 const cairo_matrix_t *font_matrix,
86 @@ -353,16 +364,22 @@ cairo_quartz_font_face_create_for_cgfont
88 cairo_status_t ignore_status;
89 ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
90 return (cairo_font_face_t *)&_cairo_font_face_nil;
93 font_face->cgFont = CGFontRetain (font);
95 + if (CTFontCreateWithGraphicsFontPtr) {
96 + font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
98 + font_face->ctFont = NULL;
101 _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
103 return &font_face->base;
107 * scaled font backend
109 @@ -772,16 +789,24 @@ static const cairo_scaled_font_backend_t
111 _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
113 cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
115 return ffont->cgFont;
119 +_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
121 + cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
123 + return ffont->ctFont;
128 * compat with old ATSUI backend
132 * cairo_quartz_font_face_create_for_atsu_font_id
133 * @font_id: an ATSUFontID for the font.
134 diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h
135 --- a/gfx/cairo/cairo/src/cairo-quartz-private.h
136 +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h
138 #include "cairo-surface-clipper-private.h"
140 #ifdef CGFLOAT_DEFINED
141 typedef CGFloat cairo_quartz_float_t;
143 typedef float cairo_quartz_float_t;
146 +/* define CTFontRef for pre-10.5 SDKs */
147 +typedef const struct __CTFont *CTFontRef;
149 typedef struct cairo_quartz_surface {
150 cairo_surface_t base;
152 CGContextRef cgContext;
153 CGAffineTransform cgContextBaseCTM;
156 cairo_surface_t *imageSurfaceEquiv;
157 @@ -99,15 +102,18 @@ CGImageRef
158 cairo_bool_t interpolate,
159 CGColorSpaceRef colorSpaceOverride,
160 CGDataProviderReleaseDataCallback releaseCallback,
164 _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
167 +_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
171 # error Cairo was not compiled with support for the quartz backend
173 #endif /* CAIRO_HAS_QUARTZ_SURFACE */
175 #endif /* CAIRO_QUARTZ_PRIVATE_H */
176 diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
177 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
178 +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
179 @@ -130,16 +130,19 @@ static void (*CGContextClipToMaskPtr) (C
180 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
181 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
182 static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
183 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
184 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
185 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
186 static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
188 +/* CTFontDrawGlyphs is not available until 10.7 */
189 +static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
191 static SInt32 _cairo_quartz_osx_version = 0x0;
193 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
199 @@ -167,16 +170,18 @@ static void quartz_ensure_symbols(void)
200 CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
201 CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
202 CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
203 CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
204 CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
205 CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
206 CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
208 + CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
210 if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
212 _cairo_quartz_osx_version = 0x1050;
215 _cairo_quartz_symbol_lookup_done = TRUE;
218 @@ -605,20 +610,23 @@ static inline void
227 - CGSize *cg_advances;
229 + CGSize *cg_advances;
230 + CGPoint *cg_positions;
233 CGAffineTransform textTransform;
235 + cairo_scaled_font_t *scaled_font;
237 } unbounded_show_glyphs_t;
241 cairo_fill_rule_t fill_rule;
242 } unbounded_stroke_fill_t;
244 @@ -686,36 +694,43 @@ static void
245 CGContextBeginPath (cgc);
246 CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
248 if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
249 CGContextFillPath (cgc);
251 CGContextEOFillPath (cgc);
252 } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
253 - CGContextSetFont (cgc, op->u.show_glyphs.font);
254 - CGContextSetFontSize (cgc, 1.0);
255 - CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
256 - CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
257 - CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
259 if (op->u.show_glyphs.isClipping) {
260 /* Note that the comment in show_glyphs about kCGTextClip
261 * and the text transform still applies here; however, the
262 * cg_advances we have were already transformed, so we
263 * don't have to do anything. */
264 CGContextSetTextDrawingMode (cgc, kCGTextClip);
265 CGContextSaveGState (cgc);
268 - CGContextShowGlyphsWithAdvances (cgc,
269 - op->u.show_glyphs.cg_glyphs,
270 - op->u.show_glyphs.cg_advances,
271 - op->u.show_glyphs.nglyphs);
273 + CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
274 + CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
275 + if (CTFontDrawGlyphsPtr) {
276 + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
277 + op->u.show_glyphs.cg_glyphs,
278 + op->u.show_glyphs.u.cg_positions,
279 + op->u.show_glyphs.nglyphs,
282 + CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
283 + CGContextSetFontSize (cgc, 1.0);
284 + CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
286 + CGContextShowGlyphsWithAdvances (cgc,
287 + op->u.show_glyphs.cg_glyphs,
288 + op->u.show_glyphs.u.cg_advances,
289 + op->u.show_glyphs.nglyphs);
292 if (op->u.show_glyphs.isClipping) {
293 CGContextClearRect (cgc, clipBoxRound);
294 CGContextRestoreGState (cgc);
296 } else if (op->op == UNBOUNDED_MASK) {
297 CGAffineTransform ctm = CGContextGetCTM (cgc);
298 CGContextSaveGState (cgc);
299 CGContextConcatCTM (cgc, op->u.mask.maskTransform);
300 @@ -2684,16 +2699,19 @@ static cairo_int_status_t
302 int *remaining_glyphs)
304 CGAffineTransform textTransform, ctm, invTextTransform;
305 #define STATIC_BUF_SIZE 64
306 CGGlyph glyphs_static[STATIC_BUF_SIZE];
307 CGSize cg_advances_static[STATIC_BUF_SIZE];
308 CGGlyph *cg_glyphs = &glyphs_static[0];
309 + /* We'll use the cg_advances array for either advances or positions,
310 + depending which API we're using to actually draw. The types involved
311 + have the same size, so this is safe. */
312 CGSize *cg_advances = &cg_advances_static[0];
314 cairo_rectangle_int_t glyph_extents;
315 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
316 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
317 cairo_quartz_drawing_state_t state;
318 cairo_quartz_float_t xprev, yprev;
320 @@ -2796,41 +2814,62 @@ static cairo_int_status_t
321 invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
322 -scaled_font->scale_inverse.yx,
323 scaled_font->scale_inverse.xy,
324 -scaled_font->scale_inverse.yy,
327 CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
329 - /* Convert our glyph positions to glyph advances. We need n-1 advances,
330 - * since the advance at index 0 is applied after glyph 0. */
331 - xprev = glyphs[0].x;
332 - yprev = glyphs[0].y;
334 - cg_glyphs[0] = glyphs[0].index;
336 - for (i = 1; i < num_glyphs; i++) {
337 - cairo_quartz_float_t xf = glyphs[i].x;
338 - cairo_quartz_float_t yf = glyphs[i].y;
339 - cg_glyphs[i] = glyphs[i].index;
340 - cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
345 /* Translate to the first glyph's position before drawing */
346 ctm = CGContextGetCTM (state.context);
347 CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
348 CGContextConcatCTM (state.context, textTransform);
350 - CGContextShowGlyphsWithAdvances (state.context,
354 + if (CTFontDrawGlyphsPtr) {
355 + /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
356 + * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
357 + * fonts like Apple Color Emoji will render properly.
358 + * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
359 + * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
361 + CGPoint *cg_positions = (CGPoint*) cg_advances;
362 + cairo_quartz_float_t origin_x = glyphs[0].x;
363 + cairo_quartz_float_t origin_y = glyphs[0].y;
365 + for (i = 0; i < num_glyphs; i++) {
366 + CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
367 + cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
368 + cg_glyphs[i] = glyphs[i].index;
371 + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
372 + cg_glyphs, cg_positions, num_glyphs, state.context);
374 + /* Convert our glyph positions to glyph advances. We need n-1 advances,
375 + * since the advance at index 0 is applied after glyph 0. */
376 + xprev = glyphs[0].x;
377 + yprev = glyphs[0].y;
379 + cg_glyphs[0] = glyphs[0].index;
381 + for (i = 1; i < num_glyphs; i++) {
382 + cairo_quartz_float_t xf = glyphs[i].x;
383 + cairo_quartz_float_t yf = glyphs[i].y;
384 + cg_glyphs[i] = glyphs[i].index;
385 + cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
390 + CGContextShowGlyphsWithAdvances (state.context,
396 CGContextSetCTM (state.context, ctm);
398 if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
399 state.action == DO_LAYER) {
400 _cairo_quartz_draw_image (&state, op);
401 } else if (state.action == DO_SHADING) {
402 CGContextConcatCTM (state.context, state.transform);
403 @@ -2847,20 +2886,27 @@ BAIL:
405 !_cairo_operator_bounded_by_mask (op))
407 unbounded_op_data_t ub;
408 ub.op = UNBOUNDED_SHOW_GLYPHS;
410 ub.u.show_glyphs.isClipping = isClipping;
411 ub.u.show_glyphs.cg_glyphs = cg_glyphs;
412 - ub.u.show_glyphs.cg_advances = cg_advances;
413 + if (CTFontDrawGlyphsPtr) {
414 + /* we're using Core Text API: the cg_advances array was
415 + reused (above) for glyph positions */
416 + CGPoint *cg_positions = (CGPoint*) cg_advances;
417 + ub.u.show_glyphs.u.cg_positions = cg_positions;
419 + ub.u.show_glyphs.u.cg_advances = cg_advances;
421 ub.u.show_glyphs.nglyphs = num_glyphs;
422 ub.u.show_glyphs.textTransform = textTransform;
423 - ub.u.show_glyphs.font = cgfref;
424 + ub.u.show_glyphs.scaled_font = scaled_font;
425 ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
427 _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
431 if (cg_advances != &cg_advances_static[0]) {