gfx/cairo/quartz-support-color-emoji-font.patch
author Chris Karlof <ckarlof@mozilla.com>
Wed, 12 Mar 2014 14:31:18 -0700
changeset 191544 58252ac4f28d9f7c816191ee95ffb5473bd28810
parent 119775 6b6cac8af7849f8d05bdec7082842d739a6cf926
permissions -rw-r--r--
Bug 982848: Don't clear syncKeyBundle in BrowserID Identity manager during failures to fetch token. r=markh

From: Jonathan Kew <jkew@mozilla.com>
bug 715798 pt 1 - support Apple Color Emoji font in cairo-quartz backend. r=jrmuizel

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