1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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;
|