]> Pileus Git - ~andy/gtk/blob - gdk/gdkcairo.c
Merge branch 'master' into treeview-refactor
[~andy/gtk] / gdk / gdkcairo.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 2005 Red Hat, Inc. 
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gdkcairo.h"
23
24 #include "gdkinternals.h"
25
26 #include <math.h>
27
28 /**
29  * SECTION:cairo_interaction
30  * @Short_description: Functions to support using Cairo
31  * @Title: Cairo Interaction
32  *
33  * <link href="http://cairographics.org">Cairo</link> is a graphics
34  * library that supports vector graphics and image compositing that
35  * can be used with GDK. GTK+ does all of its drawing using Cairo.
36  *
37  * GDK does not wrap the Cairo API, instead it allows to create Cairo
38  * contexts which can be used to draw on #GdkWindows. Additional
39  * functions allow use #GdkRectangles with cairo and to use #GdkColors,
40  * #GdkPixbufs and #GdkWindows as sources for drawing operations.
41  */
42
43
44 /**
45  * gdk_cairo_get_clip_rectangle:
46  * @cr: a cairo context
47  * @rect: (out) (allow-none): return location for the clip, or %NULL
48  *
49  * This is a convenience function around cairo_clip_extents(). It rounds
50  * the clip extents to integer coordinates and returns a boolean
51  * indicating if a clip area exists.
52  *
53  * Returns: %TRUE if a clip rectangle exists, %FALSE if all of @cr is
54  * clipped and all drawing can be skipped.
55  **/
56 gboolean
57 gdk_cairo_get_clip_rectangle (cairo_t      *cr,
58                               GdkRectangle *rect)
59 {
60   double x1, y1, x2, y2;
61   gboolean clip_exists;
62
63   cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
64
65   clip_exists = x1 < x2 && y1 < y2;
66
67   if (rect)
68     {
69       x1 = floor (x1);
70       y1 = floor (y1);
71       x2 = ceil (x2);
72       y2 = ceil (y2);
73
74       rect->x      = CLAMP (x1,      G_MININT, G_MAXINT);
75       rect->y      = CLAMP (y1,      G_MININT, G_MAXINT);
76       rect->width  = CLAMP (x2 - x1, G_MININT, G_MAXINT);
77       rect->height = CLAMP (y2 - y1, G_MININT, G_MAXINT);
78     }
79
80   return clip_exists;
81 }
82
83 /**
84  * gdk_cairo_set_source_color:
85  * @cr: a #cairo_t
86  * @color: a #GdkColor
87  *
88  * Sets the specified #GdkColor as the source color of @cr.
89  *
90  * Since: 2.8
91  **/
92 void
93 gdk_cairo_set_source_color (cairo_t        *cr,
94                             const GdkColor *color)
95 {
96   g_return_if_fail (cr != NULL);
97   g_return_if_fail (color != NULL);
98     
99   cairo_set_source_rgb (cr,
100                         color->red / 65535.,
101                         color->green / 65535.,
102                         color->blue / 65535.);
103 }
104
105 /**
106  * gdk_cairo_set_source_rgba:
107  * @cr: a #cairo_t
108  * @rgba: a #GdkRGBA
109  *
110  * Sets the specified #GdkRGBA as the source color of @cr.
111  *
112  * Since: 3.0
113  **/
114 void
115 gdk_cairo_set_source_rgba (cairo_t       *cr,
116                            const GdkRGBA *rgba)
117 {
118   g_return_if_fail (cr != NULL);
119   g_return_if_fail (rgba != NULL);
120
121   cairo_set_source_rgba (cr,
122                          rgba->red,
123                          rgba->green,
124                          rgba->blue,
125                          rgba->alpha);
126 }
127
128 /**
129  * gdk_cairo_rectangle:
130  * @cr: a #cairo_t
131  * @rectangle: a #GdkRectangle
132  * 
133  * Adds the given rectangle to the current path of @cr.
134  *
135  * Since: 2.8
136  **/
137 void
138 gdk_cairo_rectangle (cairo_t            *cr,
139                      const GdkRectangle *rectangle)
140 {
141   g_return_if_fail (cr != NULL);
142   g_return_if_fail (rectangle != NULL);
143
144   cairo_rectangle (cr,
145                    rectangle->x,     rectangle->y,
146                    rectangle->width, rectangle->height);
147 }
148
149 /**
150  * gdk_cairo_region:
151  * @cr: a #cairo_t
152  * @region: a #cairo_region_t
153  * 
154  * Adds the given region to the current path of @cr.
155  *
156  * Since: 2.8
157  **/
158 void
159 gdk_cairo_region (cairo_t         *cr,
160                   const cairo_region_t *region)
161 {
162   cairo_rectangle_int_t box;
163   gint n_boxes, i;
164
165   g_return_if_fail (cr != NULL);
166   g_return_if_fail (region != NULL);
167
168   n_boxes = cairo_region_num_rectangles (region);
169
170   for (i = 0; i < n_boxes; i++)
171     {
172       cairo_region_get_rectangle (region, i, &box);
173       cairo_rectangle (cr, box.x, box.y, box.width, box.height);
174     }
175 }
176
177 /**
178  * gdk_cairo_set_source_pixbuf:
179  * @cr: a #Cairo context
180  * @pixbuf: a #GdkPixbuf
181  * @pixbuf_x: X coordinate of location to place upper left corner of @pixbuf
182  * @pixbuf_y: Y coordinate of location to place upper left corner of @pixbuf
183  * 
184  * Sets the given pixbuf as the source pattern for the Cairo context.
185  * The pattern has an extend mode of %CAIRO_EXTEND_NONE and is aligned
186  * so that the origin of @pixbuf is @pixbuf_x, @pixbuf_y
187  *
188  * Since: 2.8
189  **/
190 void
191 gdk_cairo_set_source_pixbuf (cairo_t         *cr,
192                              const GdkPixbuf *pixbuf,
193                              double           pixbuf_x,
194                              double           pixbuf_y)
195 {
196   gint width = gdk_pixbuf_get_width (pixbuf);
197   gint height = gdk_pixbuf_get_height (pixbuf);
198   guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
199   int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
200   int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
201   int cairo_stride;
202   guchar *cairo_pixels;
203   cairo_format_t format;
204   cairo_surface_t *surface;
205   static const cairo_user_data_key_t key;
206   int j;
207
208   if (n_channels == 3)
209     format = CAIRO_FORMAT_RGB24;
210   else
211     format = CAIRO_FORMAT_ARGB32;
212
213   cairo_stride = cairo_format_stride_for_width (format, width);
214   cairo_pixels = g_malloc (height * cairo_stride);
215   surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels,
216                                                  format,
217                                                  width, height, cairo_stride);
218
219   cairo_surface_set_user_data (surface, &key,
220                                cairo_pixels, (cairo_destroy_func_t)g_free);
221
222   for (j = height; j; j--)
223     {
224       guchar *p = gdk_pixels;
225       guchar *q = cairo_pixels;
226
227       if (n_channels == 3)
228         {
229           guchar *end = p + 3 * width;
230           
231           while (p < end)
232             {
233 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
234               q[0] = p[2];
235               q[1] = p[1];
236               q[2] = p[0];
237 #else     
238               q[1] = p[0];
239               q[2] = p[1];
240               q[3] = p[2];
241 #endif
242               p += 3;
243               q += 4;
244             }
245         }
246       else
247         {
248           guchar *end = p + 4 * width;
249           guint t1,t2,t3;
250             
251 #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
252
253           while (p < end)
254             {
255 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
256               MULT(q[0], p[2], p[3], t1);
257               MULT(q[1], p[1], p[3], t2);
258               MULT(q[2], p[0], p[3], t3);
259               q[3] = p[3];
260 #else     
261               q[0] = p[3];
262               MULT(q[1], p[0], p[3], t1);
263               MULT(q[2], p[1], p[3], t2);
264               MULT(q[3], p[2], p[3], t3);
265 #endif
266               
267               p += 4;
268               q += 4;
269             }
270           
271 #undef MULT
272         }
273
274       gdk_pixels += gdk_rowstride;
275       cairo_pixels += cairo_stride;
276     }
277
278   cairo_set_source_surface (cr, surface, pixbuf_x, pixbuf_y);
279   cairo_surface_destroy (surface);
280 }
281
282 /**
283  * gdk_cairo_set_source_window:
284  * @cr: a #Cairo context
285  * @window: a #GdkWindow
286  * @x: X coordinate of location to place upper left corner of @window
287  * @y: Y coordinate of location to place upper left corner of @window
288  *
289  * Sets the given window as the source pattern for the Cairo context.
290  * The pattern has an extend mode of %CAIRO_EXTEND_NONE and is aligned
291  * so that the origin of @window is @x, @y. The window contains all its
292  * subwindows when rendering.
293  *
294  * Note that the contents of @window are undefined outside of the
295  * visible part of @window, so use this function with care.
296  *
297  * Since: 2.24
298  */
299 void
300 gdk_cairo_set_source_window (cairo_t   *cr,
301                              GdkWindow *window,
302                              double     x,
303                              double     y)
304 {
305   cairo_surface_t *surface;
306   
307   g_return_if_fail (cr != NULL);
308   g_return_if_fail (GDK_IS_WINDOW (window));
309
310   surface = _gdk_window_ref_cairo_surface (window);
311   cairo_set_source_surface (cr, surface, x, y);
312   cairo_surface_destroy (surface);
313 }
314
315 /**
316  * _gdk_cairo_surface_extents:
317  * @surface: surface to measure
318  * @extents: (out): rectangle to put the extents
319  *
320  * Measures the area covered by @surface and puts it into @extents.
321  * Note that this function respects device offsets set on @surface.
322  * if @surface is unbounded, the resulting extents will be empty and
323  * not be a maximal sized rectangle. This is to avoid careless coding.
324  * You must explicitly check the return value of you want to handle
325  * that case.
326  *
327  * Returns: %TRUE if the extents fit in a #GdkRectangle, %FALSE if not.
328  **/
329 gboolean
330 _gdk_cairo_surface_extents (cairo_surface_t *surface,
331                             GdkRectangle *extents)
332 {
333   double x1, x2, y1, y2;
334   cairo_t *cr;
335
336   g_return_val_if_fail (surface != NULL, FALSE);
337   g_return_val_if_fail (extents != NULL, FALSE);
338
339   cr = cairo_create (surface);
340   cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
341   cairo_destroy (cr);
342
343   x1 = floor (x1);
344   y1 = floor (y1);
345   x2 = ceil (x2);
346   y2 = ceil (y2);
347   x2 -= x1;
348   y2 -= y1;
349   
350   if (x1 < G_MININT || x1 > G_MAXINT ||
351       y1 < G_MININT || y1 > G_MAXINT ||
352       x2 > G_MAXINT || y2 > G_MAXINT)
353     {
354       extents->x = extents->y = extents->width = extents->height = 0;
355       return FALSE;
356     }
357
358   extents->x = x1;
359   extents->y = y1;
360   extents->width = x2;
361   extents->height = y2;
362
363   return TRUE;
364 }
365
366 /* This function originally from Jean-Edouard Lachand-Robert, and
367  * available at www.codeguru.com. Simplified for our needs, not sure
368  * how much of the original code left any longer. Now handles just
369  * one-bit deep bitmaps (in Window parlance, ie those that GDK calls
370  * bitmaps (and not pixmaps), with zero pixels being transparent.
371  */
372 /**
373  * gdk_cairo_region_create_from_surface:
374  * @surface: A surface
375  *
376  * Creates region that describes covers the area where the given @surface
377  * is more than 50% opaque. This function takes into account device
378  * offsets that might be set with cairo_surface_set_device_offset().
379  *
380  * Returns: A #cairo_region_t. This must be freed with cairo_region_destroy()
381  *   when you are done.
382  */
383 cairo_region_t *
384 gdk_cairo_region_create_from_surface (cairo_surface_t *surface)
385 {
386   cairo_region_t *region;
387   GdkRectangle extents, rect;
388   cairo_surface_t *image;
389   cairo_t *cr;
390   gint x, y, stride;
391   guchar *data;
392
393   _gdk_cairo_surface_extents (surface, &extents);
394
395   if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR)
396     return cairo_region_create_rectangle (&extents);
397
398   if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE ||
399       cairo_image_surface_get_format (surface) != CAIRO_FORMAT_A1)
400     {
401       /* coerce to an A1 image */
402       image = cairo_image_surface_create (CAIRO_FORMAT_A1,
403                                           extents.width, extents.height);
404       cr = cairo_create (image);
405       cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
406       cairo_paint (cr);
407       cairo_destroy (cr);
408     }
409   else
410     image = cairo_surface_reference (surface);
411
412   data = cairo_image_surface_get_data (image);
413   stride = cairo_image_surface_get_stride (image);
414
415   region = cairo_region_create ();
416
417   for (y = 0; y < extents.height; y++)
418     {
419       for (x = 0; x < extents.width; x++)
420         {
421           /* Search for a continuous range of "non transparent pixels"*/
422           gint x0 = x;
423           while (x < extents.width)
424             {
425               if (((data[x / 8] >> (x%8)) & 1) == 0)
426                 /* This pixel is "transparent"*/
427                 break;
428               x++;
429             }
430           
431           if (x > x0)
432             {
433               /* Add the pixels (x0, y) to (x, y+1) as a new rectangle
434                * in the region
435                */
436               rect.x = x0;
437               rect.width = x - x0;
438               rect.y = y;
439               rect.height = 1;
440
441               cairo_region_union_rectangle (region, &rect);
442             }
443         }
444       data += stride;
445     }
446
447   cairo_surface_destroy (image);
448   
449   cairo_region_translate (region, extents.x, extents.y);
450
451   return region;
452 }
453