Bug 1900028 - Handle CAIRO_FORMAT_A8 in _cairo_surface_to_cgimage for masking operations. r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D212354
This commit is contained in:
Jonathan Kew 2024-06-02 11:40:59 +00:00
parent ef05d7cc23
commit 50d383bca2
2 changed files with 214 additions and 6 deletions

View file

@ -0,0 +1,124 @@
# HG changeset patch
# User Jonathan Kew <jkew@mozilla.com>
# 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;

View file

@ -684,6 +684,55 @@ CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
&gradient_callbacks); &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 static cairo_status_t
_cairo_surface_to_cgimage (cairo_surface_t *source, _cairo_surface_to_cgimage (cairo_surface_t *source,
cairo_rectangle_int_t *extents, cairo_rectangle_int_t *extents,
@ -742,13 +791,48 @@ _cairo_surface_to_cgimage (cairo_surface_t *source,
&image_extra); &image_extra);
if (unlikely (status)) if (unlikely (status))
return status; return status;
image_surface =
(cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base); if (surface->format == CAIRO_FORMAT_A8) {
status = image_surface->base.status; /* cairo_quartz_image_surface_create doesn't handle CAIRO_FORMAT_A8,
if (status) * 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); _cairo_surface_release_source_image (source, surface, image_extra);
else return status;
acquired = TRUE; } 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; *image_out = NULL;