summaryrefslogtreecommitdiffstats
path: root/gfx/cairo/26-quartz-surface-mask.patch
blob: dd5c71c07a6267f28d7b57b5153d1643f4898766 (plain)
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;