diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2022-04-27 00:04:43 +0000 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2022-04-27 00:04:43 +0000 |
commit | 455a4cca5472a309d036b6393f1db0dc4403d303 (patch) | |
tree | eadac9e15f0bcb4688020670a74e89655df96ef3 | |
parent | f07d539c07488edccd0bea241073572bc20c8ed1 (diff) | |
parent | cfb3835f57abd05b5d862904dd1d4300ae990a8a (diff) |
Merge branch 'quartz-core-text' into 'master'
quartz: support rendering colored bitmap fonts
See merge request cairo/cairo!289
-rw-r--r-- | configure.ac | 7 | ||||
-rw-r--r-- | meson.build | 20 | ||||
-rw-r--r-- | src/cairo-quartz-font.c | 520 | ||||
-rw-r--r-- | src/cairo-quartz-private.h | 6 | ||||
-rw-r--r-- | src/cairo-quartz-surface.c | 259 | ||||
-rw-r--r-- | test/Makefile.am | 2 | ||||
-rw-r--r-- | test/Makefile.sources | 2 | ||||
-rw-r--r-- | test/meson.build | 1 | ||||
-rw-r--r-- | test/quartz-color-font.c | 54 | ||||
-rw-r--r-- | test/reference/inverse-text.quartz.ref.png | bin | 2565 -> 2602 bytes | |||
-rw-r--r-- | test/reference/overlapping-glyphs.quartz.argb32.ref.png | bin | 2766 -> 2687 bytes | |||
-rw-r--r-- | test/reference/overlapping-glyphs.quartz.rgb24.ref.png | bin | 1667 -> 1630 bytes | |||
-rw-r--r-- | test/reference/pdf-operators-text.quartz.ref.png | bin | 0 -> 8226 bytes | |||
-rw-r--r-- | test/reference/quartz-color-font.quartz.rgb24.ref.png | bin | 0 -> 2050 bytes | |||
-rw-r--r-- | test/reference/quartz-color-font.ref.png | bin | 0 -> 2442 bytes | |||
-rw-r--r-- | test/reference/select-font-face.quartz.ref.png | bin | 2645 -> 2691 bytes |
16 files changed, 359 insertions, 512 deletions
diff --git a/configure.ac b/configure.ac index 209df4fb8..c75648779 100644 --- a/configure.ac +++ b/configure.ac @@ -193,11 +193,8 @@ dnl =========================================================================== CAIRO_ENABLE_SURFACE_BACKEND(quartz, Quartz, auto, [ dnl There is no pkgconfig for quartz; lets do a header check AC_CHECK_HEADER(ApplicationServices/ApplicationServices.h, , [use_quartz="no (requires ApplicationServices framework)"]) - if test "x$use_quartz" != "xyes" ; then - dnl check for CoreGraphics as a separate framework - AC_CHECK_HEADER(CoreGraphics/CoreGraphics.h, , [use_quartz="no (requires CoreGraphics framework)"]) - quartz_LIBS="-Xlinker -framework -Xlinker CoreGraphics" - else + AC_CHECK_FUNC([CTFontDrawGlyphs],,[use_quartz_fonts="no (requires Mac OS X 10.7 or later)"]) + if test "x$use_quartz" = "xyes" ; then quartz_LIBS="-Xlinker -framework -Xlinker ApplicationServices" fi ]) diff --git a/meson.build b/meson.build index 8c07f02e9..aa6506332 100644 --- a/meson.build +++ b/meson.build @@ -455,7 +455,6 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled() deps += [quartz_deps] feature_conf.set('CAIRO_HAS_QUARTZ_SURFACE', 1) - feature_conf.set('CAIRO_HAS_QUARTZ_FONT', 1) feature_conf.set('CAIRO_HAS_QUARTZ_IMAGE_SURFACE', 1) built_features += [ @@ -468,13 +467,18 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled() 'name': 'cairo-quartz-image', 'description': 'Quartz Image surface backend', 'deps': quartz_deps, - }, - { - 'name': 'cairo-quartz-font', - 'description': 'Quartz font backend', - 'deps': quartz_deps, - }, - ] + }] + compiler = meson.get_compiler('c') + if compiler.has_function('CTFontDrawGlyphs', prefix: '#include <ApplicationServices/ApplicationServices.h>', + dependencies: quartz_deps) + built_features += [ + { + 'name': 'cairo-quartz-font', + 'description': 'Quartz font backend', + 'deps': quartz_deps, + }] + feature_conf.set('CAIRO_HAS_QUARTZ_FONT', 1) + endif endif endif diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 77a9d6a55..641a2dfc7 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -44,15 +44,13 @@ #include "cairo-error-private.h" +//#define DEBUG /* Uncomment this to get debug messages on the console. */ /** * SECTION:cairo-quartz-fonts * @Title: Quartz (CGFont) Fonts - * @Short_Description: Font support via CGFont on OS X + * @Short_Description: Font support via Core Text on Apple operating systems. * @See_Also: #cairo_font_face_t * - * The Quartz font backend is primarily used to render text on Apple - * MacOS X systems. The CGFont API is used for the internal - * implementation of the font backend methods. **/ /** @@ -64,103 +62,37 @@ * Since: 1.6 **/ -static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL; - -/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */ -static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL; -static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL; - -/* These aren't public before 10.5, and some have different names in 10.4 */ -static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL; -static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL; -static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL; -static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL; - -/* Not public, but present */ -static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; +/* These are private functions */ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; - -/* Not public in the least bit */ -static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL; - -/* CTFontCreateWithGraphicsFont is not available until 10.5 */ -typedef const struct __CTFontDescriptor *CTFontDescriptorRef; -static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform*, CTFontDescriptorRef) = NULL; -static CGPathRef (*CTFontCreatePathForGlyphPtr) (CTFontRef, CGGlyph, CGAffineTransform *) = NULL; - -/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ -typedef struct { - int ascent; - 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; - -/* Not public anymore in 64-bits nor in 10.7 */ static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; -static cairo_bool_t _cairo_quartz_font_symbols_present = 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 +#define FONT_COLOR_GLYPHS kCTFontTraitColorGlyphs +#else +#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal +#define FONT_COLOR_GLYPHS kCTFontColorGlyphsTrait +#endif + static void quartz_font_ensure_symbols(void) { if (_cairo_quartz_font_symbol_lookup_done) return; - CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag"); - - /* Look for the 10.5 versions first */ - CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes"); - if (!CGFontGetGlyphBBoxesPtr) - CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes"); - - CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars"); - if (!CGFontGetGlyphsForUnicharsPtr) - CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); - - CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox"); - - /* We just need one of these two */ - CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName"); - CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName"); - - /* These have the same name in 10.4 and 10.5 */ - CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); - CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); - - CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); - CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph"); - if (!CTFontCreateWithGraphicsFontPtr || !CTFontCreatePathForGlyphPtr) - CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); - - 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"); + CGContextGetAllowsFontSmoothingPtr = + dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); - if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && - CGFontGetGlyphBBoxesPtr && - CGFontGetGlyphsForUnicharsPtr && - CGFontGetUnitsPerEmPtr && - CGFontGetGlyphAdvancesPtr && - ((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) && - (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) - _cairo_quartz_font_symbols_present = TRUE; - _cairo_quartz_font_symbol_lookup_done = TRUE; } @@ -169,6 +101,7 @@ 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 { @@ -187,14 +120,10 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, { const char *family; char *full_name; - CFStringRef cgFontName = NULL; + CFStringRef FontName = NULL; CGFontRef cgFont = NULL; int loop; - quartz_font_ensure_symbols(); - if (! _cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - family = toy_face->family; full_name = _cairo_malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. /* handle CSS-ish faces */ @@ -221,23 +150,19 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, if (loop < 3 && (loop & 1) == 0) { if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) - strcat (full_name, " Bold"); + strcat (full_name, "-Bold"); } if (loop < 3 && (loop & 2) == 0) { if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) - strcat (full_name, " Italic"); + strcat (full_name, "-Italic"); else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) - strcat (full_name, " Oblique"); + strcat (full_name, "-Oblique"); } - if (CGFontCreateWithFontNamePtr) { - cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - cgFont = CGFontCreateWithFontNamePtr (cgFontName); - CFRelease (cgFontName); - } else { - cgFont = CGFontCreateWithNamePtr (full_name); - } + FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); + cgFont = CGFontCreateWithFontName (FontName); + CFRelease (FontName); if (cgFont) break; @@ -249,7 +174,7 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, } *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); - CGFontRelease (cgFont); + CFRelease (cgFont); return CAIRO_STATUS_SUCCESS; } @@ -265,6 +190,51 @@ _cairo_quartz_font_face_destroy (void *abstract_face) 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, @@ -276,13 +246,9 @@ _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; - quartz_font_ensure_symbols(); - if (!_cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t)); if (font == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -295,48 +261,29 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, if (status) goto FINISH; - ems = CGFontGetUnitsPerEmPtr (font_face->cgFont); - + ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, font_scale, NULL, NULL); /* initialize metrics */ - if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) { - fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems); - fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems); - fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + - (CGFontGetLeadingPtr (font_face->cgFont) / ems); - - bbox = CGFontGetFontBBoxPtr (font_face->cgFont); - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; - fs_metrics.max_y_advance = 0.0; - } else { - CGGlyph wGlyph; - UniChar u; - - quartz_CGFontMetrics *m; - m = CGFontGetHMetricsPtr (font_face->cgFont); - - /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */ - if (!m) { - status = _cairo_error(CAIRO_STATUS_NULL_POINTER); - goto FINISH; - } + fs_metrics.ascent = CTFontGetAscent (ctFont); + fs_metrics.descent = CTFontGetDescent (ctFont); + fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + + CTFontGetLeading (ctFont); + + 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); - fs_metrics.ascent = (m->ascent / ems); - fs_metrics.descent = - (m->descent / ems); - fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems); - - /* We kind of have to guess here; W's big, right? */ - u = (UniChar) 'W'; - CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1); - if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) { - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; - fs_metrics.max_y_advance = 0.0; - } else { - fs_metrics.max_x_advance = 0.0; - fs_metrics.max_y_advance = 0.0; - } + 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); } - - status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); +#endif FINISH: if (status != CAIRO_STATUS_SUCCESS) { @@ -355,6 +302,23 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { _cairo_quartz_font_face_scaled_font_create }; +static inline cairo_quartz_font_face_t* +_cairo_quartz_font_face_create () +{ + cairo_quartz_font_face_t *font_face = + _cairo_malloc (sizeof (cairo_quartz_font_face_t)); + + if (!font_face) { + cairo_status_t ignore_status; + ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_quartz_font_face_t *)&_cairo_font_face_nil; + } + + _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); + + return font_face; +} + /** * cairo_quartz_font_face_create_for_cgfont: * @font: a #CGFontRef obtained through a method external to cairo. @@ -371,21 +335,13 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { - cairo_quartz_font_face_t *font_face; + cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); - quartz_font_ensure_symbols(); - - font_face = _cairo_malloc (sizeof (cairo_quartz_font_face_t)); - 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; - } + if (cairo_font_face_status (&font_face->base)) + return &font_face->base; font_face->cgFont = CGFontRetain (font); - _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); - return &font_face->base; } @@ -405,6 +361,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 @@ -419,21 +377,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 = CGFontGetUnitsPerEmPtr (font_face->cgFont); double xmin, ymin, xmax, ymax; if (unlikely (glyph == CGGLYPH_INVALID)) goto FAIL; - if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxesPtr (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 && @@ -444,31 +398,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); @@ -480,13 +412,17 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, extents.y_bearing = - ymax; extents.width = xmax - xmin; extents.height = ymax - ymin; +/* At the necessary 1.0pt ctFont size some glyphs get a reduced + * advance that causes overlaps when scaled up. We can avoid that by + * using the width instead if it's wider. Since cairo doesn't support + * vertical font layout we don't do the same for y_advance. + */ + extents.x_advance = MAX(extents.width, advance.width); + extents.y_advance = advance.height; - extents.x_advance = (double) advance / emscale; - extents.y_advance = 0.0; - -#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: @@ -542,7 +478,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); @@ -555,7 +491,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; @@ -573,13 +508,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - if (CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) { - CTFontRef ctFont = CTFontCreateWithGraphicsFontPtr (font_face->cgFont, 1.0, NULL, NULL); - glyphPath = CTFontCreatePathForGlyphPtr (ctFont, glyph, &textMatrix); - CFRelease (ctFont); - } else { - glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); - } + glyphPath = CTFontCreatePathForGlyph (font->ctFont, glyph, &textMatrix); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -599,30 +528,42 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } +static cairo_bool_t +_cairo_quartz_font_has_color_glyphs (void *abstract_font) +{ + cairo_quartz_scaled_font_t *face = (cairo_quartz_scaled_font_t*)abstract_font; + CTFontSymbolicTraits traits = CTFontGetSymbolicTraits (face->ctFont); + return traits & FONT_COLOR_GLYPHS; +} + static cairo_int_status_t _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, - cairo_scaled_glyph_t *scaled_glyph) + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info, + const cairo_color_t *fg_color) { 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 = CGFontGetUnitsPerEmPtr (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); - + cairo_bool_t is_color = info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; + cairo_format_t format = is_color ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8; + +#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. @@ -639,31 +580,33 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } - if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) - { +/* Note: Certain opentype color fonts have the ability to provide a + * mixture of color and not-color glyphs. The Core Text API doesn't + * expose a way to query individual glyphs and at the level that that + * API is written it's not supposed to matter. The following code will + * cheerfully render any glyph requested onto the image surface. If + * the font is capable of color and + * COLOR_SCALED_GLYPH_INFO_COLOR_SURFACE is set then you get back a + * CAIRO_FORMAT_ARGB32 surface. If a foreground color is provided then + * the glyph will be drawn in that color, otherwise it will be black. + */ + if (unlikely (is_color && ! _cairo_quartz_font_has_color_glyphs (font))) return CAIRO_INT_STATUS_UNSUPPORTED; - } /* 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", @@ -672,70 +615,50 @@ _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); + surface = (cairo_image_surface_t*) cairo_image_surface_create (format, 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); + CGColorSpaceRef colorspace = is_color ? CGColorSpaceCreateDeviceRGB () : NULL; + CGBitmapInfo bitinfo = is_color ? kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst : kCGImageAlphaOnly; + + CGContextRef cgContext = CGBitmapContextCreate (surface->data, + surface->width, + surface->height, + 8, + surface->stride, + colorspace, + bitinfo); 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); - if (CGContextSetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (cgContext)) - CGContextSetAllowsFontSmoothingPtr (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; - } - + if (fg_color) + CGContextSetRGBFillColor (cgContext, fg_color->red, fg_color->green, fg_color->blue, fg_color->alpha); + _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); + CGColorSpaceRelease (colorspace); } cairo_surface_set_device_offset (&surface->base, - glyphOrigin.x, height + glyphOrigin.y); + cairo_surface_mark_dirty (&surface->base); - _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); + if (is_color) + _cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color != NULL); + else + _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); return status; } @@ -755,8 +678,10 @@ _cairo_quartz_scaled_glyph_init (void *abstract_font, if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH)) status = _cairo_quartz_init_glyph_path (font, scaled_glyph); - if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) - status = _cairo_quartz_init_glyph_surface (font, scaled_glyph); + if (!status && (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE ))) + status = _cairo_quartz_init_glyph_surface (font, scaled_glyph, + info, foreground_color); return status; } @@ -766,13 +691,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); - CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len); - + CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyph, len); return glyph[0]; } @@ -783,11 +706,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 = NULL; - - if (likely (CGFontCopyTableForTagPtr)) - data = CGFontCopyTableForTagPtr (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; @@ -816,9 +736,12 @@ static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { NULL, /* text_to_glyphs */ _cairo_quartz_ucs4_to_index, _cairo_quartz_load_truetype_table, - NULL, /* map_glyphs_to_unicode */ + NULL, /*index_to_ucs4*/ + NULL, /* is_synthetic */ + NULL, /* index_to_glyph_name */ + NULL, /* load_type1_data */ + _cairo_quartz_font_has_color_glyphs }; - /* * private methods that the quartz surface uses */ @@ -831,6 +754,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 609a9052c..f142f8471 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -105,6 +105,12 @@ 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 (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 fa6d9b1c9..97ddf2c28 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 @@ -85,46 +91,22 @@ * Since: 1.6 **/ -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 -/* This method is private, but it exists. Its params are are exposed - * as args to the NS* method, but not as CG. - */ -enum PrivateCGCompositeMode { - kPrivateCGCompositeClear = 0, - kPrivateCGCompositeCopy = 1, - kPrivateCGCompositeSourceOver = 2, - kPrivateCGCompositeSourceIn = 3, - kPrivateCGCompositeSourceOut = 4, - kPrivateCGCompositeSourceAtop = 5, - kPrivateCGCompositeDestinationOver = 6, - kPrivateCGCompositeDestinationIn = 7, - kPrivateCGCompositeDestinationOut = 8, - kPrivateCGCompositeDestinationAtop = 9, - kPrivateCGCompositeXOR = 10, - kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) - kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) -}; -typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; -CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); -#endif - -/* Some of these are present in earlier versions of the OS than where - * they are public; other are not public at all - */ - -/* public since 10.6 */ -static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; - -/* not yet public */ -static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; -static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; - -static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; - /* - * Utility functions + * macOS Private functions */ +static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; +static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; +static void +quartz_ensure_symbols() +{ + static cairo_bool_t symbol_lookup_done = FALSE; + if (!symbol_lookup_done) { + CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType"); + CGContextGetAllowsFontSmoothingPtr = + dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + symbol_lookup_done = TRUE; + } +} #ifdef QUARTZ_DEBUG static void quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest); @@ -152,20 +134,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, unsigned int width, unsigned int height); -/* Load all extra symbols */ -static void quartz_ensure_symbols (void) -{ - if (likely (_cairo_quartz_symbol_lookup_done)) - return; - - CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType"); - CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath"); - CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - - _cairo_quartz_symbol_lookup_done = TRUE; -} - CGImageRef CairoQuartzCreateCGImage (cairo_format_t format, unsigned int width, @@ -270,6 +238,7 @@ _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) if (unlikely (cgc == NULL)) return FALSE; + quartz_ensure_symbols (); if (likely (CGContextGetTypePtr)) { /* 4 is the type value of a bitmap context */ return CGContextGetTypePtr (cgc) == 4; @@ -377,58 +346,6 @@ _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path, * Misc helpers/callbacks */ -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 -static PrivateCGCompositeMode -_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return kPrivateCGCompositeClear; - case CAIRO_OPERATOR_SOURCE: - return kPrivateCGCompositeCopy; - case CAIRO_OPERATOR_OVER: - return kPrivateCGCompositeSourceOver; - case CAIRO_OPERATOR_IN: - return kPrivateCGCompositeSourceIn; - case CAIRO_OPERATOR_OUT: - return kPrivateCGCompositeSourceOut; - case CAIRO_OPERATOR_ATOP: - return kPrivateCGCompositeSourceAtop; - case CAIRO_OPERATOR_DEST_OVER: - return kPrivateCGCompositeDestinationOver; - case CAIRO_OPERATOR_DEST_IN: - return kPrivateCGCompositeDestinationIn; - case CAIRO_OPERATOR_DEST_OUT: - return kPrivateCGCompositeDestinationOut; - case CAIRO_OPERATOR_DEST_ATOP: - return kPrivateCGCompositeDestinationAtop; - case CAIRO_OPERATOR_XOR: - return kPrivateCGCompositeXOR; - case CAIRO_OPERATOR_ADD: - return kPrivateCGCompositePlusLighter; - - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - default: - ASSERT_NOT_REACHED; - } -} -#endif static CGBlendMode _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) @@ -465,7 +382,6 @@ _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) case CAIRO_OPERATOR_HSL_LUMINOSITY: return kCGBlendModeLuminosity; -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 case CAIRO_OPERATOR_CLEAR: return kCGBlendModeClear; case CAIRO_OPERATOR_SOURCE: @@ -490,21 +406,6 @@ _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) return kCGBlendModeXOR; case CAIRO_OPERATOR_ADD: return kCGBlendModePlusLighter; -#else - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: -#endif - case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_SATURATE: default: @@ -534,16 +435,6 @@ _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op) return CAIRO_INT_STATUS_UNSUPPORTED; } -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 - if (op <= CAIRO_OPERATOR_ADD) { - PrivateCGCompositeMode compmode; - - compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op); - CGContextSetCompositeOperation (context, compmode); - return CAIRO_STATUS_SUCCESS; - } -#endif - blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op); CGContextSetBlendMode (context, blendmode); return CAIRO_STATUS_SUCCESS; @@ -1472,7 +1363,7 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface, cairo_surface_t *return_surface = NULL; unsigned int stride, bitinfo, bpp, color_comps; CGColorSpaceRef colorspace; - void *imageData; + unsigned char *imageData; cairo_format_t format; if (IS_EMPTY (surface)) @@ -1558,13 +1449,6 @@ _cairo_quartz_surface_finish (void *abstract_surface) surface->cgContext = NULL; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - if (surface->imageData) { - free (surface->imageData); - surface->imageData = NULL; - } -#endif - if (surface->cgLayer) { CGLayerRelease (surface->cgLayer); @@ -2016,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; @@ -2045,44 +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); - if (CGContextSetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) - { - didForceFontSmoothing = TRUE; - CGContextSetAllowsFontSmoothingPtr (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 */ @@ -2099,43 +1954,30 @@ _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); BAIL: if (didForceFontSmoothing) - CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); + CGContextSetAllowsFontSmoothing (state.cgMaskContext, FALSE); _cairo_quartz_teardown_state (&state, extents); @@ -2319,8 +2161,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, { cairo_quartz_surface_t *surface; - quartz_ensure_symbols (); - /* Init the base surface */ surface = _cairo_malloc (sizeof (cairo_quartz_surface_t)); if (unlikely (surface == NULL)) @@ -2342,9 +2182,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, surface->extents.width = width; surface->extents.height = height; surface->virtual_extents = surface->extents; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - surface->imageData = NULL; -#endif if (IS_EMPTY (surface)) { surface->cgContext = NULL; @@ -2473,16 +2310,6 @@ cairo_quartz_surface_create (cairo_format_t format, * so we don't have to anything special on allocation. */ stride = (stride + 15) & ~15; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - imageData = _cairo_malloc_ab (height, stride); - if (unlikely (!imageData)) { - CGColorSpaceRelease (cgColorspace); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - /* zero the memory to match the image surface behavior */ - memset (imageData, 0, height * stride); -#endif /* For newer macOS versions let Core Graphics manage the buffer. */ cgc = CGBitmapContextCreate (imageData, width, height, @@ -2515,9 +2342,6 @@ cairo_quartz_surface_create (cairo_format_t format, return &surf->base; } -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - surf->imageData = imageData; -#endif surf->base.is_clear = TRUE; return &surf->base; @@ -2633,7 +2457,7 @@ quartz_image_to_png (CGImageRef image, const char *dest) memset (pathbuf, 0, sizeof (pathbuf)); dest = dest ? dest : image_name; - snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++, ext); + snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++); path = CFStringCreateWithCString (NULL, pathbuf, kCFStringEncodingUTF8); url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, FALSE); image_dest = CGImageDestinationCreateWithURL (url, png_utti, 1, NULL); @@ -2648,7 +2472,6 @@ quartz_image_to_png (CGImageRef image, const char *dest) void quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest) { - static int sctr = 0; CGImageRef image; if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) { diff --git a/test/Makefile.am b/test/Makefile.am index 8e271fe9b..a74d71a7c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -27,7 +27,7 @@ endif # Need to add quartz-surface-source if CAIRO_HAS_QUARTZ_SURFACE -test_sources += $(quartz_surface_test_sources) +test_sources += "$(quartz_surface_test_sources) $(quartz_color_font_test_sources)" endif if CAIRO_HAS_PDF_SURFACE diff --git a/test/Makefile.sources b/test/Makefile.sources index c180289ab..330aa9da1 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -434,6 +434,8 @@ egl_surface_test_sources = \ quartz_surface_test_sources = quartz-surface-source.c +quartz_color_font_test_sources = quartz-color-font.c + pdf_surface_test_sources = \ pdf-features.c \ pdf-mime-data.c \ diff --git a/test/meson.build b/test/meson.build index c0be0e086..21a0588e7 100644 --- a/test/meson.build +++ b/test/meson.build @@ -438,6 +438,7 @@ test_egl_sources = [ test_quartz_sources = [ 'quartz-surface-source.c', + 'quartz-color-font.c', ] test_pdf_sources = [ diff --git a/test/quartz-color-font.c b/test/quartz-color-font.c new file mode 100644 index 000000000..7dae214f6 --- /dev/null +++ b/test/quartz-color-font.c @@ -0,0 +1,54 @@ +/* + * Copyright © 2022 John Ralls <jralls@ceridwen.us> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "cairo-test.h" +#include <cairo-quartz.h> + +#define WIDTH 100 +#define HEIGHT 50 + +#define FONT "Apple Color Emoji" + +static const char smiley_face_utf8[] = { 0xf0, 0x9f, 0x99, 0x82, 0x00 }; /* U+1F642 */ + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_select_font_face(cr, FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (cr, HEIGHT/2); + cairo_move_to (cr, width/5, 2*height/3); + + cairo_show_text(cr, smiley_face_utf8); + cairo_show_text(cr, smiley_face_utf8); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (quartz_color_font, + "Test color font", + "quartz, font", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, draw) diff --git a/test/reference/inverse-text.quartz.ref.png b/test/reference/inverse-text.quartz.ref.png Binary files differindex df0ea57ea..f57f45035 100644 --- a/test/reference/inverse-text.quartz.ref.png +++ b/test/reference/inverse-text.quartz.ref.png diff --git a/test/reference/overlapping-glyphs.quartz.argb32.ref.png b/test/reference/overlapping-glyphs.quartz.argb32.ref.png Binary files differindex 2bbbb3944..44200d59e 100644 --- a/test/reference/overlapping-glyphs.quartz.argb32.ref.png +++ b/test/reference/overlapping-glyphs.quartz.argb32.ref.png diff --git a/test/reference/overlapping-glyphs.quartz.rgb24.ref.png b/test/reference/overlapping-glyphs.quartz.rgb24.ref.png Binary files differindex a3961f8bf..d236e19a5 100644 --- a/test/reference/overlapping-glyphs.quartz.rgb24.ref.png +++ b/test/reference/overlapping-glyphs.quartz.rgb24.ref.png diff --git a/test/reference/pdf-operators-text.quartz.ref.png b/test/reference/pdf-operators-text.quartz.ref.png Binary files differnew file mode 100644 index 000000000..ecfa7cc5d --- /dev/null +++ b/test/reference/pdf-operators-text.quartz.ref.png diff --git a/test/reference/quartz-color-font.quartz.rgb24.ref.png b/test/reference/quartz-color-font.quartz.rgb24.ref.png Binary files differnew file mode 100644 index 000000000..00a0c4499 --- /dev/null +++ b/test/reference/quartz-color-font.quartz.rgb24.ref.png diff --git a/test/reference/quartz-color-font.ref.png b/test/reference/quartz-color-font.ref.png Binary files differnew file mode 100644 index 000000000..e52674ba0 --- /dev/null +++ b/test/reference/quartz-color-font.ref.png diff --git a/test/reference/select-font-face.quartz.ref.png b/test/reference/select-font-face.quartz.ref.png Binary files differindex 69fd2e239..1100fe28c 100644 --- a/test/reference/select-font-face.quartz.ref.png +++ b/test/reference/select-font-face.quartz.ref.png |