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
35static cairo_format_t
36gdk_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
50static cairo_surface_t *
51gdk_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
74static void
75convert_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
114static void
115convert_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 */
161GdkPixbuf *
162gdk_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
219static void
220pixbuf_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 */
238GdkPixbuf *
239gdk_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

source code of gtk/gdk/gdkpixbuf-drawable.c