From 05073e24b6297c81a92ddd0b9b9b33b45790a46d Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 22 Apr 2026 13:00:22 -0700 Subject: [PATCH 20/29] Handle CAIRO_FORMAT_A8 in _cairo_surface_to_cgimage for masking operations --- src/cairo-quartz-surface.c | 96 +++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 1f9dcae9f..3c21511b9 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -684,6 +684,55 @@ 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) +{ + free (info); +} + static cairo_status_t _cairo_surface_to_cgimage (cairo_surface_t *source, cairo_rectangle_int_t *extents, @@ -742,13 +791,48 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, &image_extra); if (unlikely (status)) return status; - image_surface = - (cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base); - status = image_surface->base.status; - if (status) + + 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); + } + + /* 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); - else - acquired = TRUE; + 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; + } } *image_out = NULL; -- 2.53.0