gdkcairo.c 9.5 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GDK - The GIMP Drawing Kit
2
3
4
5
6
7
8
9
10
 * Copyright (C) 2005 Red Hat, Inc. 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
13
14
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16
17
 */

18
#include "config.h"
19

Benjamin Otte's avatar
Benjamin Otte committed
20
#include "gdkcairoprivate.h"
21

22
#include <math.h>
23

24
25
/**
 * gdk_cairo_set_source_rgba:
26
 * @cr: a cairo context
Matthias Clasen's avatar
Matthias Clasen committed
27
 * @rgba: a `GdkRGBA`
28
 *
Matthias Clasen's avatar
Matthias Clasen committed
29
 * Sets the specified `GdkRGBA` as the source color of @cr.
30
 */
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void
gdk_cairo_set_source_rgba (cairo_t       *cr,
                           const GdkRGBA *rgba)
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (rgba != NULL);

  cairo_set_source_rgba (cr,
                         rgba->red,
                         rgba->green,
                         rgba->blue,
                         rgba->alpha);
}

45
46
/**
 * gdk_cairo_rectangle:
47
 * @cr: a cairo context
Matthias Clasen's avatar
Matthias Clasen committed
48
 * @rectangle: a `GdkRectangle`
49
 *
50
 * Adds the given rectangle to the current path of @cr.
51
 */
52
void
53
gdk_cairo_rectangle (cairo_t            *cr,
54
                     const GdkRectangle *rectangle)
55
56
57
58
59
{
  g_return_if_fail (cr != NULL);
  g_return_if_fail (rectangle != NULL);

  cairo_rectangle (cr,
60
61
                   rectangle->x,     rectangle->y,
                   rectangle->width, rectangle->height);
62
63
64
65
}

/**
 * gdk_cairo_region:
66
 * @cr: a cairo context
Matthias Clasen's avatar
Matthias Clasen committed
67
 * @region: a `cairo_region_t`
68
 *
69
 * Adds the given region to the current path of @cr.
70
 */
71
void
Matthias Clasen's avatar
Matthias Clasen committed
72
gdk_cairo_region (cairo_t              *cr,
73
                  const cairo_region_t *region)
74
{
75
  cairo_rectangle_int_t box;
Benjamin Otte's avatar
Benjamin Otte committed
76
  int n_boxes, i;
77
78
79
80

  g_return_if_fail (cr != NULL);
  g_return_if_fail (region != NULL);

81
  n_boxes = cairo_region_num_rectangles (region);
82
83

  for (i = 0; i < n_boxes; i++)
84
85
86
87
    {
      cairo_region_get_rectangle (region, i, &box);
      cairo_rectangle (cr, box.x, box.y, box.width, box.height);
    }
88
89
}

90
void
Benjamin Otte's avatar
Benjamin Otte committed
91
92
gdk_cairo_surface_paint_pixbuf (cairo_surface_t *surface,
                                const GdkPixbuf *pixbuf)
93
{
94
  GdkTexture *texture;
95

96
97
98
  if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
    return;

Benjamin Otte's avatar
Benjamin Otte committed
99
100
  /* This function can't just copy any pixbuf to any surface, be
   * sure to read the invariants here before calling it */
101

Benjamin Otte's avatar
Benjamin Otte committed
102
103
104
105
106
107
108
109
  g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
  g_assert (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_RGB24 ||
            cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32);
  g_assert (cairo_image_surface_get_width (surface) == gdk_pixbuf_get_width (pixbuf));
  g_assert (cairo_image_surface_get_height (surface) == gdk_pixbuf_get_height (pixbuf));

  cairo_surface_flush (surface);

110
111
112
113
114
  texture = gdk_texture_new_for_pixbuf (GDK_PIXBUF (pixbuf));
  gdk_texture_download (texture,
                        cairo_image_surface_get_data (surface),
                        cairo_image_surface_get_stride (surface));
  g_object_unref (texture);
115

116
  cairo_surface_mark_dirty (surface);
Benjamin Otte's avatar
Benjamin Otte committed
117
118
}

119
120
121
/**
 * gdk_cairo_set_source_pixbuf:
 * @cr: a cairo context
Matthias Clasen's avatar
Matthias Clasen committed
122
 * @pixbuf: a `GdkPixbuf`
123
124
125
126
127
128
129
130
131
132
133
 * @pixbuf_x: X coordinate of location to place upper left corner of @pixbuf
 * @pixbuf_y: Y coordinate of location to place upper left corner of @pixbuf
 *
 * Sets the given pixbuf as the source pattern for @cr.
 *
 * The pattern has an extend mode of %CAIRO_EXTEND_NONE and is aligned
 * so that the origin of @pixbuf is @pixbuf_x, @pixbuf_y.
 */
void
gdk_cairo_set_source_pixbuf (cairo_t         *cr,
                             const GdkPixbuf *pixbuf,
134
135
                             double           pixbuf_x,
                             double           pixbuf_y)
136
{
137
  cairo_format_t format;
138
139
  cairo_surface_t *surface;

140
141
142
143
144
145
146
147
148
149
  if (gdk_pixbuf_get_n_channels (pixbuf) == 3)
    format = CAIRO_FORMAT_RGB24;
  else
    format = CAIRO_FORMAT_ARGB32;

  surface = cairo_surface_create_similar_image (cairo_get_target (cr),
                                                format,
                                                gdk_pixbuf_get_width (pixbuf),
                                                gdk_pixbuf_get_height (pixbuf));

150
151
  gdk_cairo_surface_paint_pixbuf (surface, pixbuf);

152
  cairo_set_source_surface (cr, surface, pixbuf_x, pixbuf_y);
153
  cairo_surface_destroy (surface);
154
155
}

156
/*
157
158
159
160
161
 * _gdk_cairo_surface_extents:
 * @surface: surface to measure
 * @extents: (out): rectangle to put the extents
 *
 * 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
166
167
168
 * not be a maximal sized rectangle. This is to avoid careless coding.
 * You must explicitly check the return value of you want to handle
 * that case.
 *
Matthias Clasen's avatar
Matthias Clasen committed
169
 * Returns: %TRUE if the extents fit in a `GdkRectangle`, %FALSE if not
170
 */
171
172
gboolean
_gdk_cairo_surface_extents (cairo_surface_t *surface,
173
                            GdkRectangle    *extents)
174
175
176
177
178
179
180
181
182
{
  double x1, x2, y1, y2;
  cairo_t *cr;

  g_return_val_if_fail (surface != NULL, FALSE);
  g_return_val_if_fail (extents != NULL, FALSE);

  cr = cairo_create (surface);
  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
Xan Lopez's avatar
Xan Lopez committed
183
  cairo_destroy (cr);
184
185
186
187
188
189
190

  x1 = floor (x1);
  y1 = floor (y1);
  x2 = ceil (x2);
  y2 = ceil (y2);
  x2 -= x1;
  y2 -= y1;
191

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  if (x1 < G_MININT || x1 > G_MAXINT ||
      y1 < G_MININT || y1 > G_MAXINT ||
      x2 > G_MAXINT || y2 > G_MAXINT)
    {
      extents->x = extents->y = extents->width = extents->height = 0;
      return FALSE;
    }

  extents->x = x1;
  extents->y = y1;
  extents->width = x2;
  extents->height = y2;

  return TRUE;
}

/* This function originally from Jean-Edouard Lachand-Robert, and
 * available at www.codeguru.com. Simplified for our needs, not sure
 * how much of the original code left any longer. Now handles just
 * one-bit deep bitmaps (in Window parlance, ie those that GDK calls
 * bitmaps (and not pixmaps), with zero pixels being transparent.
 */
/**
 * 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
222
223
 * This function takes into account device offsets that might be
 * set with cairo_surface_set_device_offset().
 *
Matthias Clasen's avatar
Matthias Clasen committed
224
 * Returns: (transfer full): A `cairo_region_t`
225
 */
226
227
228
229
230
231
232
cairo_region_t *
gdk_cairo_region_create_from_surface (cairo_surface_t *surface)
{
  cairo_region_t *region;
  GdkRectangle extents, rect;
  cairo_surface_t *image;
  cairo_t *cr;
Benjamin Otte's avatar
Benjamin Otte committed
233
  int x, y, stride;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  guchar *data;

  _gdk_cairo_surface_extents (surface, &extents);

  if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR)
    return cairo_region_create_rectangle (&extents);

  if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE ||
      cairo_image_surface_get_format (surface) != CAIRO_FORMAT_A1)
    {
      /* coerce to an A1 image */
      image = cairo_image_surface_create (CAIRO_FORMAT_A1,
                                          extents.width, extents.height);
      cr = cairo_create (image);
248
      cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
249
250
251
252
253
254
      cairo_paint (cr);
      cairo_destroy (cr);
    }
  else
    image = cairo_surface_reference (surface);

255
256
257
  /* Flush the surface to make sure that the rendering is up to date. */
  cairo_surface_flush (image);

258
259
260
261
262
263
264
265
  data = cairo_image_surface_get_data (image);
  stride = cairo_image_surface_get_stride (image);

  region = cairo_region_create ();

  for (y = 0; y < extents.height; y++)
    {
      for (x = 0; x < extents.width; x++)
266
267
        {
          /* Search for a continuous range of "non transparent pixels"*/
Benjamin Otte's avatar
Benjamin Otte committed
268
          int x0 = x;
269
270
          while (x < extents.width)
            {
271
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
272
              if (((data[x / 8] >> (x%8)) & 1) == 0)
273
274
275
#else
              if (((data[x / 8] >> (7-(x%8))) & 1) == 0)
#endif
276
277
278
279
280
281
282
283
284
285
                /* This pixel is "transparent"*/
                break;
              x++;
            }

          if (x > x0)
            {
              /* Add the pixels (x0, y) to (x, y+1) as a new rectangle
               * in the region
               */
286
287
288
289
290
291
              rect.x = x0;
              rect.width = x - x0;
              rect.y = y;
              rect.height = 1;

              cairo_region_union_rectangle (region, &rect);
292
293
            }
        }
294
295
296
297
      data += stride;
    }

  cairo_surface_destroy (image);
298

299
300
301
302
  cairo_region_translate (region, extents.x, extents.y);

  return region;
}
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

cairo_region_t *
gdk_cairo_region_from_clip (cairo_t *cr)
{
  cairo_rectangle_list_t *rectangles;
  cairo_region_t *region;
  int i;

  rectangles = cairo_copy_clip_rectangle_list (cr);

  if (rectangles->status != CAIRO_STATUS_SUCCESS)
    return NULL;

  region = cairo_region_create ();
  for (i = 0; i < rectangles->num_rectangles; i++)
    {
      cairo_rectangle_int_t clip_rect;
      cairo_rectangle_t *rect;

      rect = &rectangles->rectangles[i];

      /* Here we assume clip rects are ints for direct targets, which
         is true for cairo */
      clip_rect.x = (int)rect->x;
      clip_rect.y = (int)rect->y;
      clip_rect.width = (int)rect->width;
      clip_rect.height = (int)rect->height;

      cairo_region_union_rectangle (region, &clip_rect);
    }

  cairo_rectangle_list_destroy (rectangles);

  return region;
}