1From: Jonathan Kew <jkew@mozilla.com>
2bug 715798 pt 1 - support Apple Color Emoji font in cairo-quartz backend. r=jrmuizel
3
4diff --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 {
8     int descent;
9     int leading;
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;
15
16+/* CTFontCreateWithGraphicsFont is not public until 10.5. */
17+typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
18+static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
19+
20 static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
21 static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
22
23 static void
24 quartz_font_ensure_symbols(void)
25 {
26     if (_cairo_quartz_font_symbol_lookup_done)
27 	return;
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");
33
34     CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
35     CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
36
37+    CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
38+
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;
50 };
51
52 struct _cairo_quartz_font_face {
53     cairo_font_face_t base;
54
55     CGFontRef cgFont;
56+    CTFontRef ctFont;
57 };
58
59 /*
60  * font face backend
61  */
62
63 static cairo_status_t
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;
67 }
68
69 static void
70 _cairo_quartz_font_face_destroy (void *abstract_face)
71 {
72     cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
73
74+    if (font_face->ctFont) {
75+        CFRelease (font_face->ctFont);
76+    }
77+
78     CGFontRelease (font_face->cgFont);
79 }
80
81 static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
82
83 static cairo_status_t
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
87     if (!font_face) {
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;
91     }
92
93     font_face->cgFont = CGFontRetain (font);
94
95+    if (CTFontCreateWithGraphicsFontPtr) {
96+        font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
97+    } else {
98+        font_face->ctFont = NULL;
99+    }
100+
101     _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
102
103     return &font_face->base;
104 }
105
106 /*
107  * scaled font backend
108  */
109@@ -772,16 +789,24 @@ static const cairo_scaled_font_backend_t
110 CGFontRef
111 _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
112 {
113     cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
114
115     return ffont->cgFont;
116 }
117
118+CTFontRef
119+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
120+{
121+    cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
122+
123+    return ffont->ctFont;
124+}
125+
126 #ifndef __LP64__
127 /*
128  * compat with old ATSUI backend
129  */
130
131 /**
132  * cairo_quartz_font_face_create_for_atsu_font_id
133  * @font_id: an ATSUFontID for the font.
134diff --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
137@@ -45,16 +45,19 @@
138 #include "cairo-surface-clipper-private.h"
139
140 #ifdef CGFLOAT_DEFINED
141 typedef CGFloat cairo_quartz_float_t;
142 #else
143 typedef float cairo_quartz_float_t;
144 #endif
145
146+/* define CTFontRef for pre-10.5 SDKs */
147+typedef const struct __CTFont *CTFontRef;
148+
149 typedef struct cairo_quartz_surface {
150     cairo_surface_t base;
151
152     CGContextRef cgContext;
153     CGAffineTransform cgContextBaseCTM;
154
155     void *imageData;
156     cairo_surface_t *imageSurfaceEquiv;
157@@ -99,15 +102,18 @@ CGImageRef
158 			      cairo_bool_t interpolate,
159 			      CGColorSpaceRef colorSpaceOverride,
160 			      CGDataProviderReleaseDataCallback releaseCallback,
161 			      void *releaseInfo);
162
163 CGFontRef
164 _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
165
166+CTFontRef
167+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
168+
169 #else
170
171 # error Cairo was not compiled with support for the quartz backend
172
173 #endif /* CAIRO_HAS_QUARTZ_SURFACE */
174
175 #endif /* CAIRO_QUARTZ_PRIVATE_H */
176diff --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;
187
188+/* CTFontDrawGlyphs is not available until 10.7 */
189+static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
190+
191 static SInt32 _cairo_quartz_osx_version = 0x0;
192
193 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
194
195 /*
196  * Utility functions
197  */
198
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");
207
208+    CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
209+
210     if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
211         // assume 10.5
212         _cairo_quartz_osx_version = 0x1050;
213     }
214
215     _cairo_quartz_symbol_lookup_done = TRUE;
216 }
217
218@@ -605,20 +610,23 @@ static inline void
219     dst->d = src->yy;
220     dst->tx = src->x0;
221     dst->ty = src->y0;
222 }
223
224 typedef struct {
225     bool isClipping;
226     CGGlyph *cg_glyphs;
227-    CGSize *cg_advances;
228+    union {
229+      CGSize *cg_advances;
230+      CGPoint *cg_positions;
231+    } u;
232     size_t nglyphs;
233     CGAffineTransform textTransform;
234-    CGFontRef font;
235+    cairo_scaled_font_t *scaled_font;
236     CGPoint origin;
237 } unbounded_show_glyphs_t;
238
239 typedef struct {
240     CGPathRef cgPath;
241     cairo_fill_rule_t fill_rule;
242 } unbounded_stroke_fill_t;
243
244@@ -686,36 +694,43 @@ static void
245 	CGContextBeginPath (cgc);
246 	CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
247
248 	if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
249 	    CGContextFillPath (cgc);
250 	else
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);
258-
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);
266 	}
267-
268-	CGContextShowGlyphsWithAdvances (cgc,
269-					 op->u.show_glyphs.cg_glyphs,
270-					 op->u.show_glyphs.cg_advances,
271-					 op->u.show_glyphs.nglyphs);
272-
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,
280+                                 cgc);
281+        } else {
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);
285+
286+	    CGContextShowGlyphsWithAdvances (cgc,
287+					     op->u.show_glyphs.cg_glyphs,
288+					     op->u.show_glyphs.u.cg_advances,
289+					     op->u.show_glyphs.nglyphs);
290+
291+        }
292 	if (op->u.show_glyphs.isClipping) {
293 	    CGContextClearRect (cgc, clipBoxRound);
294 	    CGContextRestoreGState (cgc);
295 	}
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
301 				      cairo_clip_t *clip,
302 				      int *remaining_glyphs)
303 {
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];
313
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;
319     int i;
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,
325 					      0.0, 0.0);
326
327     CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
328
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;
333-
334-    cg_glyphs[0] = glyphs[0].index;
335-
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);
341-	xprev = xf;
342-	yprev = yf;
343-    }
344-
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);
349
350-    CGContextShowGlyphsWithAdvances (state.context,
351-				     cg_glyphs,
352-				     cg_advances,
353-				     num_glyphs);
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. */
360+
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;
364+
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;
369+        }
370+
371+        CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
372+                             cg_glyphs, cg_positions, num_glyphs, state.context);
373+    } else {
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;
378+
379+        cg_glyphs[0] = glyphs[0].index;
380+
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);
386+	    xprev = xf;
387+	    yprev = yf;
388+        }
389+
390+        CGContextShowGlyphsWithAdvances (state.context,
391+				         cg_glyphs,
392+				         cg_advances,
393+				         num_glyphs);
394+    }
395
396     CGContextSetCTM (state.context, ctm);
397
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:
404 	cgfref &&
405 	!_cairo_operator_bounded_by_mask (op))
406     {
407 	unbounded_op_data_t ub;
408 	ub.op = UNBOUNDED_SHOW_GLYPHS;
409
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;
418+	} else {
419+	    ub.u.show_glyphs.u.cg_advances = cg_advances;
420+	}
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);
426
427 	_cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
428     }
429
430
431     if (cg_advances != &cg_advances_static[0]) {
432 	free (cg_advances);
433