From 826a9088531b21abf410e90689900fd8294f2586 Mon Sep 17 00:00:00 2001 From: Kristian Rietveld Date: Sat, 5 Sep 2015 00:08:16 +0200 Subject: [PATCH] quartz: support rendering colored bitmap fonts In _cairo_quartz_cg_glyphs prefer to use CTFontDrawGlyphsPtr 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 | 25 +++++++++++++++++ src/cairo-quartz-private.h | 6 ++++ src/cairo-quartz-surface.c | 70 +++++++++++++++++++++++++++++++++------------- 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index feee61a..0571d4e 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -92,6 +92,10 @@ 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; + /* Not public anymore in 64-bits nor in 10.7 */ static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; @@ -133,6 +137,8 @@ quartz_font_ensure_symbols(void) CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); + FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && @@ -157,6 +163,7 @@ struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; + CTFontRef ctFont; }; /* @@ -241,6 +248,10 @@ _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); return TRUE; } @@ -366,6 +377,12 @@ cairo_quartz_font_face_create_for_cgfont (CGFontRef font) 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; @@ -811,6 +828,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..f1a7250 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -57,6 +57,9 @@ typedef enum { DO_TILED_IMAGE } cairo_quartz_action_t; +/* define CTFontRef for pre-10.5 SDKs */ +typedef const struct __CTFont *CTFontRef; + typedef struct cairo_quartz_surface { cairo_surface_t base; @@ -100,6 +103,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..b762ce4 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,30 +2046,51 @@ _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; - - 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); - CGContextShowGlyphsWithAdvances (state.cgMaskContext, - 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 preferred to CGContextShowGlyphsWithAdvances + * so that colored-bitmap fonts like Apple Color Emoji will render + * properly. */ + + 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.cgMaskContext); + } 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.cgMaskContext, + cg_glyphs, + cg_advances, + num_glyphs); + } CGContextConcatCTM (state.cgMaskContext, invTextTransform); CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); -- 1.9.5 (Apple Git-50.3)