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, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gdkcairoprivate.h"
21
22#include <math.h>
23
24/**
25 * gdk_cairo_set_source_rgba:
26 * @cr: a cairo context
27 * @rgba: a `GdkRGBA`
28 *
29 * Sets the specified `GdkRGBA` as the source color of @cr.
30 */
31void
32gdk_cairo_set_source_rgba (cairo_t *cr,
33 const GdkRGBA *rgba)
34{
35 g_return_if_fail (cr != NULL);
36 g_return_if_fail (rgba != NULL);
37
38 cairo_set_source_rgba (cr,
39 red: rgba->red,
40 green: rgba->green,
41 blue: rgba->blue,
42 alpha: rgba->alpha);
43}
44
45/**
46 * gdk_cairo_rectangle:
47 * @cr: a cairo context
48 * @rectangle: a `GdkRectangle`
49 *
50 * Adds the given rectangle to the current path of @cr.
51 */
52void
53gdk_cairo_rectangle (cairo_t *cr,
54 const GdkRectangle *rectangle)
55{
56 g_return_if_fail (cr != NULL);
57 g_return_if_fail (rectangle != NULL);
58
59 cairo_rectangle (cr,
60 x: rectangle->x, y: rectangle->y,
61 width: rectangle->width, height: rectangle->height);
62}
63
64/**
65 * gdk_cairo_region:
66 * @cr: a cairo context
67 * @region: a `cairo_region_t`
68 *
69 * Adds the given region to the current path of @cr.
70 */
71void
72gdk_cairo_region (cairo_t *cr,
73 const cairo_region_t *region)
74{
75 cairo_rectangle_int_t box;
76 int n_boxes, i;
77
78 g_return_if_fail (cr != NULL);
79 g_return_if_fail (region != NULL);
80
81 n_boxes = cairo_region_num_rectangles (region);
82
83 for (i = 0; i < n_boxes; i++)
84 {
85 cairo_region_get_rectangle (region, nth: i, rectangle: &box);
86 cairo_rectangle (cr, x: box.x, y: box.y, width: box.width, height: box.height);
87 }
88}
89
90void
91gdk_cairo_surface_paint_pixbuf (cairo_surface_t *surface,
92 const GdkPixbuf *pixbuf)
93{
94 GdkTexture *texture;
95
96 if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
97 return;
98
99 /* This function can't just copy any pixbuf to any surface, be
100 * sure to read the invariants here before calling it */
101
102 g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
103 g_assert (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_RGB24 ||
104 cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32);
105 g_assert (cairo_image_surface_get_width (surface) == gdk_pixbuf_get_width (pixbuf));
106 g_assert (cairo_image_surface_get_height (surface) == gdk_pixbuf_get_height (pixbuf));
107
108 cairo_surface_flush (surface);
109
110 texture = gdk_texture_new_for_pixbuf (GDK_PIXBUF (pixbuf));
111 gdk_texture_download (texture,
112 data: cairo_image_surface_get_data (surface),
113 stride: cairo_image_surface_get_stride (surface));
114 g_object_unref (object: texture);
115
116 cairo_surface_mark_dirty (surface);
117}
118
119/**
120 * gdk_cairo_set_source_pixbuf:
121 * @cr: a cairo context
122 * @pixbuf: a `GdkPixbuf`
123 * @pixbuf_x: X coordinate of location to place upper left corner of @pixbuf
124 * @pixbuf_y: Y coordinate of location to place upper left corner of @pixbuf
125 *
126 * Sets the given pixbuf as the source pattern for @cr.
127 *
128 * The pattern has an extend mode of %CAIRO_EXTEND_NONE and is aligned
129 * so that the origin of @pixbuf is @pixbuf_x, @pixbuf_y.
130 */
131void
132gdk_cairo_set_source_pixbuf (cairo_t *cr,
133 const GdkPixbuf *pixbuf,
134 double pixbuf_x,
135 double pixbuf_y)
136{
137 cairo_format_t format;
138 cairo_surface_t *surface;
139
140 if (gdk_pixbuf_get_n_channels (pixbuf) == 3)
141 format = CAIRO_FORMAT_RGB24;
142 else
143 format = CAIRO_FORMAT_ARGB32;
144
145 surface = cairo_surface_create_similar_image (other: cairo_get_target (cr),
146 format,
147 width: gdk_pixbuf_get_width (pixbuf),
148 height: gdk_pixbuf_get_height (pixbuf));
149
150 gdk_cairo_surface_paint_pixbuf (surface, pixbuf);
151
152 cairo_set_source_surface (cr, surface, x: pixbuf_x, y: pixbuf_y);
153 cairo_surface_destroy (surface);
154}
155
156/*
157 * _gdk_cairo_surface_extents:
158 * @surface: surface to measure
159 * @extents: (out): rectangle to put the extents
160 *
161 * Measures the area covered by @surface and puts it into @extents.
162 *
163 * Note that this function respects device offsets set on @surface.
164 * If @surface is unbounded, the resulting extents will be empty and
165 * not be a maximal sized rectangle. This is to avoid careless coding.
166 * You must explicitly check the return value of you want to handle
167 * that case.
168 *
169 * Returns: %TRUE if the extents fit in a `GdkRectangle`, %FALSE if not
170 */
171gboolean
172_gdk_cairo_surface_extents (cairo_surface_t *surface,
173 GdkRectangle *extents)
174{
175 double x1, x2, y1, y2;
176 cairo_t *cr;
177
178 g_return_val_if_fail (surface != NULL, FALSE);
179 g_return_val_if_fail (extents != NULL, FALSE);
180
181 cr = cairo_create (target: surface);
182 cairo_clip_extents (cr, x1: &x1, y1: &y1, x2: &x2, y2: &y2);
183 cairo_destroy (cr);
184
185 x1 = floor (x: x1);
186 y1 = floor (x: y1);
187 x2 = ceil (x: x2);
188 y2 = ceil (x: y2);
189 x2 -= x1;
190 y2 -= y1;
191
192 if (x1 < G_MININT || x1 > G_MAXINT ||
193 y1 < G_MININT || y1 > G_MAXINT ||
194 x2 > G_MAXINT || y2 > G_MAXINT)
195 {
196 extents->x = extents->y = extents->width = extents->height = 0;
197 return FALSE;
198 }
199
200 extents->x = x1;
201 extents->y = y1;
202 extents->width = x2;
203 extents->height = y2;
204
205 return TRUE;
206}
207
208/* This function originally from Jean-Edouard Lachand-Robert, and
209 * available at www.codeguru.com. Simplified for our needs, not sure
210 * how much of the original code left any longer. Now handles just
211 * one-bit deep bitmaps (in Window parlance, ie those that GDK calls
212 * bitmaps (and not pixmaps), with zero pixels being transparent.
213 */
214/**
215 * gdk_cairo_region_create_from_surface:
216 * @surface: a cairo surface
217 *
218 * Creates region that covers the area where the given
219 * @surface is more than 50% opaque.
220 *
221 * This function takes into account device offsets that might be
222 * set with cairo_surface_set_device_offset().
223 *
224 * Returns: (transfer full): A `cairo_region_t`
225 */
226cairo_region_t *
227gdk_cairo_region_create_from_surface (cairo_surface_t *surface)
228{
229 cairo_region_t *region;
230 GdkRectangle extents, rect;
231 cairo_surface_t *image;
232 cairo_t *cr;
233 int x, y, stride;
234 guchar *data;
235
236 _gdk_cairo_surface_extents (surface, extents: &extents);
237
238 if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR)
239 return cairo_region_create_rectangle (rectangle: &extents);
240
241 if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE ||
242 cairo_image_surface_get_format (surface) != CAIRO_FORMAT_A1)
243 {
244 /* coerce to an A1 image */
245 image = cairo_image_surface_create (format: CAIRO_FORMAT_A1,
246 width: extents.width, height: extents.height);
247 cr = cairo_create (target: image);
248 cairo_set_source_surface (cr, surface, x: -extents.x, y: -extents.y);
249 cairo_paint (cr);
250 cairo_destroy (cr);
251 }
252 else
253 image = cairo_surface_reference (surface);
254
255 /* Flush the surface to make sure that the rendering is up to date. */
256 cairo_surface_flush (surface: image);
257
258 data = cairo_image_surface_get_data (surface: image);
259 stride = cairo_image_surface_get_stride (surface: image);
260
261 region = cairo_region_create ();
262
263 for (y = 0; y < extents.height; y++)
264 {
265 for (x = 0; x < extents.width; x++)
266 {
267 /* Search for a continuous range of "non transparent pixels"*/
268 int x0 = x;
269 while (x < extents.width)
270 {
271#if G_BYTE_ORDER == G_LITTLE_ENDIAN
272 if (((data[x / 8] >> (x%8)) & 1) == 0)
273#else
274 if (((data[x / 8] >> (7-(x%8))) & 1) == 0)
275#endif
276 /* This pixel is "transparent"*/
277 break;
278 x++;
279 }
280
281 if (x > x0)
282 {
283 /* Add the pixels (x0, y) to (x, y+1) as a new rectangle
284 * in the region
285 */
286 rect.x = x0;
287 rect.width = x - x0;
288 rect.y = y;
289 rect.height = 1;
290
291 cairo_region_union_rectangle (dst: region, rectangle: &rect);
292 }
293 }
294 data += stride;
295 }
296
297 cairo_surface_destroy (surface: image);
298
299 cairo_region_translate (region, dx: extents.x, dy: extents.y);
300
301 return region;
302}
303
304cairo_region_t *
305gdk_cairo_region_from_clip (cairo_t *cr)
306{
307 cairo_rectangle_list_t *rectangles;
308 cairo_region_t *region;
309 int i;
310
311 rectangles = cairo_copy_clip_rectangle_list (cr);
312
313 if (rectangles->status != CAIRO_STATUS_SUCCESS)
314 return NULL;
315
316 region = cairo_region_create ();
317 for (i = 0; i < rectangles->num_rectangles; i++)
318 {
319 cairo_rectangle_int_t clip_rect;
320 cairo_rectangle_t *rect;
321
322 rect = &rectangles->rectangles[i];
323
324 /* Here we assume clip rects are ints for direct targets, which
325 is true for cairo */
326 clip_rect.x = (int)rect->x;
327 clip_rect.y = (int)rect->y;
328 clip_rect.width = (int)rect->width;
329 clip_rect.height = (int)rect->height;
330
331 cairo_region_union_rectangle (dst: region, rectangle: &clip_rect);
332 }
333
334 cairo_rectangle_list_destroy (rectangle_list: rectangles);
335
336 return region;
337}
338

source code of gtk/gdk/gdkcairo.c