Source code

Revision control

Copy as Markdown

Other Tools

From 5be6ed95c097f3ad1090c43b59661df41a25d542 Mon Sep 17 00:00:00 2001
From: Jonathan Kew <jkew@mozilla.com>
Date: Wed, 22 Apr 2026 13:00:22 -0700
Subject: [PATCH 26/29] Revert cairo-quartz-*.[ch] to the pre-1.18.0
Mozilla-maintained state
The quartz backend is pinned to the version that preceded cairo 1.18.0;
upstream changes to the quartz code after 1.17.8 are not carried into the
Mozilla tree. This patch is the last in the series and must be applied on top
of all earlier patches. It restores the pre-1.18.0 Mozilla-maintained state
of cairo-quartz-font.c, cairo-quartz-image-surface.c, cairo-quartz-image.h,
cairo-quartz-private.h, cairo-quartz.h, and cairo-quartz-surface.c.
---
src/cairo-quartz-font.c | 584 +++++++-----
src/cairo-quartz-image-surface.c | 184 ++--
src/cairo-quartz-image.h | 2 +-
src/cairo-quartz-private.h | 47 +-
src/cairo-quartz-surface.c | 1522 +++++++++++++++++-------------
src/cairo-quartz.h | 12 +-
6 files changed, 1301 insertions(+), 1050 deletions(-)
diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
index 9c0eac367..2819ed02a 100644
--- a/src/cairo-quartz-font.c
+++ b/src/cairo-quartz-font.c
@@ -44,14 +44,15 @@
#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 Core Text on Apple operating systems.
+ * @Short_Description: Font support via CGFont on OS X
* @See_Also: #cairo_font_face_t
*
- * Provide support for font faces via Core Text.
+ * 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.
**/
/**
@@ -63,41 +64,121 @@
* Since: 1.6
**/
-/* These are private functions */
+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;
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;
+static double (*CTFontGetAdvancesForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGSize *, CFIndex) = NULL;
+static CGRect (*CTFontGetBoundingRectsForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGRect *, CFIndex) = 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;
+
#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID
-static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
+/* Not public anymore in 64-bits nor in 10.7 */
+static ATSFontRef (*FMGetATSFontRefFromFontPtr) (ATSUFontID iFont) = NULL;
#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
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;
+static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
/* 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 kCTFontColorGlyphsTrait
-#else
-#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal
-#define FONT_COLOR_GLYPHS kCTFontTraitColorGlyphs
-#endif
-
static void
quartz_font_ensure_symbols(void)
{
if (_cairo_quartz_font_symbol_lookup_done)
return;
- CGContextGetAllowsFontSmoothingPtr =
- dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ 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");
+
+ /*
+ * Some Tiger systems have a partial version of CoreText, which
+ * has incompatible signatures: CTFontCreateWithGraphicsFont
+ * accepts a double for the size argument even on i386 and all
+ * functions omit the CTFontOrientation arguments. Since the 10.4
+ * CoreText library does not provide the CTFontCreatePathForGlyph
+ * symbol, use it to determine whether to use CoreText at all.
+ */
+ CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph");
+ if (CTFontCreatePathForGlyphPtr) {
+ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
+ CTFontGetAdvancesForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetAdvancesForGlyphs");
+ CTFontGetBoundingRectsForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetBoundingRectsForGlyphs");
+ } else {
+ 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");
#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID
FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
+ 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;
}
@@ -106,13 +187,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;
};
/*
@@ -125,10 +206,14 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
{
const char *family;
char *full_name;
- CFStringRef FontName = NULL;
+ CFStringRef cgFontName = 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 */
@@ -155,29 +240,22 @@ _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");
}
- FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
- cgFont = CGFontCreateWithFontName (FontName);
- CFRelease (FontName);
-
- if (!cgFont) {
- /* Attempt to create font by replacing hyphens for spaces in font name. */
- for (size_t i = 0; i < strlen (full_name); i++) {
- if (full_name[i] == '-')
- full_name[i] = ' ';
- }
- FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
- cgFont = CGFontCreateWithFontName (FontName);
- CFRelease (FontName);
+ if (CGFontCreateWithFontNamePtr) {
+ cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
+ cgFont = CGFontCreateWithFontNamePtr (cgFontName);
+ CFRelease (cgFontName);
+ } else {
+ cgFont = CGFontCreateWithNamePtr (full_name);
}
if (cgFont)
@@ -190,7 +268,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);
- CFRelease (cgFont);
+ CGFontRelease (cgFont);
return CAIRO_STATUS_SUCCESS;
}
@@ -200,57 +278,15 @@ _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;
}
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,
@@ -262,10 +298,14 @@ _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;
- CTFontRef ctFont;
+ double ems;
CGRect bbox;
- font = _cairo_calloc (sizeof(cairo_quartz_scaled_font_t));
+ 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);
@@ -277,29 +317,48 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face,
if (status)
goto FINISH;
- ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, font_scale, NULL, NULL);
+ ems = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+
/* initialize metrics */
- 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);
+ 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 = 0;
+ UniChar u;
+
+ quartz_CGFontMetrics *m;
+ m = CGFontGetHMetricsPtr (font_face->cgFont);
- 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);
+ /* 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 = (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;
+ }
}
-#endif
+
+ status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
FINISH:
if (status != CAIRO_STATUS_SUCCESS) {
@@ -318,23 +377,6 @@ 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_calloc (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.
@@ -351,13 +393,26 @@ _cairo_quartz_font_face_create ()
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 ();
+ cairo_quartz_font_face_t *font_face;
- if (cairo_font_face_status (&font_face->base))
- return &font_face->base;
+ 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;
+ }
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;
}
@@ -377,8 +432,6 @@ _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
@@ -393,30 +446,67 @@ _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);
- CGSize advance;
+ int advance;
CGRect bbox;
double xmin, ymin, xmax, ymax;
if (unlikely (glyph == CGGLYPH_INVALID))
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,
- if (unlikely (bbox.origin.x == -32767 &&
- bbox.origin.y == -32767 &&
- bbox.size.width == 65534 &&
- bbox.size.height == 65534)) {
- bbox.origin.x = bbox.origin.y = 0;
- bbox.size.width = bbox.size.height = 0;
- }
+ if (font_face->ctFont) {
+ CGSize advanceSize;
+ CTFontGetBoundingRectsForGlyphsPtr (font_face->ctFont,
+ kCTFontOrientationDefault,
+ &glyph, &bbox, 1);
+
+ CTFontGetAdvancesForGlyphsPtr (font_face->ctFont,
+ kCTFontOrientationDefault,
+ &glyph, &advanceSize, 1);
+
+ extents.x_advance = advanceSize.width;
+ extents.y_advance = advanceSize.height;
+ } else if (CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) &&
+ CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) {
+ double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+
+ /* broken fonts like Al Bayan return incorrect bounds for some null
+ if (unlikely (bbox.origin.x == -32767 &&
+ bbox.origin.y == -32767 &&
+ bbox.size.width == 65534 &&
+ bbox.size.height == 65534)) {
+ bbox.origin.x = bbox.origin.y = 0;
+ 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);
-#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);
+ extents.x_advance = advance / emscale;
+ extents.y_advance = 0.0;
+ } else {
+ goto FAIL;
+ };
+
+ /* 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
xmin = CGRectGetMinX(bbox);
@@ -428,17 +518,10 @@ _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;
-#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);
+#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);
#endif
FAIL:
@@ -494,7 +577,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);
@@ -507,6 +590,7 @@ 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;
@@ -524,7 +608,11 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
-font->base.scale.yy,
0, 0);
- glyphPath = CTFontCreatePathForGlyph (font->ctFont, glyph, &textMatrix);
+ if (font_face->ctFont) {
+ glyphPath = CTFontCreatePathForGlyphPtr (font_face->ctFont, glyph, &textMatrix);
+ } else {
+ glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
+ }
if (!glyphPath)
return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -544,42 +632,30 @@ _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_info_t info,
- const cairo_color_t *fg_color)
+ 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);
- cairo_text_extents_t metrics = scaled_glyph->fs_metrics;
- CGRect bbox = CGRectMake (metrics.x_bearing, -(metrics.y_bearing + metrics.height),
- metrics.width, metrics.height);
+
+ int advance;
+ CGRect bbox;
double width, height;
+ double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+ CGContextRef cgContext = NULL;
CGAffineTransform textMatrix;
CGRect glyphRect, glyphRectInt;
CGPoint glyphOrigin;
- 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
+
+ //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);
+
/* 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.
@@ -596,33 +672,31 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
return CAIRO_STATUS_SUCCESS;
}
-/* 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)))
+ if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
+ !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+ {
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 = CGRectApplyAffineTransform (bbox, textMatrix);
+ 0, -0);
+ glyphRect = CGRectMake (bbox.origin.x / emscale,
+ bbox.origin.y / emscale,
+ bbox.size.width / emscale,
+ bbox.size.height / emscale);
+
+ glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);
/* Round the rectangle outwards, so that we don't have to deal
* with non-integer-pixel origins or dimensions.
*/
glyphRectInt = CGRectIntegral (glyphRect);
-#ifdef DEBUG
+#if 0
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",
@@ -631,50 +705,70 @@ _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;
- surface = (cairo_image_surface_t*) cairo_image_surface_create (format, width, 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) {
- 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);
+ 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);
}
- 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);
+ 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;
+ }
+
CGContextSetAlpha (cgContext, 1.0);
- CGContextTranslateCTM (cgContext, -glyphOrigin.x, -glyphOrigin.y);
- CGContextConcatCTM (cgContext, textMatrix);
- CTFontDrawGlyphs (font->ctFont, &glyph, &CGPointZero, 1, cgContext);
+ CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
+
CGContextRelease (cgContext);
- CGColorSpaceRelease (colorspace);
}
cairo_surface_set_device_offset (&surface->base,
- glyphOrigin.x,
height + glyphOrigin.y);
- cairo_surface_mark_dirty (&surface->base);
- if (is_color)
- _cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color);
- else
- _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
+ _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
return status;
}
@@ -682,8 +776,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
static cairo_int_status_t
_cairo_quartz_scaled_glyph_init (void *abstract_font,
cairo_scaled_glyph_t *scaled_glyph,
- cairo_scaled_glyph_info_t info,
- const cairo_color_t *foreground_color)
+ cairo_scaled_glyph_info_t info)
{
cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
@@ -694,10 +787,8 @@ _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 |
- CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE )))
- status = _cairo_quartz_init_glyph_surface (font, scaled_glyph,
- info, foreground_color);
+ if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE))
+ status = _cairo_quartz_init_glyph_surface (font, scaled_glyph);
return status;
}
@@ -707,11 +798,13 @@ _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 (font->ctFont, utf16, glyph, len);
+ CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len);
+
return glyph[0];
}
@@ -722,8 +815,11 @@ _cairo_quartz_load_truetype_table (void *abstract_font,
unsigned char *buffer,
unsigned long *length)
{
- cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
- CFDataRef data = CTFontCopyTable (font->ctFont, tag, kCTFontTableOptionNoOptions);
+ 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);
if (!data)
return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -752,12 +848,9 @@ 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, /*index_to_ucs4*/
- NULL, /* is_synthetic */
- NULL, /* index_to_glyph_name */
- NULL, /* load_type1_data */
- _cairo_quartz_font_has_color_glyphs
+ NULL, /* map_glyphs_to_unicode */
};
+
/*
* private methods that the quartz surface uses
*/
@@ -771,42 +864,13 @@ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
}
CTFontRef
-_cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *abstract_font)
+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
{
- cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
+ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
- return font->ctFont;
+ return ffont->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
*/
@@ -855,4 +919,4 @@ cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
}
-#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
+#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
\ No newline at end of file
diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c
index 03fea5b37..5b229d166 100644
--- a/src/cairo-quartz-image-surface.c
+++ b/src/cairo-quartz-image-surface.c
@@ -49,14 +49,11 @@
#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)))
#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT)))
-/**
- * CAIRO_HAS_QUARTZ_IMAGE_SURFACE:
- *
- * Defined if the Quartz image surface backend is available.
- * This macro can be used to conditionally compile backend-specific code.
- *
- * Since: 1.10
- **/
+static void
+DataProviderReleaseCallback (void *image_info, const void *data, size_t size)
+{
+ free (image_info);
+}
static cairo_surface_t *
_cairo_quartz_image_surface_create_similar (void *asurface,
@@ -90,9 +87,8 @@ _cairo_quartz_image_surface_finish (void *asurface)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
- CGContextRelease (surface->cgContext);
- if (surface->imageSurface)
- cairo_surface_destroy ( (cairo_surface_t*) surface->imageSurface);
+ CGImageRelease (surface->image);
+ cairo_surface_destroy ( (cairo_surface_t*) surface->imageSurface);
return CAIRO_STATUS_SUCCESS;
}
@@ -138,6 +134,47 @@ _cairo_quartz_image_surface_get_extents (void *asurface,
return TRUE;
}
+/* we assume some drawing happened to the image buffer; make sure it's
+ * represented in the CGImage on flush()
+ */
+
+static cairo_status_t
+_cairo_quartz_image_surface_flush (void *asurface,
+ unsigned flags)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+ CGImageRef oldImage = surface->image;
+ CGImageRef newImage = NULL;
+ void *image_data;
+ const unsigned int size = surface->imageSurface->height * surface->imageSurface->stride;
+ if (flags)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* XXX only flush if the image has been modified. */
+
+ image_data = _cairo_malloc_ab ( surface->imageSurface->height,
+ surface->imageSurface->stride);
+ if (unlikely (!image_data))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (image_data, surface->imageSurface->data,
+ surface->imageSurface->height * surface->imageSurface->stride);
+ newImage = CairoQuartzCreateCGImage (surface->imageSurface->format,
+ surface->imageSurface->width,
+ surface->imageSurface->height,
+ surface->imageSurface->stride,
+ image_data,
+ TRUE,
+ NULL,
+ DataProviderReleaseCallback,
+ image_data);
+
+ surface->image = newImage;
+ CGImageRelease (oldImage);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
static cairo_int_status_t
_cairo_quartz_image_surface_paint (void *abstract_surface,
cairo_operator_t op,
@@ -238,7 +275,7 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
_cairo_quartz_image_surface_get_extents,
NULL, /* get_font_options */
- NULL, /*surface_flush */
+ _cairo_quartz_image_surface_flush,
NULL, /* mark_dirty_rectangle */
_cairo_quartz_image_surface_paint,
@@ -253,9 +290,12 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
* cairo_quartz_image_surface_create:
* @image_surface: a cairo image surface to wrap with a quartz image surface
*
- * Creates a Quartz surface backed by a CGBitmapContext that references the
+ * Creates a Quartz surface backed by a CGImageRef that references the
* given image surface. The resulting surface can be rendered quickly
- * when used as a source when rendering to a #cairo_quartz_surface.
+ * when used as a source when rendering to a #cairo_quartz_surface. If
+ * the data in the image surface is ever updated, cairo_surface_flush()
+ * must be called on the #cairo_quartz_image_surface to ensure that the
+ * CGImageRef refers to the updated data.
*
* Return value: the newly created surface.
*
@@ -265,11 +305,13 @@ cairo_surface_t *
cairo_quartz_image_surface_create (cairo_surface_t *surface)
{
cairo_quartz_image_surface_t *qisurf;
+
+ CGImageRef image;
+
cairo_image_surface_t *image_surface;
int width, height, stride;
cairo_format_t format;
- CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
- CGColorSpaceRef colorspace;
+ void *image_data;
if (surface->status)
return surface;
@@ -292,108 +334,58 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface)
if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24)
return SURFACE_ERROR_INVALID_FORMAT;
- qisurf = _cairo_calloc (sizeof(cairo_quartz_image_surface_t));
+ qisurf = _cairo_malloc (sizeof(cairo_quartz_image_surface_t));
if (qisurf == NULL)
return SURFACE_ERROR_NO_MEMORY;
+ memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t));
+
+ image_data = _cairo_malloc_ab (height, stride);
+ if (unlikely (!image_data)) {
+ free(qisurf);
+ return SURFACE_ERROR_NO_MEMORY;
+ }
+
+ memcpy (image_data, image_surface->data, height * stride);
+ image = CairoQuartzCreateCGImage (format,
+ width, height,
+ stride,
+ image_data,
+ TRUE,
+ NULL,
+ DataProviderReleaseCallback,
+ image_data);
+
+ if (!image) {
+ free (qisurf);
+ return SURFACE_ERROR_NO_MEMORY;
+ }
+
_cairo_surface_init (&qisurf->base,
&cairo_quartz_image_surface_backend,
NULL, /* device */
_cairo_content_from_format (format),
FALSE); /* is_vector */
- if (_cairo_surface_is_quartz (surface) || _cairo_surface_is_quartz_image (surface)) {
- CGContextRef context = cairo_quartz_surface_get_cg_context(surface);
- colorspace = _cairo_quartz_create_color_space (context);
- }
- else {
-#if CAIRO_HAS_QUARTZ_APPLICATION_SERVICES /* available on macOS but not iOS */
- colorspace = CGDisplayCopyColorSpace (CGMainDisplayID ());
-#else
- colorspace = CGColorSpaceCreateDeviceRGB ();
-#endif
- }
-
- bitinfo |= format == CAIRO_FORMAT_ARGB32 ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
-
qisurf->width = width;
qisurf->height = height;
- qisurf->cgContext = CGBitmapContextCreate (image_surface->data, width, height, 8, image_surface->stride,
- colorspace, bitinfo);
+ qisurf->image = image;
qisurf->imageSurface = (cairo_image_surface_t*) cairo_surface_reference(surface);
- CGColorSpaceRelease (colorspace);
return &qisurf->base;
}
-/**
- * cairo_quartz_image_surface_get_image:
- * @surface: a #cairo_surface_t
- *
- * Returns a #cairo_surface_t image surface that refers to the same bits
- * as the image of the quartz surface.
- *
- * Return value: a #cairo_surface_t (owned by the quartz #cairo_surface_t),
- * or %NULL if the quartz surface is not an image surface.
- *
- * Since: 1.6
- **/
cairo_surface_t *
-cairo_quartz_image_surface_get_image (cairo_surface_t *surface)
+cairo_quartz_image_surface_get_image (cairo_surface_t *asurface)
{
- cairo_quartz_image_surface_t *qsurface = (cairo_quartz_image_surface_t*) surface;
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface;
/* Throw an error for a non-quartz surface */
- if (! _cairo_surface_is_quartz (surface)) {
+ if (! _cairo_surface_is_quartz (asurface)) {
return SURFACE_ERROR_TYPE_MISMATCH;
}
- return (cairo_surface_t*) qsurface->imageSurface;
-}
-
-/*
- * _cairo_quartz_image_surface_get_cg_context:
- * @surface: the Cairo Quartz surface
- *
- * Returns the CGContextRef that the given Quartz surface is backed
- * by.
- *
- * A call to cairo_surface_flush() is required before using the
- * CGContextRef to ensure that all pending drawing operations are
- * finished and to restore any temporary modification cairo has made
- * to its state. A call to cairo_surface_mark_dirty() is required
- * after the state or the content of the CGContextRef has been
- * modified.
- *
- * Return value: the CGContextRef for the given surface.
- *
- **/
-CGContextRef
-_cairo_quartz_image_surface_get_cg_context (cairo_surface_t *surface)
-{
- if (surface && _cairo_surface_is_quartz_image (surface)) {
- cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
- return quartz->cgContext;
- } else
- return NULL;
-}
-
-/*
- * _cairo_surface_is_quartz_image:
- * @surface: a #cairo_surface_t
- *
- * Checks if a surface is a #cairo_quartz_surface_t
- *
- * Return value: True if the surface is an quartz surface
- **/
-cairo_bool_t
-_cairo_surface_is_quartz_image (const cairo_surface_t *surface) {
- return surface->backend == &cairo_quartz_image_surface_backend;
-}
-
-cairo_bool_t
-_cairo_quartz_image_surface_is_zero (const cairo_quartz_image_surface_t *surface) {
- return surface->width == 0 || surface->height == 0;
-}
+ return (cairo_surface_t*) surface->imageSurface;
+}
\ No newline at end of file
diff --git a/src/cairo-quartz-image.h b/src/cairo-quartz-image.h
index 9e8409c11..396700fd2 100644
--- a/src/cairo-quartz-image.h
+++ b/src/cairo-quartz-image.h
@@ -54,4 +54,4 @@ CAIRO_END_DECLS
# error Cairo was not compiled with support for the quartz-image backend
#endif /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */
-#endif /* CAIRO_QUARTZ_IMAGE_H */
+#endif /* CAIRO_QUARTZ_IMAGE_H */
\ No newline at end of file
diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h
index bdfdf8c2b..3bff8025e 100644
--- a/src/cairo-quartz-private.h
+++ b/src/cairo-quartz-private.h
@@ -55,6 +55,7 @@ typedef enum {
DO_DIRECT,
DO_SHADING,
DO_IMAGE,
+ DO_TILED_IMAGE,
DO_LAYER
} cairo_quartz_action_t;
@@ -67,21 +68,28 @@ typedef struct cairo_quartz_surface {
CGContextRef cgContext;
CGAffineTransform cgContextBaseCTM;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600
void *imageData;
-#endif
+ cairo_surface_t *imageSurfaceEquiv;
cairo_surface_clipper_t clipper;
+
+ /**
+ * If non-null, this is the CGLayer for the surface.
+ */
+ CGLayerRef cgLayer;
+
cairo_rectangle_int_t extents;
cairo_rectangle_int_t virtual_extents;
- CGLayerRef cgLayer;
+
+ cairo_bool_t ownsData;
} cairo_quartz_surface_t;
typedef struct cairo_quartz_image_surface {
cairo_surface_t base;
int width, height;
- CGContextRef cgContext;
+
+ CGImageRef image;
cairo_image_surface_t *imageSurface;
} cairo_quartz_image_surface_t;
@@ -91,27 +99,22 @@ _cairo_quartz_verify_surface_size(int width, int height);
cairo_private cairo_bool_t
_cairo_surface_is_quartz (const cairo_surface_t *surface);
-cairo_private cairo_bool_t
-_cairo_surface_is_quartz_image (const cairo_surface_t *surface);
-cairo_private cairo_bool_t
-_cairo_quartz_image_surface_is_zero (const cairo_quartz_image_surface_t *surface);
-
-cairo_private CGColorSpaceRef
-_cairo_quartz_create_color_space (CGContextRef context);
-cairo_private CGContextRef
-_cairo_quartz_image_surface_get_cg_context (cairo_surface_t *surface);
+cairo_private CGImageRef
+CairoQuartzCreateCGImage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo);
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);
-cairo_status_t _cairo_quartz_surface_to_png (cairo_surface_t *abstract_surface, const char *dest);
-cairo_private void _cairo_quartz_image_to_png (CGImageRef, const char *dest);
+cairo_private CTFontRef
+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
#else
@@ -119,4 +122,4 @@ cairo_private void _cairo_quartz_image_to_png (CGImageRef, const char *dest);
#endif /* CAIRO_HAS_QUARTZ_SURFACE */
-#endif /* CAIRO_QUARTZ_PRIVATE_H */
+#endif /* CAIRO_QUARTZ_PRIVATE_H */
\ No newline at end of file
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 3c21511b9..296e08ba3 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -34,10 +34,10 @@
* Vladimir Vukicevic <vladimir@mozilla.com>
*/
+#define _GNU_SOURCE /* required for RTLD_DEFAULT */
#include "cairoint.h"
#include "cairo-quartz-private.h"
-#include "cairo-quartz-image.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
@@ -57,7 +57,6 @@
#endif
#include <limits.h>
-#include <assert.h>
#undef QUARTZ_DEBUG
@@ -67,30 +66,7 @@
#define ND(_x) do {} while(0)
#endif
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
-#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation
-#else
-#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal
-#endif
-
-static inline cairo_bool_t
-_is_quartz_surface (cairo_surface_t *surface) {
- return _cairo_surface_is_quartz (surface) || _cairo_surface_is_quartz_image (surface);
-}
-
-static inline cairo_bool_t
-_cairo_quartz_surface_is_zero (cairo_quartz_surface_t* surface) {
- return surface->extents.width == 0 || surface->extents.height == 0;
-}
-
-static inline cairo_bool_t
-_cairo_quartz_is_zero_surface (cairo_surface_t* surface) {
- assert (_is_quartz_surface (surface));
- if (_cairo_surface_is_quartz (surface))
- return (_cairo_quartz_surface_is_zero ((cairo_quartz_surface_t*) surface));
- else
- return (_cairo_quartz_image_surface_is_zero ((cairo_quartz_image_surface_t*) surface));
-}
+#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
/**
* SECTION:cairo-quartz
@@ -111,51 +87,56 @@ _cairo_quartz_is_zero_surface (cairo_surface_t* surface) {
* Since: 1.6
**/
-/*
- * macOS Private functions
+#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
*/
-typedef enum {
- kCGContextTypeUnknown,
- kCGContextTypePDF,
- kCGContextTypePostScript,
- kCGContextTypeWindow,
- kCGContextTypeBitmap,
- kCGContextTypeGL,
- kCGContextTypeDisplayList,
- kCGContextTypeKSeparation,
- kCGContextTypeIOSurface,
- kCGContextTypeCount
-} CGContextType;
+/* public since 10.5 */
+static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+/* public since 10.6 */
+static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
-static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+/* not yet public */
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;
- }
-}
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
-typedef struct
-{
- cairo_surface_t base;
- CGImageRef image;
-} cairo_quartz_snapshot_t;
+/* CTFontDrawGlyphs is not available until 10.7 */
+static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
-static cairo_surface_t* _cairo_quartz_snapshot_create (cairo_surface_t *surface);
-static cairo_status_t _cairo_quartz_snapshot_finish (void *surface);
-static CGImageRef _cairo_quartz_surface_snapshot_get_image (cairo_surface_t *surface);
+static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
-static const cairo_surface_backend_t cairo_quartz_snapshot_backend = {
- CAIRO_INTERNAL_SURFACE_TYPE_QUARTZ_SNAPSHOT,
- _cairo_quartz_snapshot_finish,
-};
+/*
+ * Utility functions
+ */
+
+#ifdef QUARTZ_DEBUG
+static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
+static void quartz_image_to_png (CGImageRef, char *dest);
+#endif
static cairo_quartz_surface_t *
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
@@ -163,74 +144,117 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
unsigned int width,
unsigned int height);
-CGColorSpaceRef
-_cairo_quartz_create_color_space (CGContextRef context)
+/* Load all extra symbols */
+static void quartz_ensure_symbols (void)
{
- CGColorSpaceRef color_space = NULL;
- CGContextType cgtype = kCGContextTypeUnknown;
+ if (likely (_cairo_quartz_symbol_lookup_done))
+ return;
- if (context)
- {
- if (CGBitmapContextGetBitsPerPixel (context) < 24)
- return 0;
-
- quartz_ensure_symbols();
- cgtype = CGContextGetTypePtr (context);
- switch (cgtype)
- {
- case kCGContextTypeUnknown:
- break;
- case kCGContextTypePDF:
- color_space = CGColorSpaceCreateDeviceRGB ();
- break;
- case kCGContextTypePostScript:
- case kCGContextTypeWindow:
- break;
- case kCGContextTypeBitmap:
- color_space = CGBitmapContextGetColorSpace (context);
- color_space = CGColorSpaceRetain (color_space);
- break;
- case kCGContextTypeGL:
- case kCGContextTypeDisplayList:
- case kCGContextTypeKSeparation:
- case kCGContextTypeIOSurface:
- case kCGContextTypeCount:
- default:
- break;
- }
- if (color_space)
- return color_space;
- }
-#if CAIRO_HAS_QUARTZ_APPLICATION_SERVICES /* available on macOS but not iOS */
- if (!color_space)
- color_space = CGDisplayCopyColorSpace (CGMainDisplayID ());
-#endif
+ CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
+ CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
- if (!color_space)
- color_space = CGColorSpaceCreateDeviceRGB ();
+ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
- return color_space;
+ _cairo_quartz_symbol_lookup_done = TRUE;
}
-static CGColorRef
-_cairo_quartz_create_cgcolor (CGColorSpaceRef cs, CGFloat red, CGFloat green,
- CGFloat blue, CGFloat alpha)
+CGImageRef
+CairoQuartzCreateCGImage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo)
{
- CGFloat colors[4] = { red, green, blue, alpha };
- CGColorRef cgc;
- if (!CGColorSpaceRetain(cs))
- {
- cs = _cairo_quartz_create_color_space (NULL);
+ CGImageRef image = NULL;
+ CGDataProviderRef dataProvider = NULL;
+ CGColorSpaceRef colorSpace = colorSpaceOverride;
+ CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
+ int bitsPerComponent, bitsPerPixel;
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ if (colorSpace == NULL)
+ colorSpace = CGColorSpaceCreateDeviceRGB ();
+ bitinfo |= kCGImageAlphaPremultipliedFirst;
+ bitsPerComponent = 8;
+ bitsPerPixel = 32;
+ break;
+
+ case CAIRO_FORMAT_RGB24:
+ if (colorSpace == NULL)
+ colorSpace = CGColorSpaceCreateDeviceRGB ();
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ bitsPerComponent = 8;
+ bitsPerPixel = 32;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ bitsPerComponent = 8;
+ bitsPerPixel = 8;
+ break;
+
+ case CAIRO_FORMAT_A1:
+#ifdef WORDS_BIGENDIAN
+ bitsPerComponent = 1;
+ bitsPerPixel = 1;
+ break;
+#endif
+
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_RGB16_565:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ return NULL;
}
- cgc = CGColorCreate (cs, colors);
- CGColorSpaceRelease (cs);
- return cgc;
-}
-static CGColorRef
-_cairo_quartz_black (CGColorSpaceRef cs)
-{
- return _cairo_quartz_create_cgcolor (cs, 0.0, 0.0, 0.0, 1.0);
+ dataProvider = CGDataProviderCreateWithData (releaseInfo,
+ data,
+ height * stride,
+ releaseCallback);
+
+ if (unlikely (!dataProvider)) {
+ // manually release
+ if (releaseCallback)
+ releaseCallback (releaseInfo, data, height * stride);
+ goto FINISH;
+ }
+
+ if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
+ cairo_quartz_float_t decode[] = {1.0, 0.0};
+ image = CGImageMaskCreate (width, height,
+ bitsPerComponent,
+ bitsPerPixel,
+ stride,
+ dataProvider,
+ decode,
+ interpolate);
+ } else
+ image = CGImageCreate (width, height,
+ bitsPerComponent,
+ bitsPerPixel,
+ stride,
+ colorSpace,
+ bitinfo,
+ dataProvider,
+ NULL,
+ interpolate,
+ kCGRenderingIntentDefault);
+
+FINISH:
+
+ CGDataProviderRelease (dataProvider);
+
+ if (colorSpace != colorSpaceOverride)
+ CGColorSpaceRelease (colorSpace);
+
+ return image;
}
static inline cairo_bool_t
@@ -239,10 +263,9 @@ _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) == kCGContextTypeBitmap;
+ return CGContextGetTypePtr (cgc) == 4;
}
/* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
@@ -347,6 +370,58 @@ _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)
@@ -383,6 +458,7 @@ _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:
@@ -407,12 +483,27 @@ _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:
ASSERT_NOT_REACHED;
}
- return kCGBlendModeNormal; /* unreached */
+ return kCGBlendModeNormal; /* just to silence clang warning [-Wreturn-type] */
}
static cairo_int_status_t
@@ -437,6 +528,16 @@ _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;
@@ -684,49 +785,6 @@ CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
&gradient_callbacks);
}
-static CGImageRef
-CairoQuartzCreateCGImageMask (cairo_format_t format,
- unsigned int width,
- unsigned int height,
- unsigned int stride,
- void *data,
- cairo_bool_t interpolate,
- CGDataProviderReleaseDataCallback releaseCallback,
- void *releaseInfo)
-{
- CGImageRef image = NULL;
- CGDataProviderRef dataProvider = NULL;
- int bitsPerComponent = 8, bitsPerPixel = 8;
-
- if (format != CAIRO_FORMAT_A8)
- return NULL;
-
- dataProvider = CGDataProviderCreateWithData (releaseInfo,
- data,
- height * stride,
- releaseCallback);
-
- if (unlikely (!dataProvider)) {
- // manually release
- if (releaseCallback)
- releaseCallback (releaseInfo, data, height * stride);
- goto FINISH;
- }
-
- cairo_quartz_float_t decode[] = {1.0, 0.0};
- image = CGImageMaskCreate (width, height,
- bitsPerComponent,
- bitsPerPixel,
- stride,
- dataProvider,
- decode,
- interpolate);
-
-FINISH:
- CGDataProviderRelease (dataProvider);
- return image;
-}
-
static void
DataProviderReleaseCallback (void *info, const void *data, size_t size)
{
@@ -742,120 +800,104 @@ _cairo_surface_to_cgimage (cairo_surface_t *source,
CGImageRef *image_out)
{
cairo_status_t status;
- cairo_quartz_image_surface_t *image_surface;
- void *image_extra;
+ cairo_image_surface_t *image_surface;
+ void *image_data, *image_extra;
cairo_bool_t acquired = FALSE;
- if (_is_quartz_surface (source)) {
- CGContextRef cgContext = cairo_quartz_surface_get_cg_context(source);
- if (_cairo_quartz_is_zero_surface (source)) {
+ if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
+ *image_out = CGImageRetain (surface->image);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (_cairo_surface_is_quartz (source)) {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
+ if (IS_EMPTY (surface)) {
*image_out = NULL;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
- if (_cairo_quartz_is_cgcontext_bitmap_context (cgContext)) {
- *image_out = _cairo_quartz_surface_snapshot_get_image (source);
- return CAIRO_STATUS_SUCCESS;
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+ *image_out = CGBitmapContextCreateImage (surface->cgContext);
+ if (*image_out)
+ return CAIRO_STATUS_SUCCESS;
}
-
- *image_out = NULL;
- return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
}
if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
- cairo_image_surface_t *surface =
- (cairo_image_surface_t*)cairo_image_surface_create (format, extents->width,
- extents->height);
- if (unlikely (surface->base.status)) {
- status = surface->base.status;
- cairo_surface_destroy (&surface->base);
+ image_surface = (cairo_image_surface_t *)
+ cairo_image_surface_create (format, extents->width, extents->height);
+ if (unlikely (image_surface->base.status)) {
+ status = image_surface->base.status;
+ cairo_surface_destroy (&image_surface->base);
return status;
}
status = _cairo_recording_surface_replay_with_clip (source,
matrix,
- &surface->base,
+ &image_surface->base,
NULL);
if (unlikely (status)) {
- cairo_surface_destroy (&surface->base);
+ cairo_surface_destroy (&image_surface->base);
return status;
}
- image_surface =
- (cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base);
cairo_matrix_init_identity (matrix);
}
else {
- cairo_image_surface_t *surface;
- status = _cairo_surface_acquire_source_image (source, &surface,
+ status = _cairo_surface_acquire_source_image (source, &image_surface,
&image_extra);
if (unlikely (status))
return status;
+ acquired = TRUE;
+ }
- if (surface->format == CAIRO_FORMAT_A8) {
- /* cairo_quartz_image_surface_create doesn't handle CAIRO_FORMAT_A8,
- * so we create a CGImage manually here for masking operations.
- */
- void* image_data = _cairo_malloc_ab (surface->height, surface->stride);
- if (unlikely (!image_data))
- {
- _cairo_surface_release_source_image (source, surface, image_extra);
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- }
+ if (image_surface->width == 0 || image_surface->height == 0) {
+ *image_out = NULL;
+ if (acquired)
+ _cairo_surface_release_source_image (source, image_surface, image_extra);
+ else
+ cairo_surface_destroy (&image_surface->base);
- /* The last row of data may have less than stride bytes so make sure we
- * only copy the minimum amount required from that row.
- */
- memcpy (image_data, surface->data,
- (surface->height - 1) * surface->stride +
- cairo_format_stride_for_width (surface->format,
- surface->width));
- *image_out = CairoQuartzCreateCGImageMask (surface->format,
- surface->width,
- surface->height,
- surface->stride,
- image_data,
- TRUE,
- DataProviderReleaseCallback,
- image_data);
- /* TODO: differentiate memory error and unsupported surface type */
- if (unlikely (*image_out == NULL))
- status = CAIRO_INT_STATUS_UNSUPPORTED;
-
- _cairo_surface_release_source_image (source, surface, image_extra);
- return status;
- } else {
- image_surface =
- (cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base);
- status = image_surface->base.status;
- if (status)
- _cairo_surface_release_source_image (source, surface, image_extra);
- else
- acquired = TRUE;
- }
+ return status;
}
- *image_out = NULL;
- if (image_surface->width > 0 && image_surface->height > 0) {
- *image_out = _cairo_quartz_surface_snapshot_get_image (&image_surface->base);
- status = CAIRO_STATUS_SUCCESS;
- }
+ image_data = _cairo_malloc_ab (image_surface->height, image_surface->stride);
+ if (unlikely (!image_data))
+ {
+ if (acquired)
+ _cairo_surface_release_source_image (source, image_surface, image_extra);
+ else
+ cairo_surface_destroy (&image_surface->base);
- if (acquired) {
- _cairo_surface_release_source_image (source, image_surface->imageSurface, image_extra);
- /* If source itself is an image surface, _cairo_surface_release_source_image
- does not release it, and image_surface->imageSurface still owns a reference
- to it. So we don't clear that field here; _cairo_quartz_image_surface_finish
- will take care of it. */
- if (source->type != CAIRO_SURFACE_TYPE_IMAGE)
- image_surface->imageSurface = NULL;
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
- cairo_surface_destroy (&image_surface->base);
+
+ // The last row of data may have less than stride bytes so make sure we
+ // only copy the minimum amount required from that row.
+ memcpy (image_data, image_surface->data,
+ (image_surface->height - 1) * image_surface->stride +
+ cairo_format_stride_for_width (image_surface->format,
+ image_surface->width));
+ *image_out = CairoQuartzCreateCGImage (image_surface->format,
+ image_surface->width,
+ image_surface->height,
+ image_surface->stride,
+ image_data,
+ TRUE,
+ NULL,
+ DataProviderReleaseCallback,
+ image_data);
/* TODO: differentiate memory error and unsupported surface type */
if (unlikely (*image_out == NULL))
status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (acquired)
+ _cairo_surface_release_source_image (source, image_surface, image_extra);
+ else
+ cairo_surface_destroy (&image_surface->base);
+
return status;
}
@@ -910,15 +952,15 @@ SurfacePatternReleaseInfoFunc (void *ainfo)
}
static cairo_int_status_t
-_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source,
+_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
+ const cairo_pattern_t *apattern,
const cairo_clip_t *clip,
CGPatternRef *cgpat)
{
- cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *) source;
- cairo_surface_t *pat_surf = spattern->surface;
+ cairo_surface_pattern_t *spattern;
+ cairo_surface_t *pat_surf;
cairo_rectangle_int_t extents;
- cairo_format_t format = _cairo_format_from_content (surface->base.content);
+ cairo_format_t format = _cairo_format_from_content (dest->base.content);
CGImageRef image;
CGRect pbounds;
@@ -929,28 +971,42 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
SurfacePatternDrawInfo *info;
cairo_quartz_float_t rw, rh;
cairo_status_t status;
- cairo_bool_t is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
- cairo_matrix_t m = spattern->base.matrix;
+ cairo_bool_t is_bounded;
+
+ cairo_matrix_t m;
/* SURFACE is the only type we'll handle here */
- assert (source->type == CAIRO_PATTERN_TYPE_SURFACE);
+ assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
+
+ spattern = (cairo_surface_pattern_t *) apattern;
+ pat_surf = spattern->surface;
- if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING)
+ if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
+ }
+ else
+ _cairo_surface_get_extents (&dest->base, &extents);
+ m = spattern->base.matrix;
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
&m, clip, &image);
-
if (unlikely (status))
return status;
- info = _cairo_calloc (sizeof (SurfacePatternDrawInfo));
+ info = _cairo_malloc (sizeof (SurfacePatternDrawInfo));
if (unlikely (!info))
- {
- CGImageRelease (image);
return CAIRO_STATUS_NO_MEMORY;
- }
+ /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
+ * that the data will stick around for this image when the printer gets to it.
+ * Otherwise, the underlying data store may disappear from under us!
+ *
+ * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
+ * since the Quartz surfaces have a higher chance of sticking around. If the
+ * source is a quartz image surface, then it's set up to retain a ref to the
+ * image surface that it's backed by.
+ */
info->image = image;
info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
info->do_reflect = FALSE;
@@ -958,18 +1014,13 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
pbounds.origin.x = 0;
pbounds.origin.y = 0;
- switch (spattern->base.extend) {
- case CAIRO_EXTEND_NONE:
- case CAIRO_EXTEND_PAD:
- case CAIRO_EXTEND_REPEAT:
- pbounds.size.width = extents.width;
- pbounds.size.height = extents.height;
- break;
- case CAIRO_EXTEND_REFLECT:
+ if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
pbounds.size.width = 2.0 * extents.width;
pbounds.size.height = 2.0 * extents.height;
info->do_reflect = TRUE;
- break;
+ } else {
+ pbounds.size.width = extents.width;
+ pbounds.size.height = extents.height;
}
rw = pbounds.size.width;
rh = pbounds.size.height;
@@ -982,12 +1033,12 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
* So we take the pattern matrix and the original context matrix,
* which gives us the correct base translation/y flip.
*/
- ptransform = CGAffineTransformConcat (stransform, surface->cgContextBaseCTM);
+ ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
#ifdef QUARTZ_DEBUG
ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
- CGAffineTransform xform = CGContextGetCTM (surface->cgContext);
+ CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
#endif
@@ -1019,10 +1070,10 @@ typedef struct {
/* Destination rect */
CGRect rect;
- /* Used with DO_SHADING, DO_IMAGE */
+ /* Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE, DO_LAYER */
CGAffineTransform transform;
- /* Used with DO_IMAGE */
+ /* Used with DO_IMAGE and DO_TILED_IMAGE */
CGImageRef image;
/* Used with DO_SHADING */
@@ -1031,130 +1082,15 @@ typedef struct {
/* Temporary destination for unbounded operations */
CGLayerRef layer;
CGRect clipRect;
-} cairo_quartz_drawing_state_t;
-
-static cairo_int_status_t
-_cairo_quartz_setup_pattern_source (cairo_quartz_drawing_state_t *state,
- const cairo_pattern_t *source,
- cairo_quartz_surface_t *surface,
- const cairo_clip_t *clip,
- cairo_operator_t op)
-{
- const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
- cairo_surface_t *pat_surf = spat->surface;
- cairo_matrix_t m = spat->base.matrix;
- cairo_format_t format = _cairo_format_from_content (surface->base.content);
- cairo_rectangle_int_t extents, pattern_extents;
- CGImageRef img;
-
- cairo_quartz_float_t patternAlpha = 1.0f;
- CGColorSpaceRef patternSpace;
- CGPatternRef cgpat = NULL;
- cairo_int_status_t status;
- CGColorRef black = _cairo_quartz_black (_cairo_quartz_create_color_space (state->cgDrawContext));
-
- _cairo_surface_get_extents (&surface->base, &extents);
-
- if (pat_surf->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) {
- cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
-
- if (quartz_surf->cgLayer && source->extend == CAIRO_EXTEND_NONE) {
- cairo_matrix_invert (&m);
- _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
- state->rect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
- state->layer = quartz_surf->cgLayer;
- state->action = DO_LAYER;
- return CAIRO_STATUS_SUCCESS;
- }
- }
-
- status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
- &m, clip, &img); // Note that only pat_surf will get used!
- if (unlikely (status))
- return status;
-
- state->image = img;
-
- if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
- m.x0 = -ceil (m.x0 - 0.5);
- m.y0 = -ceil (m.y0 - 0.5);
- } else {
- cairo_matrix_invert (&m);
- }
-
- _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
-
- if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
- cairo_bool_t is_bounded = _cairo_surface_get_extents (pat_surf, &pattern_extents);
- assert (is_bounded);
- } else {
- _cairo_surface_get_extents (&surface->base, &pattern_extents);
- }
-
- if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD) {
- int x, y;
-
- if (op == CAIRO_OPERATOR_SOURCE &&
- (pat_surf->content == CAIRO_CONTENT_ALPHA ||
- ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
- {
- state->layer = CGLayerCreateWithContext (surface->cgContext,
- state->clipRect.size,
- NULL);
- state->cgDrawContext = CGLayerGetContext (state->layer);
- CGContextTranslateCTM (state->cgDrawContext,
- -state->clipRect.origin.x,
- -state->clipRect.origin.y);
- }
-
- CGContextSetFillColorWithColor (state->cgDrawContext, black);
-
- state->rect = CGRectMake (0, 0, pattern_extents.width, pattern_extents.height);
- state->action = DO_IMAGE;
- return CAIRO_STATUS_SUCCESS;
- }
-
- if (source->extend == CAIRO_EXTEND_REPEAT)
- {
- CGAffineTransform xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
- state->transform);
- CGRect srcRect = CGRectMake (0, 0, extents.width, extents.height);
- srcRect = CGRectApplyAffineTransform (srcRect, xform);
- xform = CGAffineTransformInvert (xform);
- srcRect = CGRectApplyAffineTransform (srcRect, xform);
- state->rect = srcRect;
- }
-
- status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &cgpat);
- if (unlikely (status))
- return status;
-
- patternSpace = CGColorSpaceCreatePattern (NULL);
- /* To pass pthread-same-source. */
- if (source->extend == CAIRO_EXTEND_REPEAT)
- CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
- CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
- CGContextSetFillPattern (state->cgDrawContext, cgpat, &patternAlpha);
- CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
- CGContextSetStrokePattern (state->cgDrawContext, cgpat, &patternAlpha);
- CGColorSpaceRelease (patternSpace);
-
- /* Quartz likes to munge the pattern phase (as yet unexplained
- * why); force it to 0,0 as we've already baked in the correct
- * pattern translation into the pattern matrix
- */
- CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
-
- CGPatternRelease (cgpat);
- CGColorRelease (black);
-
- state->action = DO_DIRECT;
- return CAIRO_STATUS_SUCCESS;
-}
+ /* Source layer to be rendered when using DO_LAYER.
+ Unlike 'layer' above, this is not owned by the drawing state
+ but by the source surface. */
+ CGLayerRef sourceLayer;
+} cairo_quartz_drawing_state_t;
/*
-Quartz does not support repeating gradients. We handle repeating gradients
+Quartz does not support repeating radients. We handle repeating gradients
by manually extending the gradient and repeating color stops. We need to
minimize the number of repetitions since Quartz seems to sample our color
function across the entire range, even if part of that range is not needed
@@ -1186,7 +1122,7 @@ _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
if (unlikely (gradFunc == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
- rgb = _cairo_quartz_create_color_space (NULL);
+ rgb = CGColorSpaceCreateDeviceRGB ();
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
state->shading = CGShadingCreateAxial (rgb,
@@ -1225,13 +1161,13 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
const cairo_clip_t *clip = composite->clip;
cairo_bool_t needs_temp;
cairo_status_t status;
+ cairo_format_t format = _cairo_format_from_content (composite->surface->content);
state->layer = NULL;
state->image = NULL;
state->shading = NULL;
state->cgDrawContext = NULL;
state->cgMaskContext = NULL;
- state->layer = NULL;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
@@ -1257,11 +1193,9 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
state->filter = _cairo_quartz_filter_to_quartz (source->filter);
if (op == CAIRO_OPERATOR_CLEAR) {
- CGColorRef black = _cairo_quartz_black (_cairo_quartz_create_color_space (surface->cgContext));
- CGContextSetFillColorWithColor (state->cgDrawContext, black);
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
state->action = DO_DIRECT;
- CGColorRelease (black);
return CAIRO_STATUS_SUCCESS;
}
@@ -1298,17 +1232,18 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
- CGColorRef color = _cairo_quartz_create_cgcolor (_cairo_quartz_create_color_space (state->cgDrawContext),
- solid->color.red,
- solid->color.green,
- solid->color.blue,
- solid->color.alpha);
-
- CGContextSetStrokeColorWithColor (state->cgDrawContext, color);
- CGContextSetFillColorWithColor (state->cgDrawContext, color);
+ CGContextSetRGBStrokeColor (state->cgDrawContext,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+ CGContextSetRGBFillColor (state->cgDrawContext,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
- CGColorRelease (color);
state->action = DO_DIRECT;
return CAIRO_STATUS_SUCCESS;
}
@@ -1327,9 +1262,147 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
}
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE ||
+ source->extend == CAIRO_EXTEND_PAD ||
+ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+ {
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ cairo_surface_t *pat_surf = spat->surface;
+ CGImageRef img;
+ cairo_matrix_t m = spat->base.matrix;
+ cairo_rectangle_int_t extents;
+ CGAffineTransform xform;
+ CGRect srcRect;
+ cairo_fixed_t fw, fh;
+ cairo_bool_t is_bounded;
+
+ /* Draw nonrepeating CGLayer surface using DO_LAYER */
+ if (source->extend != CAIRO_EXTEND_REPEAT &&
+ cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
+ cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
+ if (quartz_surf->cgLayer) {
+ cairo_matrix_invert(&m);
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+ state->rect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
+ state->sourceLayer = quartz_surf->cgLayer;
+ state->action = DO_LAYER;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ _cairo_surface_get_extents (composite->surface, &extents);
+ status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
+ &m, clip, &img);
+ if (unlikely (status))
+ return status;
+
+ state->image = img;
+
+ if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
+ m.x0 = -ceil (m.x0 - 0.5);
+ m.y0 = -ceil (m.y0 - 0.5);
+ } else {
+ cairo_matrix_invert (&m);
+ }
+
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+ if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+ }
+
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+
+ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD) {
+ int x, y;
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ (pat_surf->content == CAIRO_CONTENT_ALPHA ||
+ ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
+ {
+ state->layer = CGLayerCreateWithContext (surface->cgContext,
+ state->clipRect.size,
+ NULL);
+ state->cgDrawContext = CGLayerGetContext (state->layer);
+ CGContextTranslateCTM (state->cgDrawContext,
+ -state->clipRect.origin.x,
+ -state->clipRect.origin.y);
+ }
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+ state->rect = srcRect;
+ state->action = DO_IMAGE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+ /* Quartz seems to tile images at pixel-aligned regions only -- this
+ * leads to seams if the image doesn't end up scaling to fill the
+ * space exactly. The CGPattern tiling approach doesn't have this
+ * problem. Check if we're going to fill up the space (within some
+ * epsilon), and if not, fall back to the CGPattern type.
+ */
- if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
- return _cairo_quartz_setup_pattern_source (state, source, surface, clip, op);
+ xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
+ state->transform);
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+ fw = _cairo_fixed_from_double (srcRect.size.width);
+ fh = _cairo_fixed_from_double (srcRect.size.height);
+
+ if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
+ (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
+ {
+ /* We're good to use DrawTiledImage, but ensure that
+ * the math works out */
+
+ srcRect.size.width = round (srcRect.size.width);
+ srcRect.size.height = round (srcRect.size.height);
+
+ xform = CGAffineTransformInvert (xform);
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+ state->rect = srcRect;
+ state->action = DO_TILED_IMAGE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_quartz_float_t patternAlpha = 1.0f;
+ CGColorSpaceRef patternSpace;
+ CGPatternRef pattern = NULL;
+ cairo_int_status_t status;
+
+ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
+ if (unlikely (status))
+ return status;
+
+ patternSpace = CGColorSpaceCreatePattern (NULL);
+ CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
+ CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
+ CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
+ CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
+ CGColorSpaceRelease (patternSpace);
+
+ /* Quartz likes to munge the pattern phase (as yet unexplained
+ * why); force it to 0,0 as we've already baked in the correct
+ * pattern translation into the pattern matrix
+ */
+ CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
+
+ CGPatternRelease (pattern);
+
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
+ }
return CAIRO_INT_STATUS_UNSUPPORTED;
}
@@ -1341,10 +1414,9 @@ _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
if (state->layer) {
- if (state->action != DO_LAYER)
- CGContextDrawLayerInRect (surface->cgContext,
- state->clipRect,
- state->layer);
+ CGContextDrawLayerInRect (surface->cgContext,
+ state->clipRect,
+ state->layer);
CGLayerRelease (state->layer);
}
@@ -1358,33 +1430,6 @@ _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
CGShadingRelease (state->shading);
}
-static inline void
-_cairo_quartz_draw_cgcontext (cairo_quartz_drawing_state_t *state,
- cairo_operator_t op)
-{
- CGColorSpaceRef cs = _cairo_quartz_create_color_space (state->cgDrawContext);
- CGColorRef transparent = _cairo_quartz_create_cgcolor (cs, 0.0, 0.0, 0.0, 0.0); //releases cs
-
- if (! (op == CAIRO_OPERATOR_SOURCE &&
- state->cgDrawContext == state->cgMaskContext))
- return;
-
- CGContextBeginPath (state->cgDrawContext);
- CGContextAddRect (state->cgDrawContext, state->rect);
-
- CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
- CGContextScaleCTM (state->cgDrawContext, 1, -1);
- CGContextConcatCTM (state->cgDrawContext,
- CGAffineTransformInvert (state->transform));
-
- CGContextAddRect (state->cgDrawContext, state->clipRect);
-
- CGContextSetFillColorWithColor (state->cgDrawContext, transparent);
- CGContextEOFillPath (state->cgDrawContext);
- CGColorRelease (transparent);
-}
-
-
static void
_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
cairo_operator_t op)
@@ -1407,18 +1452,34 @@ _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
CGContextScaleCTM (state->cgDrawContext, 1, -1);
- if (state->action == DO_IMAGE) {
+ if (state->action == DO_LAYER) {
+ /* Note that according to Apple docs it's completely legal to draw a CGLayer
+ * to any CGContext, even one it wasn't created for.
+ */
+ assert (state->sourceLayer);
+ CGContextDrawLayerAtPoint (state->cgDrawContext, state->rect.origin,
+ state->sourceLayer);
+ } else if (state->action == DO_IMAGE) {
CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
- _cairo_quartz_draw_cgcontext (state, op);
- return;
- }
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ state->cgDrawContext == state->cgMaskContext)
+ {
+ CGContextBeginPath (state->cgDrawContext);
+ CGContextAddRect (state->cgDrawContext, state->rect);
- if (state->action == DO_LAYER) {
- CGContextDrawLayerInRect (state->cgDrawContext, state->rect, state->layer);
- _cairo_quartz_draw_cgcontext (state, op);
- return;
+ CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+ CGContextScaleCTM (state->cgDrawContext, 1, -1);
+ CGContextConcatCTM (state->cgDrawContext,
+ CGAffineTransformInvert (state->transform));
+
+ CGContextAddRect (state->cgDrawContext, state->clipRect);
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
+ CGContextEOFillPath (state->cgDrawContext);
+ }
+ } else {
+ CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
}
- assert (FALSE); // Unreachable
}
static cairo_image_surface_t *
@@ -1426,25 +1487,26 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
- cairo_surface_t *return_surface = NULL;
unsigned int stride, bitinfo, bpp, color_comps;
CGColorSpaceRef colorspace;
- unsigned char *imageData;
+ void *imageData;
cairo_format_t format;
- if (_cairo_quartz_is_zero_surface (&surface->base))
+ if (surface->imageSurfaceEquiv)
+ return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
+
+ if (IS_EMPTY (surface))
return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
- return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
// let's hope they don't add YUV under us
- colorspace = _cairo_quartz_create_color_space (surface->cgContext);
+ colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
- CGColorSpaceRelease (colorspace);
/* XXX TODO: We can handle many more data formats by
* converting to pixman_format_t */
@@ -1461,32 +1523,34 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface,
{
format = CAIRO_FORMAT_RGB24;
}
- else if (bpp == 8 && color_comps == 0)
+ else if (bpp == 8 && color_comps == 1)
{
- format = CAIRO_FORMAT_A8;
+ format = CAIRO_FORMAT_A1;
}
else
{
- return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
imageData = CGBitmapContextGetData (surface->cgContext);
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
- imageData += extents->y * stride + extents->x * bpp / 8;
- return_surface = cairo_image_surface_create_for_data (imageData,
- format,
- extents->width,
- extents->height,
- stride);
-
- return (cairo_image_surface_t *) return_surface;
+ return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
+ format,
+ extents->width,
+ extents->height,
+ stride);
}
static cairo_int_status_t
_cairo_quartz_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ if (surface->imageSurfaceEquiv)
+ return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
+
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
@@ -1506,11 +1570,17 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
unsigned char *imageData;
cairo_image_surface_t *isurf;
- if (_cairo_quartz_is_zero_surface (&surface->base)) {
+ if (IS_EMPTY(surface)) {
*image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
return CAIRO_STATUS_SUCCESS;
}
+ if (surface->imageSurfaceEquiv) {
+ CGContextFlush(surface->cgContext);
+ *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
if (_cairo_quartz_is_cgcontext_bitmap_context(surface->cgContext)) {
unsigned int stride;
unsigned int bitinfo;
@@ -1596,7 +1666,7 @@ _cairo_quartz_surface_finish (void *abstract_surface)
ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
- if (_cairo_quartz_is_zero_surface (&surface->base))
+ if (IS_EMPTY (surface))
return CAIRO_STATUS_SUCCESS;
/* Restore our saved gstate that we use to reset clipping */
@@ -1607,10 +1677,19 @@ _cairo_quartz_surface_finish (void *abstract_surface)
surface->cgContext = NULL;
- if (surface->cgLayer)
- {
- CGLayerRelease (surface->cgLayer);
- surface->cgLayer = NULL;
+ if (surface->imageSurfaceEquiv) {
+ if (surface->ownsData)
+ _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
+ } else if (surface->imageData && surface->ownsData) {
+ free (surface->imageData);
+ }
+
+ surface->imageData = NULL;
+
+ if (surface->cgLayer) {
+ CGLayerRelease (surface->cgLayer);
}
return CAIRO_STATUS_SUCCESS;
@@ -1645,66 +1724,20 @@ _cairo_quartz_surface_release_source_image (void *abstract_surface,
_cairo_quartz_surface_unmap_image (abstract_surface, image);
}
-static cairo_surface_t*
-_cairo_quartz_surface_create_with_cglayer (cairo_quartz_surface_t *surface,
- cairo_content_t content,
- int width, int height)
-{
- CGAffineTransform xform;
- CGContextRef context;
- CGLayerRef layer;
- cairo_quartz_surface_t* new_surface;
-
- if (surface->cgContext == NULL || surface->cgLayer != NULL)
- return NULL;
-
- if (width <= 0 || height <= 0)
- return NULL;
-
- xform = CGContextGetUserSpaceToDeviceSpaceTransform (surface->cgContext);
- layer = CGLayerCreateWithContext (surface->cgContext,
- CGSizeMake (width * xform.a,
- height * xform.d),
- NULL);
-
- context = CGLayerGetContext (layer);
- CGContextTranslateCTM (context, 0.0, height);
- CGContextScaleCTM (context, xform.a, -xform.d);
- new_surface = _cairo_quartz_surface_create_internal (context, content,
- width, height);
- if (unlikely (new_surface->base.status))
- {
- CGContextRelease (context);
- CGLayerRelease (layer);
- return &new_surface->base;
- }
- new_surface->cgLayer = CGLayerRetain(layer);
- CGContextRetain(context);
- new_surface->virtual_extents = surface->virtual_extents;
-
- return &new_surface->base;
-}
-
static cairo_surface_t *
_cairo_quartz_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
- cairo_quartz_surface_t *surface, *similar_quartz;
+ cairo_quartz_surface_t *similar_quartz;
cairo_surface_t *similar;
cairo_format_t format;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
- // verify width and height of surface
- if (!_cairo_quartz_verify_surface_size (width, height)) {
- return _cairo_surface_create_in_error (_cairo_error
- (CAIRO_STATUS_INVALID_SIZE));
- }
-
- surface = (cairo_quartz_surface_t *) abstract_surface;
- if (surface->cgContext && !surface->cgLayer && !(width && height))
- _cairo_quartz_surface_create_with_cglayer (surface, content,
- width, height);
+ if (surface->cgLayer)
+ return cairo_quartz_surface_create_cg_layer (abstract_surface, content,
+ width, height);
if (content == CAIRO_CONTENT_COLOR_ALPHA)
format = CAIRO_FORMAT_ARGB32;
@@ -1715,10 +1748,17 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
else
return NULL;
+ // verify width and height of surface
+ if (!_cairo_quartz_verify_surface_size (width, height)) {
+ return _cairo_surface_create_in_error (_cairo_error
+ (CAIRO_STATUS_INVALID_SIZE));
+ }
+
similar = cairo_quartz_surface_create (format, width, height);
if (unlikely (similar->status))
return similar;
+ surface = (cairo_quartz_surface_t *) abstract_surface;
similar_quartz = (cairo_quartz_surface_t *) similar;
similar_quartz->virtual_extents = surface->virtual_extents;
@@ -1862,7 +1902,7 @@ _cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
mask_surf = extents->mask_pattern.surface.surface;
/* When an opaque surface used as a mask in Quartz, its
- * luminosity is used as the alpha value, so we can only use
+ * luminosity is used as the alpha value, so we con only use
* surfaces with alpha without creating a temporary mask. */
need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
}
@@ -2059,15 +2099,17 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
cairo_bool_t permit_subpixel_antialiasing)
{
CGAffineTransform textTransform, invTextTransform;
- CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)];
- CGPoint cg_positions_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)];
+ CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
+ CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
CGGlyph *cg_glyphs = &glyphs_static[0];
- CGPoint *cg_positions = &cg_positions_static[0];
+ CGSize *cg_advances = &cg_advances_static[0];
+ CGPoint *cg_positions;
+ 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;
- CGPoint origin;
- CTFontRef ctFont = NULL;
+ int i;
cairo_bool_t didForceFontSmoothing = FALSE;
cairo_antialias_t effective_antialiasing;
@@ -2086,26 +2128,54 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
}
- /* this doesn't addref */
- ctFont = _cairo_quartz_scaled_font_get_ct_font (scaled_font);
- _cairo_quartz_set_antialiasing (state.cgMaskContext, scaled_font->options.antialias);
+ if (!CTFontDrawGlyphsPtr) {
+ /* this doesn't addref */
+ CGFontRef cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+ CGContextSetFont (state.cgMaskContext, cgfref);
+ CGContextSetFontSize (state.cgMaskContext, 1.0);
+ }
effective_antialiasing = scaled_font->options.antialias;
if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL &&
!permit_subpixel_antialiasing) {
effective_antialiasing = CAIRO_ANTIALIAS_GRAY;
}
-
- _cairo_quartz_set_antialiasing (state.cgMaskContext, effective_antialiasing);
+ switch (effective_antialiasing) {
+ 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;
+ }
if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
- cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGPoint));
- if (unlikely (cg_glyphs == NULL)) {
+ cg_advances = _cairo_malloc_ab (num_glyphs,
+ sizeof (CGSize) + sizeof (CGGlyph));
+
+ if (unlikely (cg_advances == NULL)) {
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
- cg_positions = (CGPoint*) (cg_glyphs + num_glyphs);
+ cg_glyphs = (CGGlyph*) (cg_advances + num_glyphs);
}
/* scale(1,-1) * scaled_font->scale */
@@ -2122,35 +2192,72 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
-scaled_font->scale_inverse.yy,
0.0, 0.0);
+ /*
+ * CGContextShowGlyphsWithAdvances does not transform the advances
+ * by the text matrix, if the drawing mode is kCGTextClip. Instead
+ * of trying to recompute the advances, make sure that the text
+ * matrix is the identity and rely on the CTM for the text
+ * transform.
+ */
+ CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
+ CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.cgMaskContext, textTransform);
- origin = CGPointMake (glyphs[0].x, glyphs[0].y);
- for (int i = 0; i < num_glyphs; ++i)
- {
+ /* Convert glyph positions to glyph advances. */
+ cg_glyphs[0] = glyphs[0].index;
+ for (i = 1; i < num_glyphs; i++) {
+ CGSize advance = CGSizeMake (glyphs[i].x - glyphs[i-1].x,
+ glyphs[i].y - glyphs[i-1].y);
+ cg_advances[i] = CGSizeApplyAffineTransform (advance, invTextTransform);
cg_glyphs[i] = glyphs[i].index;
- 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, origin.x, origin.y);
- CGContextConcatCTM (state.cgMaskContext, textTransform);
+ if (CTFontDrawGlyphsPtr) {
+ /* If CTFontDrawGlyphs is available, we want to use that
+ * instead of the deprecated CGContextShowGlyphsWithAdvances
+ * so that colored-bitmap fonts like Apple Color Emoji will
+ * render properly. */
+
+ /* Accumulate the glyph advances into glyph positions,
+ * overwriting them. Start at (0,0) because the CTM already
+ * takes into account the position of the first glyph. */
+ CGPoint pos = CGPointMake (0, 0);
+ cg_positions = (CGPoint *) cg_advances;
+ cg_positions[0] = pos;
+ for (i = 1; i < num_glyphs; i++) {
+ pos.x += cg_advances[i].width;
+ pos.y += cg_advances[i].height;
+ cg_positions[i] = pos;
+ }
- CTFontDrawGlyphs (ctFont, cg_glyphs, cg_positions, num_glyphs, state.cgMaskContext);
+ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
+ cg_glyphs,
+ cg_positions,
+ num_glyphs,
+ state.cgMaskContext);
+ } else {
+ CGContextShowGlyphsWithAdvances (state.cgMaskContext,
+ cg_glyphs,
+ cg_advances + 1,
+ num_glyphs);
+ }
+ /* Revert the changes to the CTM. This fragment cannot rely on
+ * CG{Save,Restore}GState, as that would reset the clip. */
CGContextConcatCTM (state.cgMaskContext, invTextTransform);
- CGContextTranslateCTM (state.cgMaskContext, -origin.x, -origin.y);
+ CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
if (state.action != DO_DIRECT)
_cairo_quartz_draw_source (&state, extents->op);
BAIL:
if (didForceFontSmoothing)
- CGContextSetAllowsFontSmoothing (state.cgMaskContext, FALSE);
+ CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
_cairo_quartz_teardown_state (&state, extents);
- if (cg_glyphs != glyphs_static)
- free (cg_glyphs);
+ if (cg_advances != cg_advances_static)
+ free (cg_advances);
return rv;
}
@@ -2253,7 +2360,7 @@ _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
- if (_cairo_quartz_is_zero_surface (&surface->base))
+ if (IS_EMPTY (surface))
return CAIRO_STATUS_SUCCESS;
if (path == NULL) {
@@ -2425,7 +2532,7 @@ _cairo_quartz_surface_tag (void *abstract_surface,
// XXXtodo implement show_page; need to figure out how to handle begin/end
-static const cairo_surface_backend_t cairo_quartz_surface_backend = {
+static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
CAIRO_SURFACE_TYPE_QUARTZ,
_cairo_quartz_surface_finish,
@@ -2456,9 +2563,10 @@ static const cairo_surface_backend_t cairo_quartz_surface_backend = {
_cairo_quartz_surface_fill,
NULL, /* fill-stroke */
_cairo_quartz_surface_glyphs,
- NULL, /* has_show_text_glyphs */
- NULL, /* show_text_glyphs */
- NULL, /* get_supported_mime_types */
+
+ NULL, /* has_show_text_glyphs */
+ NULL, /* show_text_glyphs */
+ NULL, /* get_supported_mime_types */
_cairo_quartz_surface_tag /* tag */
};
@@ -2470,8 +2578,10 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
{
cairo_quartz_surface_t *surface;
+ quartz_ensure_symbols ();
+
/* Init the base surface */
- surface = _cairo_calloc (sizeof (cairo_quartz_surface_t));
+ surface = _cairo_malloc (sizeof (cairo_quartz_surface_t));
if (unlikely (surface == NULL))
return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
@@ -2491,8 +2601,11 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
surface->extents.width = width;
surface->extents.height = height;
surface->virtual_extents = surface->extents;
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
- if (_cairo_quartz_is_zero_surface (&surface->base)) {
+
+ if (IS_EMPTY (surface)) {
surface->cgContext = NULL;
surface->cgContextBaseCTM = CGAffineTransformIdentity;
surface->base.is_clear = TRUE;
@@ -2507,6 +2620,8 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
surface->cgContext = cgContext;
surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
+ surface->ownsData = TRUE;
+
return surface;
}
@@ -2542,29 +2657,97 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
unsigned int width,
unsigned int height)
{
- cairo_quartz_surface_t *surf =
- _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
- width, height);
+ cairo_quartz_surface_t *surf;
+
+ surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
if (likely (!surf->base.status))
CGContextRetain (cgContext);
return &surf->base;
}
+/**
+ * cairo_quartz_surface_create_cg_layer
+ * @surface: The returned surface can be efficiently drawn into this
+ * destination surface (if tiling is not used)."
+ * @content: the content type of the surface
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface backed by a CGLayer, if the given surface
+ * is a Quartz surface; the CGLayer is created to match the surface's
+ * Quartz context. Otherwise just calls cairo_surface_create_similar.
+ * The returned surface can be efficiently blitted to the given surface,
+ * but tiling and 'extend' modes other than NONE are not so efficient.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
+ cairo_content_t content,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_quartz_surface_t *surf;
+ CGLayerRef layer;
+ CGContextRef ctx;
+ CGContextRef cgContext;
+
+ cgContext = cairo_quartz_surface_get_cg_context (surface);
+ if (!cgContext)
+ return cairo_surface_create_similar (surface, content,
+ width, height);
+
+ if (!_cairo_quartz_verify_surface_size(width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ /* If we pass zero width or height into CGLayerCreateWithContext below,
+ * it will fail.
+ */
+ if (width == 0 || height == 0) {
+ return (cairo_surface_t*)
+ _cairo_quartz_surface_create_internal (NULL, content,
+ width, height);
+ }
+
+ layer = CGLayerCreateWithContext (cgContext,
+ CGSizeMake (width, height),
+ NULL);
+ if (!layer)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ ctx = CGLayerGetContext (layer);
+ /* Flip it when we draw into it, so that when we finally composite it
+ * to a flipped target, the directions match and Quartz will optimize
+ * the composition properly
+ */
+ CGContextTranslateCTM (ctx, 0, height);
+ CGContextScaleCTM (ctx, 1, -1);
+
+ CGContextRetain (ctx);
+ surf = _cairo_quartz_surface_create_internal (ctx, content,
+ width, height);
+ if (surf->base.status) {
+ CGLayerRelease (layer);
+ // create_internal will have set an error
+ return (cairo_surface_t*) surf;
+ }
+ surf->cgLayer = layer;
+
+ return (cairo_surface_t *) surf;
+}
+
/**
* cairo_quartz_surface_create:
* @format: format of pixels in the surface to create
* @width: width of the surface, in pixels
* @height: height of the surface, in pixels
*
- * Creates a Quartz surface backed by a CGBitmapContext using the main
- * display's colorspace to avoid an expensive colorspace transform
- * done serially on the CPU. This may produce slightly different
- * colors from what's intended. Programs for which color management is
- * important should create their own CGBitmapContext with a
- * device-independent color space; most will expect Cairo to draw in
- * sRGB and would use CGColorSpaceCreateWithName(kCGColorSpaceSRGB).
- *
+ * Creates a Quartz surface backed by a CGBitmap. The surface is
+ * created using the Device RGB (or Device Gray, for A8) color space.
* All Cairo operations, including those that require software
* rendering, will succeed on this surface.
*
@@ -2581,7 +2764,7 @@ cairo_quartz_surface_create (cairo_format_t format,
CGContextRef cgc;
CGColorSpaceRef cgColorspace;
CGBitmapInfo bitinfo;
- void *imageData = NULL;
+ void *imageData;
int stride;
int bitsPerComponent;
@@ -2596,7 +2779,7 @@ cairo_quartz_surface_create (cairo_format_t format,
if (format == CAIRO_FORMAT_ARGB32 ||
format == CAIRO_FORMAT_RGB24)
{
- cgColorspace = _cairo_quartz_create_color_space (NULL);
+ cgColorspace = CGColorSpaceCreateDeviceRGB ();
bitinfo = kCGBitmapByteOrder32Host;
if (format == CAIRO_FORMAT_ARGB32)
bitinfo |= kCGImageAlphaPremultipliedFirst;
@@ -2624,6 +2807,16 @@ cairo_quartz_surface_create (cairo_format_t format,
* so we don't have to anything special on allocation.
*/
stride = (stride + 15) & ~15;
+
+ 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 behaviour */
+ memset (imageData, 0, height * stride);
+
cgc = CGBitmapContextCreate (imageData,
width,
height,
@@ -2634,9 +2827,7 @@ cairo_quartz_surface_create (cairo_format_t format,
CGColorSpaceRelease (cgColorspace);
if (!cgc) {
- if (imageData)
- free (imageData);
-
+ free (imageData);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
@@ -2648,16 +2839,19 @@ cairo_quartz_surface_create (cairo_format_t format,
width, height);
if (surf->base.status) {
CGContextRelease (cgc);
-
- if (imageData)
- free (imageData);
-
+ free (imageData);
// create_internal will have set an error
return &surf->base;
}
surf->base.is_clear = TRUE;
+ surf->imageData = imageData;
+ surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
+
+ // We created this data, so we can delete it.
+ surf->ownsData = TRUE;
+
return &surf->base;
}
@@ -2685,14 +2879,8 @@ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
if (surface && _cairo_surface_is_quartz (surface)) {
cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
return quartz->cgContext;
- }
-
- if (surface && _cairo_surface_is_quartz_image (surface)) {
- cairo_quartz_image_surface_t *quartz = (cairo_quartz_image_surface_t *) surface;
- return quartz->cgContext;
- }
-
- return NULL;
+ } else
+ return NULL;
}
/**
@@ -2709,109 +2897,111 @@ _cairo_surface_is_quartz (const cairo_surface_t *surface)
return surface->backend == &cairo_quartz_surface_backend;
}
-cairo_surface_t*
-_cairo_quartz_snapshot_create (cairo_surface_t *surface)
+cairo_surface_t *
+cairo_quartz_surface_get_image (cairo_surface_t *surface)
{
- cairo_quartz_snapshot_t *snapshot = NULL;
- CGContextRef cgContext;
- if (!surface || ! _is_quartz_surface (surface) || _cairo_quartz_is_zero_surface (surface))
- return NULL;
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface;
+ cairo_image_surface_t *image;
- if (_cairo_surface_is_quartz (surface) &&
- ! _cairo_quartz_is_cgcontext_bitmap_context (((cairo_quartz_surface_t*)surface)->cgContext))
- return NULL;
+ if (_cairo_quartz_get_image(quartz, &image))
+ return NULL;
- snapshot = _cairo_calloc (sizeof (cairo_quartz_snapshot_t));
+ return (cairo_surface_t *)image;
+}
- if (unlikely (surface == NULL))
- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+/* Debug stuff */
- memset (snapshot, 0, sizeof (cairo_quartz_snapshot_t));
- cgContext = cairo_quartz_surface_get_cg_context (surface);
- _cairo_surface_init (&snapshot->base,
- &cairo_quartz_snapshot_backend,
- NULL, CAIRO_CONTENT_COLOR_ALPHA, FALSE);
- snapshot->image = CGBitmapContextCreateImage (cgContext);
- _cairo_surface_attach_snapshot (surface, &snapshot->base, NULL);
- cairo_surface_destroy (&snapshot->base); // The surface has reffed the snapshot so we must unref it here.
-
- return &snapshot->base;
-}
+#ifdef QUARTZ_DEBUG
+
+#include <Movies.h>
-cairo_status_t
-_cairo_quartz_snapshot_finish (void *surface)
+void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
{
- cairo_quartz_snapshot_t *snapshot = (cairo_quartz_snapshot_t *)surface;
- if (snapshot->image)
- CGImageRelease (snapshot->image);
- return CAIRO_STATUS_SUCCESS;
+ Handle dataRef = NULL;
+ OSType dataRefType;
+ CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
+
+ GraphicsExportComponent grex = 0;
+ unsigned long sizeWritten;
+
+ ComponentResult result;
+
+ // create the data reference
+ result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
+ 0, &dataRef, &dataRefType);
+
+ if (NULL != dataRef && noErr == result) {
+ // get the PNG exporter
+ result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
+ &grex);
+
+ if (grex) {
+ // tell the exporter where to find its source image
+ result = GraphicsExportSetInputCGImage (grex, inImageRef);
+
+ if (noErr == result) {
+ // tell the exporter where to save the exporter image
+ result = GraphicsExportSetOutputDataReference (grex, dataRef,
+ dataRefType);
+
+ if (noErr == result) {
+ // write the PNG file
+ result = GraphicsExportDoExport (grex, &sizeWritten);
+ }
+ }
+
+ // remember to close the component
+ CloseComponent (grex);
+ }
+
+ // remember to dispose of the data reference handle
+ DisposeHandle (dataRef);
+ }
}
-CGImageRef
-_cairo_quartz_surface_snapshot_get_image (cairo_surface_t *surface)
+void
+quartz_image_to_png (CGImageRef imgref, char *dest)
{
- cairo_surface_t *snapshot;
- assert (_is_quartz_surface (surface));
- snapshot =
- _cairo_surface_has_snapshot (surface, &cairo_quartz_snapshot_backend);
+ static int sctr = 0;
+ char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
- if (unlikely (!snapshot))
- {
- snapshot = _cairo_quartz_snapshot_create (surface);
- if (unlikely (!snapshot || cairo_surface_status (snapshot)))
- return NULL;
+ if (dest == NULL) {
+ fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
+ sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
+ sctr++;
+ dest = sptr;
}
- return CGImageRetain (((cairo_quartz_snapshot_t*)snapshot)->image);
+ ExportCGImageToPNGFile (imgref, dest);
}
void
-_cairo_quartz_image_to_png (CGImageRef image, const char *dest)
+quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
{
- CFStringRef png_utti = CFSTR("public.png");
- CFStringRef path;
- CFURLRef url;
- CGImageDestinationRef image_dest;
+ static int sctr = 0;
+ char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
- if (!dest)
+ if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
+ fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
return;
- path = CFStringCreateWithCString (NULL, dest, kCFStringEncodingUTF8);
- url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, FALSE);
- image_dest = CGImageDestinationCreateWithURL (url, png_utti, 1, NULL);
-
- CGImageDestinationAddImage (image_dest, image, NULL);
- CGImageDestinationFinalize (image_dest);
+ }
- CFRelease (url);
- CFRelease (path);
- }
+ if (dest == NULL) {
+ fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
+ sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
+ sctr++;
+ dest = sptr;
+ }
-cairo_status_t
-_cairo_quartz_surface_to_png (cairo_surface_t *abstract_surface, const char *dest)
-{
- CGImageRef image;
- cairo_quartz_surface_t *surface;
- if (!(_cairo_surface_is_quartz (abstract_surface) &&
- _cairo_quartz_is_cgcontext_bitmap_context (((cairo_quartz_surface_t*)abstract_surface)->cgContext))) {
- return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+ CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
+ if (imgref == NULL) {
+ fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
+ return;
}
- surface = (cairo_quartz_surface_t*)abstract_surface;
- image = CGBitmapContextCreateImage (surface->cgContext);
- _cairo_quartz_image_to_png (image, dest);
+ ExportCGImageToPNGFile (imgref, dest);
- CGImageRelease (image);
- return CAIRO_STATUS_SUCCESS;
+ CGImageRelease (imgref);
}
-cairo_surface_t *
-cairo_quartz_surface_get_image (cairo_surface_t *surface)
-{
- cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface;
- cairo_image_surface_t *image;
-
- if (_cairo_quartz_get_image(quartz, &image))
- return NULL;
-
- return (cairo_surface_t *)image;
-}
+#endif /* QUARTZ_DEBUG */
\ No newline at end of file
diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h
index 009e3e9fb..6280f3bcc 100644
--- a/src/cairo-quartz.h
+++ b/src/cairo-quartz.h
@@ -49,10 +49,6 @@
#include <ApplicationServices/ApplicationServices.h>
#endif
-#if CAIRO_HAS_IMAGE_IO
-#include <ImageIO/ImageIO.h>
-#endif
-
CAIRO_BEGIN_DECLS
cairo_public cairo_surface_t *
@@ -65,6 +61,12 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
unsigned int width,
unsigned int height);
+cairo_public cairo_surface_t *
+cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
+ cairo_content_t content,
+ unsigned int width,
+ unsigned int height);
+
cairo_public CGContextRef
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
@@ -95,4 +97,4 @@ CAIRO_END_DECLS
#endif /* CAIRO_HAS_QUARTZ_SURFACE */
-#endif /* CAIRO_QUARTZ_H */
+#endif /* CAIRO_QUARTZ_H */
\ No newline at end of file
--
2.53.0