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