From c6dc5df61264be1bec2e622355b97c6c45d236fc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 21 Apr 2022 17:19:15 -0700 Subject: [PATCH] [quartz] Convert font handling from CGFont to CTFont. --- src/cairo-quartz-font.c | 301 +++++++++--------- src/cairo-quartz-private.h | 2 + src/cairo-quartz-surface.c | 83 ++--- .../overlapping-glyphs.quartz.argb32.ref.png | Bin 2766 -> 2687 bytes .../overlapping-glyphs.quartz.rgb24.ref.png | Bin 1667 -> 1630 bytes 5 files changed, 184 insertions(+), 202 deletions(-) diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 3a702ca0e..9e667e0d6 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -44,6 +44,7 @@ #include "cairo-error-private.h" +//#define DEBUG /* Uncomment this to get debug messages on the console. */ /** * SECTION:cairo-quartz-fonts * @Title: Quartz (CGFont) Fonts @@ -66,11 +67,19 @@ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; +/* Cairo's transformations assume a unit-scaled font. */ +static const CGFloat font_scale = 1.0; /* Defined in 10.11 */ #define CGGLYPH_MAX ((CGGlyph) 0xFFFE) /* kCGFontIndexMax */ #define CGGLYPH_INVALID ((CGGlyph) 0xFFFF) /* kCGFontIndexInvalid */ +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation +#else +#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal +#endif + static void quartz_font_ensure_symbols(void) { @@ -90,14 +99,13 @@ typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t; struct _cairo_quartz_scaled_font { cairo_scaled_font_t base; + CTFontRef ctFont; }; struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; - CTFontRef ctFont; - double ctFont_scale; }; /* @@ -111,7 +119,7 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, const char *family; char *full_name; CFStringRef FontName = NULL; - CTFontRef ctFont = NULL; + CGFontRef cgFont = NULL; int loop; family = toy_face->family; @@ -151,20 +159,20 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, } FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - ctFont = CTFontCreateWithName (FontName, 1.0, NULL); + cgFont = CGFontCreateWithFontName (FontName); CFRelease (FontName); - if (ctFont) + if (cgFont) break; } - if (!ctFont) { + if (!cgFont) { /* Give up */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - *font_face = _cairo_quartz_font_face_create_for_ctfont (ctFont); - CFRelease (ctFont); + *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); + CFRelease (cgFont); return CAIRO_STATUS_SUCCESS; } @@ -175,12 +183,56 @@ _cairo_quartz_font_face_destroy (void *abstract_face) cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; CGFontRelease (font_face->cgFont); - CFRelease (font_face->ctFont); return TRUE; } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; +#ifdef DEBUG +static void +_cairo_quartz_debug_font_characteristics (cairo_quartz_scaled_font_t *font) +{ + CGRect ct_bbox = CTFontGetBoundingBox (font->ctFont); + CGFloat ct_ascent = CTFontGetAscent (font->ctFont); + CGFloat ct_descent = CTFontGetDescent (font->ctFont); + CGFloat ct_leading = CTFontGetLeading (font->ctFont); + CGFloat ct_capheight = CTFontGetCapHeight (font->ctFont); + CGFloat ct_xheight = CTFontGetXHeight (font->ctFont); + char chars[] = "ymMW"; + CGGlyph glyphs[4]; + UniChar *utf16 = NULL; + CGSize ct_advances[4]; + CGRect ct_gbbox[4], ct_gobox[4], ct_rbbox, ct_robox; + double ct_radvance; + int converted; + cairo_status_t rv; + + rv = _cairo_utf8_to_utf16 (chars, 4, &utf16, &converted); + if (rv) return; + CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyphs, 4); + free (utf16); + ct_rbbox = CTFontGetBoundingRectsForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, glyphs, ct_gbbox, 4); + ct_robox = CTFontGetOpticalBoundsForGlyphs (font->ctFont, glyphs, ct_gobox, 4, 0); + ct_radvance = CTFontGetAdvancesForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, glyphs, ct_advances, 4); + + fprintf (stderr, "\nCTFont Bounding Box: %f %f %f %f\nAscent %f Descent %f Leading %f Cap Height %f X-Height %f\n", + ct_bbox.origin.x, ct_bbox.origin.y, ct_bbox.size.width, ct_bbox.size.height, ct_ascent, ct_descent, + ct_leading, ct_capheight, ct_xheight); + fprintf (stderr, "CTFont string\n\t bounding box %f %f %f %f advance %f\n\toptical box %f %f %f %f\n\n", + ct_rbbox.origin.x, ct_rbbox.origin.y, ct_rbbox.size.width, ct_rbbox.size.height, ct_radvance, + ct_robox.origin.x, ct_robox.origin.y, ct_robox.size.width, ct_robox.size.height); + for (int i = 0; i < 4; ++i) + { + fprintf (stderr, "Character %c\n", chars[i]); + fprintf (stderr, "\tbox %f %f %f %f\n\toptical %f %f %f %f advance %f %f\n", + ct_gbbox[i].origin.x, ct_gbbox[i].origin.y, ct_gbbox[i].size.width, ct_gbbox[i].size.height, + ct_advances[i].width, ct_advances[i].height, + ct_gobox[i].origin.x, ct_gobox[i].origin.y, ct_gobox[i].size.width, ct_gobox[i].size.height); + } + fprintf (stderr, "\n"); +} +#endif + static cairo_status_t _cairo_quartz_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, @@ -192,7 +244,7 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, cairo_quartz_scaled_font_t *font = NULL; cairo_status_t status; cairo_font_extents_t fs_metrics; - double ems; + CTFontRef ctFont; CGRect bbox; font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t)); @@ -207,19 +259,29 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, if (status) goto FINISH; - ems = CGFontGetUnitsPerEm (font_face->cgFont); - + ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, font_scale, NULL, NULL); /* initialize metrics */ - fs_metrics.ascent = (CGFontGetAscent (font_face->cgFont) / ems); - fs_metrics.descent = - (CGFontGetDescent (font_face->cgFont) / ems); + fs_metrics.ascent = CTFontGetAscent (ctFont); + fs_metrics.descent = CTFontGetDescent (ctFont); fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + - (CGFontGetLeading (font_face->cgFont) / ems); + CTFontGetLeading (ctFont); - bbox = CGFontGetFontBBox (font_face->cgFont); - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; + bbox = CTFontGetBoundingBox (ctFont); + fs_metrics.max_x_advance = CGRectGetMaxX(bbox); fs_metrics.max_y_advance = 0.0; - + font->ctFont = CFRetain (ctFont); status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); +#ifdef DEBUG + { + CFStringRef fontFullName = CTFontCopyFullName (ctFont); + const char* font_full_name = CFStringGetCStringPtr(fontFullName, kCFStringEncodingUTF8); + + fprintf (stderr, "Create scaled font %s with scale %f ascent %f, descent %f, height %f, x-advance %f\n", + font_full_name, fs_metrics.ascent, fs_metrics.descent, fs_metrics.height, + fs_metrics.max_x_advance); + _cairo_quartz_debug_font_characteristics (font); + } +#endif FINISH: if (status != CAIRO_STATUS_SUCCESS) { @@ -272,29 +334,11 @@ cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); - double font_scale = 1.0; /* 1.0 produces glyphs of the right size to pass tests. */ if (cairo_font_face_status (&font_face->base)) return &font_face->base; font_face->cgFont = CGFontRetain (font); - font_face->ctFont = CTFontCreateWithGraphicsFont (font, font_scale, NULL, NULL); - font_face->ctFont_scale = CTFontGetUnitsPerEm(font_face->ctFont); - - return &font_face->base; -} - -cairo_font_face_t * -_cairo_quartz_font_face_create_for_ctfont (CTFontRef font) -{ - cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); - - if (cairo_font_face_status (&font_face->base)) - return &font_face->base; - - font_face->ctFont = CFRetain (font); - font_face->cgFont = CTFontCopyGraphicsFont (font, NULL); - font_face->ctFont_scale = CTFontGetUnitsPerEm(font_face->ctFont); return &font_face->base; } @@ -315,6 +359,8 @@ _cairo_quartz_scaled_to_face (void *abstract_font) static void _cairo_quartz_scaled_font_fini(void *abstract_font) { + cairo_quartz_scaled_font_t* font = (cairo_quartz_scaled_font_t*)abstract_font; + CFRelease (font->ctFont); } static inline CGGlyph @@ -329,21 +375,17 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0}; CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - int advance; + CGSize advance; CGRect bbox; - double emscale = CGFontGetUnitsPerEm (font_face->cgFont); double xmin, ymin, xmax, ymax; if (unlikely (glyph == CGGLYPH_INVALID)) goto FAIL; - if (!CGFontGetGlyphAdvances (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxes (font_face->cgFont, &glyph, 1, &bbox)) - goto FAIL; - + CTFontGetAdvancesForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &advance, 1); + CTFontGetBoundingRectsForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &bbox, 1); /* broken fonts like Al Bayan return incorrect bounds for some null characters, see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */ if (unlikely (bbox.origin.x == -32767 && @@ -354,31 +396,9 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, bbox.size.width = bbox.size.height = 0; } - bbox = CGRectMake (bbox.origin.x / emscale, - bbox.origin.y / emscale, - bbox.size.width / emscale, - bbox.size.height / emscale); - - /* Should we want to always integer-align glyph extents, we can do so in this way */ -#if 0 - { - CGAffineTransform textMatrix; - textMatrix = CGAffineTransformMake (font->base.scale.xx, - -font->base.scale.yx, - -font->base.scale.xy, - font->base.scale.yy, - 0.0f, 0.0f); - - bbox = CGRectApplyAffineTransform (bbox, textMatrix); - bbox = CGRectIntegral (bbox); - bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix)); - } -#endif - -#if 0 - fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph, - bbox.origin.x / emscale, bbox.origin.y / emscale, - bbox.size.width / emscale, bbox.size.height / emscale); +#ifdef DEBUG + fprintf (stderr, "[0x%04x] bbox: x %f y %f width %f height %f\n", glyph, + bbox.origin.x, bbox.origin.y, bbox.size.width, bbox.size.height); #endif xmin = CGRectGetMinX(bbox); @@ -391,12 +411,12 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, extents.width = xmax - xmin; extents.height = ymax - ymin; - extents.x_advance = (double) advance / emscale; - extents.y_advance = 0.0; + extents.x_advance = advance.width; + extents.y_advance = advance.height; -#if 0 - fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph, - extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance); +#ifdef DEBUG + fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f %f\n\n", glyph, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance, extents.y_advance); #endif FAIL: @@ -452,7 +472,7 @@ _cairo_quartz_path_apply_func (void *info, const CGPathElement *el) _cairo_fixed_from_double(el->points[1].y), _cairo_fixed_from_double(el->points[2].x), _cairo_fixed_from_double(el->points[2].y)); - assert(!status); + assert(!status); break; case kCGPathElementCloseSubpath: status = _cairo_path_fixed_close_path (path); @@ -465,7 +485,6 @@ static cairo_int_status_t _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; CGPathRef glyphPath; @@ -483,7 +502,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - glyphPath = CTFontCreatePathForGlyph (font_face->ctFont, glyph, &textMatrix); + glyphPath = CTFontCreatePathForGlyph (font->ctFont, glyph, &textMatrix); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -508,25 +527,25 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); - cairo_image_surface_t *surface = NULL; CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - - int advance; - CGRect bbox; + cairo_text_extents_t metrics = scaled_glyph->fs_metrics; + CGRect bbox = CGRectMake (metrics.x_bearing, -(metrics.y_bearing + metrics.height), + metrics.width, metrics.height); double width, height; - double emscale = CGFontGetUnitsPerEm (font_face->cgFont); - CGContextRef cgContext = NULL; CGAffineTransform textMatrix; CGRect glyphRect, glyphRectInt; CGPoint glyphOrigin; - //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface); - +#ifdef DEBUG + fprintf (stderr, "[0x%04x] bearing: %f %f width %f height %f advances %f %f\n", + glyph, metrics.x_bearing, metrics.y_bearing, metrics.width, metrics.height, + metrics.x_advance, metrics.y_advance); + fprintf (stderr, "[0x%04x] bounds: origin %f %f, size %f %f\n", glyph, bbox.origin.x, + bbox.origin.y, bbox.size.width, bbox.size.height); +#endif /* Create blank 2x2 image if we don't have this character. * Maybe we should draw a better missing-glyph slug or something, * but this is ok for now. @@ -543,27 +562,20 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } - CTFontGetBoundingRectsForGlyphs (font_face->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &bbox, 1); - /* scale(1,-1) * font->base.scale * scale(1,-1) */ textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, -font->base.scale.xy, font->base.scale.yy, - 0, -0); - glyphRect = CGRectMake (bbox.origin.x / emscale, - bbox.origin.y / emscale, - bbox.size.width / emscale, - bbox.size.height / emscale); - - glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix); + 0, 0); + glyphRect = CGRectApplyAffineTransform (bbox, textMatrix); /* Round the rectangle outwards, so that we don't have to deal * with non-integer-pixel origins or dimensions. */ glyphRectInt = CGRectIntegral (glyphRect); -#if 0 +#ifdef DEBUG fprintf (stderr, "glyphRect[o]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); fprintf (stderr, "glyphRectInt: %f %f %f %f\n", @@ -572,63 +584,32 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, glyphOrigin = glyphRectInt.origin; - //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm)); - width = glyphRectInt.size.width; height = glyphRectInt.size.height; - //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); - surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); if (surface->base.status) return surface->base.status; if (surface->width != 0 && surface->height != 0) { - cgContext = CGBitmapContextCreate (surface->data, - surface->width, - surface->height, - 8, - surface->stride, - NULL, - kCGImageAlphaOnly); + CGContextRef cgContext = CGBitmapContextCreate (surface->data, + surface->width, + surface->height, + 8, + surface->stride, + NULL, + kCGImageAlphaOnly); if (cgContext == NULL) { cairo_surface_destroy (&surface->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - CGContextSetFont (cgContext, font_face->cgFont); - CGContextSetFontSize (cgContext, 1.0); - CGContextSetTextMatrix (cgContext, textMatrix); - - switch (font->base.options.antialias) { - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_BEST: - CGContextSetShouldAntialias (cgContext, TRUE); - CGContextSetShouldSmoothFonts (cgContext, TRUE); - quartz_font_ensure_symbols (); - if (CGContextGetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (cgContext)) - CGContextSetAllowsFontSmoothing (cgContext, TRUE); - break; - case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (cgContext, FALSE); - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_GOOD: - case CAIRO_ANTIALIAS_FAST: - CGContextSetShouldAntialias (cgContext, TRUE); - CGContextSetShouldSmoothFonts (cgContext, FALSE); - break; - case CAIRO_ANTIALIAS_DEFAULT: - default: - /* Don't do anything */ - break; - } - + _cairo_quartz_set_antialiasing (cgContext, font->base.options.antialias); CGContextSetAlpha (cgContext, 1.0); - CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1); - + CGContextTranslateCTM (cgContext, -glyphOrigin.x, -glyphOrigin.y); + CGContextConcatCTM (cgContext, textMatrix); + CTFontDrawGlyphs (font->ctFont, &glyph, &CGPointZero, 1, cgContext); CGContextRelease (cgContext); } @@ -667,12 +648,11 @@ _cairo_quartz_ucs4_to_index (void *abstract_font, uint32_t ucs4) { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; - cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); CGGlyph glyph[2]; UniChar utf16[2]; int len = _cairo_ucs4_to_utf16 (ucs4, utf16); - CTFontGetGlyphsForCharacters (ffont->ctFont, utf16, glyph, len); + CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyph, len); return glyph[0]; } @@ -683,8 +663,8 @@ _cairo_quartz_load_truetype_table (void *abstract_font, unsigned char *buffer, unsigned long *length) { - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font); - CFDataRef data = CGFontCopyTableForTag (font_face->cgFont, tag); + cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; + CFDataRef data = CTFontCopyTable (font->ctFont, tag, kCTFontTableOptionNoOptions); if (!data) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -728,6 +708,43 @@ _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 (cairo_scaled_font_t *abstract_font) +{ + cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; + + return font->ctFont; +} + + void + _cairo_quartz_set_antialiasing (CGContextRef cgContext, cairo_antialias_t antialias) +{ + switch (antialias) { + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + CGContextSetShouldAntialias (cgContext, TRUE); + CGContextSetShouldSmoothFonts (cgContext, TRUE); + quartz_font_ensure_symbols (); + if (CGContextGetAllowsFontSmoothingPtr && + !CGContextGetAllowsFontSmoothingPtr (cgContext)) + CGContextSetAllowsFontSmoothing (cgContext, TRUE); + break; + case CAIRO_ANTIALIAS_NONE: + CGContextSetShouldAntialias (cgContext, FALSE); + break; + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: + CGContextSetShouldAntialias (cgContext, TRUE); + CGContextSetShouldSmoothFonts (cgContext, FALSE); + break; + case CAIRO_ANTIALIAS_DEFAULT: + default: + /* Don't do anything */ + break; + } + +} /* * compat with old ATSUI backend */ diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index e3600d5fc..f142f8471 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -109,6 +109,8 @@ cairo_private CTFontRef _cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *sfont); cairo_private cairo_font_face_t* _cairo_quartz_font_face_create_for_ctfont (CTFontRef ctFont); +cairo_private void +_cairo_quartz_set_antialiasing (CGContextRef context, cairo_antialias_t antialias); #else diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index bc6968c06..ae6546029 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -65,6 +65,12 @@ #endif #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation +#else +#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal +#endif + /** * SECTION:cairo-quartz @@ -1894,17 +1900,15 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, cairo_bool_t overlap) { CGAffineTransform textTransform, invTextTransform; - CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; - CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; + CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGGlyph)]; + CGPoint cg_positions_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)]; CGGlyph *cg_glyphs = &glyphs_static[0]; - CGSize *cg_advances = &cg_advances_static[0]; - COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); + CGPoint *cg_positions = &cg_positions_static[0]; cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_quartz_float_t xprev, yprev; - int i; - CGFontRef cgfref = NULL; + CGPoint origin; + CTFontRef ctFont = NULL; cairo_bool_t didForceFontSmoothing = FALSE; @@ -1923,45 +1927,17 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, } /* this doesn't addref */ - cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); - CGContextSetFont (state.cgMaskContext, cgfref); - CGContextSetFontSize (state.cgMaskContext, 1.0); - - switch (scaled_font->options.antialias) { - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_BEST: - CGContextSetShouldAntialias (state.cgMaskContext, TRUE); - CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE); - quartz_ensure_symbols(); - if (CGContextGetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) - { - didForceFontSmoothing = TRUE; - CGContextSetAllowsFontSmoothing (state.cgMaskContext, TRUE); - } - break; - case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (state.cgMaskContext, FALSE); - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_GOOD: - case CAIRO_ANTIALIAS_FAST: - CGContextSetShouldAntialias (state.cgMaskContext, TRUE); - CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE); - break; - case CAIRO_ANTIALIAS_DEFAULT: - /* Don't do anything */ - break; - } + ctFont = _cairo_quartz_scaled_font_get_ct_font (scaled_font); + _cairo_quartz_set_antialiasing (state.cgMaskContext, scaled_font->options.antialias); if (num_glyphs > ARRAY_LENGTH (glyphs_static)) { - cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize)); + cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGPoint)); if (unlikely (cg_glyphs == NULL)) { rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } - cg_advances = (CGSize*) (cg_glyphs + num_glyphs); + cg_positions = (CGPoint*) (cg_glyphs + num_glyphs); } /* scale(1,-1) * scaled_font->scale */ @@ -1978,36 +1954,23 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, -scaled_font->scale_inverse.yy, 0.0, 0.0); - 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; + origin = CGPointMake (glyphs[0].x, glyphs[0].y); + for (int i = 0; i < num_glyphs; ++i) + { cg_glyphs[i] = glyphs[i].index; - cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform); - xprev = xf; - yprev = yf; + cg_positions[i] = CGPointMake (glyphs[i].x - origin.x, glyphs[i].y - origin.y); + cg_positions[i] = CGPointApplyAffineTransform (cg_positions[i], invTextTransform); } /* Translate to the first glyph's position before drawing */ - CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); + CGContextTranslateCTM (state.cgMaskContext, origin.x, origin.y); CGContextConcatCTM (state.cgMaskContext, textTransform); - CGContextShowGlyphsWithAdvances (state.cgMaskContext, - cg_glyphs, - cg_advances, - num_glyphs); + CTFontDrawGlyphs (ctFont, cg_glyphs, cg_positions, num_glyphs, state.cgMaskContext); CGContextConcatCTM (state.cgMaskContext, invTextTransform); - CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); + CGContextTranslateCTM (state.cgMaskContext, -origin.x, -origin.y); if (state.action != DO_DIRECT) _cairo_quartz_draw_source (&state, extents->op); diff --git a/test/reference/overlapping-glyphs.quartz.argb32.ref.png b/test/reference/overlapping-glyphs.quartz.argb32.ref.png index 2bbbb3944be27ef65d44bb6e13f83a05af58813a..44200d59ee7b64efbc86a674934ea6b3875fae19 100644 GIT binary patch delta 2660 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 delta 1632 zcmV-m2A}!f41*1jHh(xtL_t(&f$f>gZxUM^$IlD{oerhcVNlwV@~B231~qL`p{A|1 z*7y6hu#G0VFm7EM6a5>yG$vj6SeV#`*rcfS(bz^%Y@`HgAi)t426?qm7zUWRi@6dP z_ZCSyw)ck5>f}7g@B7V}bAIOs#Bm&0e?Snl)*<`@@LC7(Uw@)zRu%lNS-D&;m&*YF zXqsMLUjB6`5{X125x)KT^JjkA{}QC)45!oi=FJ<9L z10s>g?RIkz6L-mAMo$qy|dfx=g*%fNz&nP^!N8C5{aIk zo@_QdJw4sq+kY#NFYEJwUukC*i^bQkU!OdAQmIsqj*gx_eR}KGt^5!KLGgHeWMl-x zFc^l7Mq^)JpUq}_^yrbnV35gVe!t&jGKs}vtJSL0>2MrhTwFv^6vy$sd-ocR#y_HN zm09V`ipS$LO}DqV$K&yQKCjp7%R>+ZSuBlS!}FtJP}n+_`i6_HBhik6DN)zKdx4*%S#p(7GM}29Df`H0Pq)OLqo&0Yu7Xyjb5+kFOyg- zCY4Hs6eScZnS~%oZ*T9jXV0S1DBqZ$pMUl06#yWePO~fv0HA5QY~sg{A7ioDkt0XS zcNtC75CmZurc^4&$HxHx(P%Uf2yEH1MOazFP$AFSy?Zx;AP*ltgkd-s4BotXbMxlS z9)FJ~l}c^cuwi0ig5$W28#l(|aR7kB;V2Xe4<0;_%jF)Ar=z3e;>C+@w|it{BpeQ> zQmJ4t$S_QIcXxYx`yWxa%2dd+){6C+XVu<@RZugl3TkFmLCvfxsF_s-l}j`LfW^f{ zk|Y_1X>4p&g^hZRsBG5DmoFU- zheo6E`Ftpf=5o2t&Q7=6ZM9mjT)EQF&`>B87>3~|(==Twl?0A?&8U!Pxm+%r&35(b z)#>T!WHLE4G!zPj)M~XzBqB+&R4QQ@*4WsHAc#aF$>;OMVzIndEEdBsOi>hqAb-VT zQ7)HfG8q8CrcIkFrcvuN&#JJq91cfUR~Lq1E|=@_<;w_yXfzrCKsK8l8yiz76dcEq zB-z>7dFap~yWRfw?c1iNCKN@(;jl)d;or{$L2w)w3WYEXYiep@7-o2QST2|U5rL~o z1+yrM>g((K@#9CSRBCBy;X8A=Tz|1xymaZ3OeRARWNK>a>({RUfS#TnkH<4OIGD*~ zo;-OHiA1hnzs_&E$B!Q?6bhfuH#9W#;>C-}$;k^BE(k7ewW(kh%d#*G^KWJt{`=~j zoSY;{GM!GhwY5p5Qi`H}{R@L6NvG3kFc?r2#V{Ts{JI3#|J$v?Owc0|VP+l@KGjrz58G;~C z6s2kU-o1MmhOsPbHk(ltEfflTol7`L!coC2lgae{{rkGQI=x;$J39-07H0y1002O# zRH{@e48!=z*=#nG$qWn(=znxNz9$-uqA1$c)kP3QJRau{_)sWB5QKo51f#-dXt7vG zlDvEOu3oRV+wI9@lA@^n`}gxjbc&*!PUnXYANa{ut2L9!EG;d~&CNw35rQBT3Pn1d zo}Ha-X=!=>{5i*QUa!~fb{{@`Sa4+m@LMICg@uJcAW%+9S(e3d99hG#P$(pm$y6#; zB9X9IET2AoN~hE1RjsY9I-M>Y4$sZaK@im3+#Cvp002g#vAp&EHLP`+I@Ib}RZx2y eRzc0ID)