]> Pileus Git - ~andy/gtk/blob - gdk/gdkpixbuf-drawable.c
Merge branch 'master' into broadway
[~andy/gtk] / gdk / gdkpixbuf-drawable.c
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, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "config.h"
26
27 #include "gdkpixbuf.h"
28
29 #include "gdkcolor.h"
30 #include "gdkwindow.h"
31 #include "gdkinternals.h"
32
33 #include <gdk-pixbuf/gdk-pixbuf.h>
34
35 /**
36  * SECTION:pixbufs
37  * @Short_description: Functions for rendering pixbufs on drawables
38  * @Title: Pixbufs
39  *
40  * These functions allow to render pixbufs on drawables. Pixbufs are
41  * client-side images. For details on how to create and manipulate
42  * pixbufs, see the #GdkPixbuf API documentation.
43  */
44
45
46 /**
47  * gdk_pixbuf_get_from_window:
48  * @window: Source window
49  * @src_x: Source X coordinate within @window
50  * @src_y: Source Y coordinate within @window
51  * @width: Width in pixels of region to get
52  * @height: Height in pixels of region to get
53  *
54  * Transfers image data from a #GdkWindow and converts it to an RGB(A)
55  * representation inside a #GdkPixbuf. In other words, copies
56  * image data from a server-side drawable to a client-side RGB(A) buffer.
57  * This allows you to efficiently read individual pixels on the client side.
58  * 
59  * This function will create an RGB pixbuf with 8 bits per channel with
60  * the same size specified by the @width and @height arguments. The pixbuf
61  * will contain an alpha channel if the @window contains one.
62  *
63  * If the window is off the screen, then there is no image data in the
64  * obscured/offscreen regions to be placed in the pixbuf. The contents of
65  * portions of the pixbuf corresponding to the offscreen region are undefined.
66  *
67  * If the window you're obtaining data from is partially obscured by
68  * other windows, then the contents of the pixbuf areas corresponding
69  * to the obscured regions are undefined.
70  *
71  * If the window is not mapped (typically because it's iconified/minimized
72  * or not on the current workspace), then %NULL will be returned.
73  *
74  * If memory can't be allocated for the return value, %NULL will be returned
75  * instead.
76  *
77  * (In short, there are several ways this function can fail, and if it fails
78  *  it returns %NULL; so check the return value.)
79  *
80  * Return value: (transfer full): A newly-created pixbuf with a reference
81  * count of 1, or %NULL on error
82  **/
83 GdkPixbuf *
84 gdk_pixbuf_get_from_window (GdkWindow   *src,
85                             int src_x,  int src_y,
86                             int width,  int height)
87 {
88   cairo_surface_t *surface;
89   GdkPixbuf *dest;
90   
91   g_return_val_if_fail (GDK_IS_WINDOW (src), NULL);
92   g_return_val_if_fail (gdk_window_is_viewable (src), NULL);
93
94   surface = _gdk_window_ref_cairo_surface (src);
95   dest = gdk_pixbuf_get_from_surface (surface,
96                                       src_x, src_y,
97                                       width, height);
98   cairo_surface_destroy (surface);
99
100   return dest;
101 }
102         
103 static cairo_format_t
104 gdk_cairo_format_for_content (cairo_content_t content)
105 {
106   switch (content)
107     {
108     case CAIRO_CONTENT_COLOR:
109       return CAIRO_FORMAT_RGB24;
110     case CAIRO_CONTENT_ALPHA:
111       return CAIRO_FORMAT_A8;
112     case CAIRO_CONTENT_COLOR_ALPHA:
113     default:
114       return CAIRO_FORMAT_ARGB32;
115     }
116 }
117
118 static cairo_surface_t *
119 gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface,
120                                    cairo_content_t content,
121                                    int src_x,
122                                    int src_y,
123                                    int width,
124                                    int height)
125 {
126   cairo_surface_t *copy;
127   cairo_t *cr;
128
129   copy = cairo_image_surface_create (gdk_cairo_format_for_content (content),
130                                      width,
131                                      height);
132
133   cr = cairo_create (copy);
134   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
135   cairo_set_source_surface (cr, surface, -src_x, -src_y);
136   cairo_paint (cr);
137   cairo_destroy (cr);
138
139   return copy;
140 }
141
142 static void
143 convert_alpha (guchar  *dest_data,
144                int      dest_stride,
145                guchar  *src_data,
146                int      src_stride,
147                int      src_x,
148                int      src_y,
149                int      width,
150                int      height)
151 {
152   int x, y;
153
154   src_data += src_stride * src_y + src_x * 4;
155
156   for (y = 0; y < height; y++) {
157     guint32 *src = (guint32 *) src_data;
158
159     for (x = 0; x < width; x++) {
160       guint alpha = src[x] >> 24;
161
162       if (alpha == 0)
163         {
164           dest_data[x * 4 + 0] = 0;
165           dest_data[x * 4 + 1] = 0;
166           dest_data[x * 4 + 2] = 0;
167         }
168       else
169         {
170           dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
171           dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
172           dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
173         }
174       dest_data[x * 4 + 3] = alpha;
175     }
176
177     src_data += src_stride;
178     dest_data += dest_stride;
179   }
180 }
181
182 static void
183 convert_no_alpha (guchar  *dest_data,
184                   int      dest_stride,
185                   guchar  *src_data,
186                   int      src_stride,
187                   int      src_x,
188                   int      src_y,
189                   int      width,
190                   int      height)
191 {
192   int x, y;
193
194   src_data += src_stride * src_y + src_x * 4;
195
196   for (y = 0; y < height; y++) {
197     guint32 *src = (guint32 *) src_data;
198
199     for (x = 0; x < width; x++) {
200       dest_data[x * 3 + 0] = src[x] >> 16;
201       dest_data[x * 3 + 1] = src[x] >>  8;
202       dest_data[x * 3 + 2] = src[x];
203     }
204
205     src_data += src_stride;
206     dest_data += dest_stride;
207   }
208 }
209
210 /**
211  * gdk_pixbuf_get_from_surface:
212  * @surface: surface to copy from
213  * @src_x: Source X coordinate within @surface
214  * @src_y: Source Y coordinate within @surface
215  * @width: Width in pixels of region to get
216  * @height: Height in pixels of region to get
217  *
218  * Transfers image data from a #cairo_surface_t and converts it to an RGB(A)
219  * representation inside a #GdkPixbuf. This allows you to efficiently read
220  * individual pixels from cairo surfaces. For #GdkWindows, use
221  * gdk_pixbuf_get_from_window() instead.
222  *
223  * This function will create an RGB pixbuf with 8 bits per channel. The pixbuf
224  * will contain an alpha channel if the @surface contains one.
225  *
226  * Return value: (transfer full): A newly-created pixbuf with a reference count
227  * of 1, or %NULL on error
228  **/
229 GdkPixbuf *
230 gdk_pixbuf_get_from_surface  (cairo_surface_t *surface,
231                               int              src_x,
232                               int              src_y,
233                               int              width,
234                               int              height)
235 {
236   cairo_content_t content;
237   GdkPixbuf *dest;
238   
239   /* General sanity checks */
240   g_return_val_if_fail (surface != NULL, NULL);
241   g_return_val_if_fail (width > 0 && height > 0, NULL);
242
243   content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
244   dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
245                          !!(content & CAIRO_CONTENT_ALPHA),
246                          8,
247                          width, height);
248
249   surface = gdk_cairo_surface_coerce_to_image (surface, content, src_x, src_y,
250                                                width, height);
251   cairo_surface_flush (surface);
252   if (cairo_surface_status (surface) || dest == NULL)
253     {
254       cairo_surface_destroy (surface);
255       return NULL;
256     }
257
258   if (gdk_pixbuf_get_has_alpha (dest))
259     convert_alpha (gdk_pixbuf_get_pixels (dest),
260                    gdk_pixbuf_get_rowstride (dest),
261                    cairo_image_surface_get_data (surface),
262                    cairo_image_surface_get_stride (surface),
263                    0, 0,
264                    width, height);
265   else
266     convert_no_alpha (gdk_pixbuf_get_pixels (dest),
267                       gdk_pixbuf_get_rowstride (dest),
268                       cairo_image_surface_get_data (surface),
269                       cairo_image_surface_get_stride (surface),
270                       0, 0,
271                       width, height);
272
273   cairo_surface_destroy (surface);
274   return dest;
275 }
276