1 | /* GdkPixbuf library - convert X drawable information to RGB |
2 | * |
3 | * Copyright (C) 1999 Michael Zucchi |
4 | * |
5 | * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au> |
6 | * Cody Russell <bratsche@dfw.net> |
7 | * Federico Mena-Quintero <federico@gimp.org> |
8 | * |
9 | * This library is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Lesser General Public |
11 | * License as published by the Free Software Foundation; either |
12 | * version 2 of the License, or (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * Lesser General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Lesser General Public |
20 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
21 | */ |
22 | |
23 | #include "config.h" |
24 | |
25 | #include "gdkpixbuf.h" |
26 | |
27 | #include "gdkmemoryformatprivate.h" |
28 | #include "gdkmemorytextureprivate.h" |
29 | #include "gdksurface.h" |
30 | #include "gdktextureprivate.h" |
31 | |
32 | #include <gdk-pixbuf/gdk-pixbuf.h> |
33 | |
34 | |
35 | static cairo_format_t |
36 | gdk_cairo_format_for_content (cairo_content_t content) |
37 | { |
38 | switch (content) |
39 | { |
40 | case CAIRO_CONTENT_COLOR: |
41 | return CAIRO_FORMAT_RGB24; |
42 | case CAIRO_CONTENT_ALPHA: |
43 | return CAIRO_FORMAT_A8; |
44 | case CAIRO_CONTENT_COLOR_ALPHA: |
45 | default: |
46 | return CAIRO_FORMAT_ARGB32; |
47 | } |
48 | } |
49 | |
50 | static cairo_surface_t * |
51 | gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface, |
52 | cairo_content_t content, |
53 | int src_x, |
54 | int src_y, |
55 | int width, |
56 | int height) |
57 | { |
58 | cairo_surface_t *copy; |
59 | cairo_t *cr; |
60 | |
61 | copy = cairo_image_surface_create (format: gdk_cairo_format_for_content (content), |
62 | width, |
63 | height); |
64 | |
65 | cr = cairo_create (target: copy); |
66 | cairo_set_operator (cr, op: CAIRO_OPERATOR_SOURCE); |
67 | cairo_set_source_surface (cr, surface, x: -src_x, y: -src_y); |
68 | cairo_paint (cr); |
69 | cairo_destroy (cr); |
70 | |
71 | return copy; |
72 | } |
73 | |
74 | static void |
75 | convert_alpha (guchar *dest_data, |
76 | int dest_stride, |
77 | guchar *src_data, |
78 | int src_stride, |
79 | int src_x, |
80 | int src_y, |
81 | int width, |
82 | int height) |
83 | { |
84 | int x, y; |
85 | |
86 | src_data += src_stride * src_y + src_x * 4; |
87 | |
88 | for (y = 0; y < height; y++) { |
89 | guint32 *src = (guint32 *) src_data; |
90 | |
91 | for (x = 0; x < width; x++) { |
92 | guint alpha = src[x] >> 24; |
93 | |
94 | if (alpha == 0) |
95 | { |
96 | dest_data[x * 4 + 0] = 0; |
97 | dest_data[x * 4 + 1] = 0; |
98 | dest_data[x * 4 + 2] = 0; |
99 | } |
100 | else |
101 | { |
102 | dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; |
103 | dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; |
104 | dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; |
105 | } |
106 | dest_data[x * 4 + 3] = alpha; |
107 | } |
108 | |
109 | src_data += src_stride; |
110 | dest_data += dest_stride; |
111 | } |
112 | } |
113 | |
114 | static void |
115 | convert_no_alpha (guchar *dest_data, |
116 | int dest_stride, |
117 | guchar *src_data, |
118 | int src_stride, |
119 | int src_x, |
120 | int src_y, |
121 | int width, |
122 | int height) |
123 | { |
124 | int x, y; |
125 | |
126 | src_data += src_stride * src_y + src_x * 4; |
127 | |
128 | for (y = 0; y < height; y++) { |
129 | guint32 *src = (guint32 *) src_data; |
130 | |
131 | for (x = 0; x < width; x++) { |
132 | dest_data[x * 3 + 0] = src[x] >> 16; |
133 | dest_data[x * 3 + 1] = src[x] >> 8; |
134 | dest_data[x * 3 + 2] = src[x]; |
135 | } |
136 | |
137 | src_data += src_stride; |
138 | dest_data += dest_stride; |
139 | } |
140 | } |
141 | |
142 | /** |
143 | * gdk_pixbuf_get_from_surface: |
144 | * @surface: surface to copy from |
145 | * @src_x: Source X coordinate within @surface |
146 | * @src_y: Source Y coordinate within @surface |
147 | * @width: Width in pixels of region to get |
148 | * @height: Height in pixels of region to get |
149 | * |
150 | * Transfers image data from a `cairo_surface_t` and converts it |
151 | * to a `GdkPixbuf`. |
152 | * |
153 | * This allows you to efficiently read individual pixels from cairo surfaces. |
154 | * |
155 | * This function will create an RGB pixbuf with 8 bits per channel. |
156 | * The pixbuf will contain an alpha channel if the @surface contains one. |
157 | * |
158 | * Returns: (nullable) (transfer full): A newly-created pixbuf with a |
159 | * reference count of 1 |
160 | */ |
161 | GdkPixbuf * |
162 | gdk_pixbuf_get_from_surface (cairo_surface_t *surface, |
163 | int src_x, |
164 | int src_y, |
165 | int width, |
166 | int height) |
167 | { |
168 | cairo_content_t content; |
169 | GdkPixbuf *dest; |
170 | |
171 | /* General sanity checks */ |
172 | g_return_val_if_fail (surface != NULL, NULL); |
173 | g_return_val_if_fail (width > 0 && height > 0, NULL); |
174 | |
175 | content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR; |
176 | dest = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, |
177 | has_alpha: !!(content & CAIRO_CONTENT_ALPHA), |
178 | bits_per_sample: 8, |
179 | width, height); |
180 | |
181 | if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE && |
182 | cairo_image_surface_get_format (surface) == gdk_cairo_format_for_content (content)) |
183 | surface = cairo_surface_reference (surface); |
184 | else |
185 | { |
186 | surface = gdk_cairo_surface_coerce_to_image (surface, content, |
187 | src_x, src_y, |
188 | width, height); |
189 | src_x = 0; |
190 | src_y = 0; |
191 | } |
192 | cairo_surface_flush (surface); |
193 | if (cairo_surface_status (surface) || dest == NULL) |
194 | { |
195 | cairo_surface_destroy (surface); |
196 | g_clear_object (&dest); |
197 | return NULL; |
198 | } |
199 | |
200 | if (gdk_pixbuf_get_has_alpha (pixbuf: dest)) |
201 | convert_alpha (dest_data: gdk_pixbuf_get_pixels (pixbuf: dest), |
202 | dest_stride: gdk_pixbuf_get_rowstride (pixbuf: dest), |
203 | src_data: cairo_image_surface_get_data (surface), |
204 | src_stride: cairo_image_surface_get_stride (surface), |
205 | src_x, src_y, |
206 | width, height); |
207 | else |
208 | convert_no_alpha (dest_data: gdk_pixbuf_get_pixels (pixbuf: dest), |
209 | dest_stride: gdk_pixbuf_get_rowstride (pixbuf: dest), |
210 | src_data: cairo_image_surface_get_data (surface), |
211 | src_stride: cairo_image_surface_get_stride (surface), |
212 | src_x, src_y, |
213 | width, height); |
214 | |
215 | cairo_surface_destroy (surface); |
216 | return dest; |
217 | } |
218 | |
219 | static void |
220 | pixbuf_texture_unref_cb (guchar *pixels, |
221 | gpointer texture) |
222 | { |
223 | g_object_unref (object: texture); |
224 | } |
225 | |
226 | /** |
227 | * gdk_pixbuf_get_from_texture: |
228 | * @texture: a `GdkTexture` |
229 | * |
230 | * Creates a new pixbuf from @texture. |
231 | * |
232 | * This should generally not be used in newly written code as later |
233 | * stages will almost certainly convert the pixbuf back into a texture |
234 | * to draw it on screen. |
235 | * |
236 | * Returns: (transfer full) (nullable): a new `GdkPixbuf` |
237 | */ |
238 | GdkPixbuf * |
239 | gdk_pixbuf_get_from_texture (GdkTexture *texture) |
240 | { |
241 | GdkMemoryTexture *memtex; |
242 | gboolean alpha; |
243 | |
244 | alpha = gdk_memory_format_alpha (format: gdk_texture_get_format (self: texture)) != GDK_MEMORY_ALPHA_OPAQUE; |
245 | |
246 | memtex = gdk_memory_texture_from_texture (texture, |
247 | format: alpha ? GDK_MEMORY_GDK_PIXBUF_ALPHA |
248 | : GDK_MEMORY_GDK_PIXBUF_OPAQUE); |
249 | |
250 | return gdk_pixbuf_new_from_data (data: gdk_memory_texture_get_data (self: memtex), |
251 | colorspace: GDK_COLORSPACE_RGB, |
252 | has_alpha: alpha, |
253 | bits_per_sample: 8, |
254 | width: gdk_texture_get_width (GDK_TEXTURE (memtex)), |
255 | height: gdk_texture_get_height (GDK_TEXTURE (memtex)), |
256 | rowstride: gdk_memory_texture_get_stride (self: memtex), |
257 | destroy_fn: pixbuf_texture_unref_cb, |
258 | destroy_fn_data: memtex); |
259 | } |
260 | |