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 "gdksurface.h"
28 #include "gdkinternals.h"
29 #include "gdktextureprivate.h"
30
31 #include <gdk-pixbuf/gdk-pixbuf.h>
32
33
34 static cairo_format_t
gdk_cairo_format_for_content(cairo_content_t content)35 gdk_cairo_format_for_content (cairo_content_t content)
36 {
37 switch (content)
38 {
39 case CAIRO_CONTENT_COLOR:
40 return CAIRO_FORMAT_RGB24;
41 case CAIRO_CONTENT_ALPHA:
42 return CAIRO_FORMAT_A8;
43 case CAIRO_CONTENT_COLOR_ALPHA:
44 default:
45 return CAIRO_FORMAT_ARGB32;
46 }
47 }
48
49 static cairo_surface_t *
gdk_cairo_surface_coerce_to_image(cairo_surface_t * surface,cairo_content_t content,int src_x,int src_y,int width,int height)50 gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface,
51 cairo_content_t content,
52 int src_x,
53 int src_y,
54 int width,
55 int height)
56 {
57 cairo_surface_t *copy;
58 cairo_t *cr;
59
60 copy = cairo_image_surface_create (gdk_cairo_format_for_content (content),
61 width,
62 height);
63
64 cr = cairo_create (copy);
65 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
66 cairo_set_source_surface (cr, surface, -src_x, -src_y);
67 cairo_paint (cr);
68 cairo_destroy (cr);
69
70 return copy;
71 }
72
73 static void
convert_alpha(guchar * dest_data,int dest_stride,guchar * src_data,int src_stride,int src_x,int src_y,int width,int height)74 convert_alpha (guchar *dest_data,
75 int dest_stride,
76 guchar *src_data,
77 int src_stride,
78 int src_x,
79 int src_y,
80 int width,
81 int height)
82 {
83 int x, y;
84
85 src_data += src_stride * src_y + src_x * 4;
86
87 for (y = 0; y < height; y++) {
88 guint32 *src = (guint32 *) src_data;
89
90 for (x = 0; x < width; x++) {
91 guint alpha = src[x] >> 24;
92
93 if (alpha == 0)
94 {
95 dest_data[x * 4 + 0] = 0;
96 dest_data[x * 4 + 1] = 0;
97 dest_data[x * 4 + 2] = 0;
98 }
99 else
100 {
101 dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
102 dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
103 dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
104 }
105 dest_data[x * 4 + 3] = alpha;
106 }
107
108 src_data += src_stride;
109 dest_data += dest_stride;
110 }
111 }
112
113 static void
convert_no_alpha(guchar * dest_data,int dest_stride,guchar * src_data,int src_stride,int src_x,int src_y,int width,int height)114 convert_no_alpha (guchar *dest_data,
115 int dest_stride,
116 guchar *src_data,
117 int src_stride,
118 int src_x,
119 int src_y,
120 int width,
121 int height)
122 {
123 int x, y;
124
125 src_data += src_stride * src_y + src_x * 4;
126
127 for (y = 0; y < height; y++) {
128 guint32 *src = (guint32 *) src_data;
129
130 for (x = 0; x < width; x++) {
131 dest_data[x * 3 + 0] = src[x] >> 16;
132 dest_data[x * 3 + 1] = src[x] >> 8;
133 dest_data[x * 3 + 2] = src[x];
134 }
135
136 src_data += src_stride;
137 dest_data += dest_stride;
138 }
139 }
140
141 /**
142 * gdk_pixbuf_get_from_surface:
143 * @surface: surface to copy from
144 * @src_x: Source X coordinate within @surface
145 * @src_y: Source Y coordinate within @surface
146 * @width: Width in pixels of region to get
147 * @height: Height in pixels of region to get
148 *
149 * Transfers image data from a `cairo_surface_t` and converts it
150 * to a `GdkPixbuf`.
151 *
152 * This allows you to efficiently read individual pixels from cairo surfaces.
153 *
154 * This function will create an RGB pixbuf with 8 bits per channel.
155 * The pixbuf will contain an alpha channel if the @surface contains one.
156 *
157 * Returns: (nullable) (transfer full): A newly-created pixbuf with a
158 * reference count of 1
159 */
160 GdkPixbuf *
gdk_pixbuf_get_from_surface(cairo_surface_t * surface,int src_x,int src_y,int width,int height)161 gdk_pixbuf_get_from_surface (cairo_surface_t *surface,
162 int src_x,
163 int src_y,
164 int width,
165 int height)
166 {
167 cairo_content_t content;
168 GdkPixbuf *dest;
169
170 /* General sanity checks */
171 g_return_val_if_fail (surface != NULL, NULL);
172 g_return_val_if_fail (width > 0 && height > 0, NULL);
173
174 content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
175 dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
176 !!(content & CAIRO_CONTENT_ALPHA),
177 8,
178 width, height);
179
180 if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE &&
181 cairo_image_surface_get_format (surface) == gdk_cairo_format_for_content (content))
182 surface = cairo_surface_reference (surface);
183 else
184 {
185 surface = gdk_cairo_surface_coerce_to_image (surface, content,
186 src_x, src_y,
187 width, height);
188 src_x = 0;
189 src_y = 0;
190 }
191 cairo_surface_flush (surface);
192 if (cairo_surface_status (surface) || dest == NULL)
193 {
194 cairo_surface_destroy (surface);
195 g_clear_object (&dest);
196 return NULL;
197 }
198
199 if (gdk_pixbuf_get_has_alpha (dest))
200 convert_alpha (gdk_pixbuf_get_pixels (dest),
201 gdk_pixbuf_get_rowstride (dest),
202 cairo_image_surface_get_data (surface),
203 cairo_image_surface_get_stride (surface),
204 src_x, src_y,
205 width, height);
206 else
207 convert_no_alpha (gdk_pixbuf_get_pixels (dest),
208 gdk_pixbuf_get_rowstride (dest),
209 cairo_image_surface_get_data (surface),
210 cairo_image_surface_get_stride (surface),
211 src_x, src_y,
212 width, height);
213
214 cairo_surface_destroy (surface);
215 return dest;
216 }
217
218 /**
219 * gdk_pixbuf_get_from_texture:
220 * @texture: a `GdkTexture`
221 *
222 * Creates a new pixbuf from @texture.
223 *
224 * This should generally not be used in newly written code as later
225 * stages will almost certainly convert the pixbuf back into a texture
226 * to draw it on screen.
227 *
228 * Returns: (transfer full) (nullable): a new `GdkPixbuf`
229 */
230 GdkPixbuf *
gdk_pixbuf_get_from_texture(GdkTexture * texture)231 gdk_pixbuf_get_from_texture (GdkTexture *texture)
232 {
233 GdkPixbuf *pixbuf;
234 cairo_surface_t *surface;
235 int width, height;
236
237 g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
238
239 width = gdk_texture_get_width (texture);
240 height = gdk_texture_get_height (texture);
241 surface = gdk_texture_download_surface (texture);
242 pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
243 cairo_surface_destroy (surface);
244
245 return pixbuf;
246 }
247