|
1 From: Jonathan Kew <jkew@mozilla.com> |
|
2 bug 715798 pt 1 - support Apple Color Emoji font in cairo-quartz backend. r=jrmuizel |
|
3 |
|
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 { |
|
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. |
|
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 |
|
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 */ |
|
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; |
|
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); |