# HG changeset patch # User Jonathan Kew # Date 1717237382 -3600 # Sat Jun 01 11:23:02 2024 +0100 # Node ID d5f7b9fd904e04406c56899c5cac9248b122ea35 # Parent c8d3e447c892474e061c9ffd22ec1823f06ecffa Bug 1900028 - Handle CAIRO_FORMAT_A8 in _cairo_surface_to_cgimage for masking operations. Differential Revision: https://phabricator.services.mozilla.com/D212354 diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -684,6 +684,55 @@ CairoQuartzCreateGradientFunction (const &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 @@ static cairo_status_t &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;