From 7f2087bbe3ec1695140a06833111d397fafcb2de Mon Sep 17 00:00:00 2001 From: Kristian Rietveld Date: Sat, 5 Sep 2015 00:08:16 +0200 Subject: [PATCH 1/2] quartz: Support rendering colored bitmap fonts In _cairo_quartz_cg_glyphs prefer to use CTFontDrawGlyphs over CGContextShowGlyphsWithAdvances when available, since the former is also capable of rendering colored bitmap glyphs. Colored bitmap glyphs are for example found in the Apple Color Emoji font. References: https://bugs.freedesktop.org/show_bug.cgi?id=63771 --- src/cairo-quartz-font.c | 15 +++++++-- src/cairo-quartz-private.h | 3 ++ src/cairo-quartz-surface.c | 76 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index feee61a..a40908b 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -157,6 +157,7 @@ struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; + CTFontRef ctFont; }; /* @@ -241,6 +242,7 @@ _cairo_quartz_font_face_destroy (void *abstract_face) { cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; + CFRelease (font_face->ctFont); CGFontRelease (font_face->cgFont); return TRUE; } @@ -365,6 +367,7 @@ cairo_quartz_font_face_create_for_cgfont (CGFontRef font) } font_face->cgFont = CGFontRetain (font); + font_face->ctFont = CTFontCreateWithGraphicsFont (font, 1.0, NULL, NULL); _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); @@ -560,9 +563,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL); - glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); - CFRelease (ctFont); + glyphPath = CTFontCreatePathForGlyph (font_face->ctFont, glyph, &textMatrix); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -811,6 +812,14 @@ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *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; +} + /* * compat with old ATSUI backend */ diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index 3ef14c3..d7e1e04 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -100,6 +100,9 @@ CairoQuartzCreateCGImage (cairo_format_t format, cairo_private CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); +cairo_private 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 diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 5e9b376..a2e7a2d 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -123,6 +123,9 @@ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; +/* CTFontDrawGlyphs is not available until 10.7 */ +static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL; + static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; /* @@ -152,6 +155,8 @@ static void quartz_ensure_symbols (void) CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); + _cairo_quartz_symbol_lookup_done = TRUE; } @@ -1954,8 +1959,11 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; CGGlyph *cg_glyphs = &glyphs_static[0]; + /* Either cg_advances or cg_positions will be used. */ CGSize *cg_advances = &cg_advances_static[0]; + CGPoint *cg_positions = (CGPoint *)&cg_advances_static[0]; COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); + COMPILE_TIME_ASSERT (sizeof (CGPoint) <= sizeof (CGSize)); cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; @@ -2018,6 +2026,7 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, } cg_advances = (CGSize*) (cg_glyphs + num_glyphs); + cg_positions = (CGPoint*) (cg_glyphs + num_glyphs); } /* scale(1,-1) * scaled_font->scale */ @@ -2037,33 +2046,56 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0); CGContextSetTextMatrix (state.cgMaskContext, 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; + if (CTFontDrawGlyphsPtr) { + CGContextConcatCTM (state.cgMaskContext, textTransform); - cg_glyphs[0] = glyphs[0].index; + /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we + * want to use that preferred to CGContextShowGlyphsWithAdvances + * so that colored-bitmap fonts like Apple Color Emoji will render + * properly. */ - 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; - } + for (i = 0; i < num_glyphs; i++) { + CGPoint pt = CGPointMake (glyphs[i].x, glyphs[i].y); + cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform); + cg_glyphs[i] = glyphs[i].index; + } - /* Translate to the first glyph's position before drawing */ - CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); - CGContextConcatCTM (state.cgMaskContext, textTransform); + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font), + cg_glyphs, + cg_positions, + num_glyphs, + state.cgMaskContext); - CGContextShowGlyphsWithAdvances (state.cgMaskContext, - cg_glyphs, - cg_advances, - num_glyphs); + CGContextConcatCTM (state.cgMaskContext, invTextTransform); + } 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; + } + + /* Translate to the first glyph's position before drawing */ + CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); + CGContextConcatCTM (state.cgMaskContext, textTransform); - CGContextConcatCTM (state.cgMaskContext, invTextTransform); - CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); + CGContextShowGlyphsWithAdvances (state.cgMaskContext, + cg_glyphs, + cg_advances, + num_glyphs); + + CGContextConcatCTM (state.cgMaskContext, invTextTransform); + CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); + } if (state.action != DO_DIRECT) _cairo_quartz_draw_source (&state, extents->op); -- 2.3.2 (Apple Git-55)