~ml-launchpad/ubuntu/natty/gcompris/fix-for-777349

« back to all changes in this revision

Viewing changes to src/goocanvas/src/goocanvasutils.c

  • Committer: Bazaar Package Importer
  • Author(s): Marc Gariepy, Marc Gariepy, Stephane Graber
  • Date: 2010-01-04 17:42:49 UTC
  • mfrom: (1.1.14 upstream)
  • Revision ID: james.westby@ubuntu.com-20100104174249-7bupatd9dtxyhvs4
Tags: 9.0-0ubuntu1
[Marc Gariepy]
* New upstream release (9.0).
* Remove cache.c from POTFILES to avoid FTBFS
* Remove unneeded rm in debian/rules (file no longer exists upstream)

[Stephane Graber]
* Bump Debian standards to 3.8.3
* Add patch system (dpatch)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
 
3
 * Released under the GNU LGPL license. See COPYING for details.
 
4
 *
 
5
 * goocanvasutils.c - utility functions.
 
6
 */
 
7
 
 
8
/**
 
9
 * SECTION:goocanvasutils
 
10
 * @Title: GooCanvas Types
 
11
 * @Short_Description: types used in GooCanvas.
 
12
 *
 
13
 * This section describes the types used throughout GooCanvas.
 
14
 */
 
15
#include <config.h>
 
16
#include <math.h>
 
17
#include <gtk/gtk.h>
 
18
#include "goocanvas.h"
 
19
 
 
20
 
 
21
/* Glib doesn't provide a g_ptr_array_index() so we need our own one. */
 
22
void
 
23
goo_canvas_util_ptr_array_insert (GPtrArray *ptr_array,
 
24
                                  gpointer   data,
 
25
                                  gint       index)
 
26
{
 
27
  gint i;
 
28
 
 
29
  /* Add the pointer at the end so there is enough room. */
 
30
  g_ptr_array_add (ptr_array, data);
 
31
 
 
32
  /* If index is -1, we are done. */
 
33
  if (index == -1)
 
34
    return;
 
35
 
 
36
  /* Move the other pointers to make room for the new one. */
 
37
  for (i = ptr_array->len - 1; i > index; i--)
 
38
    ptr_array->pdata[i] = ptr_array->pdata[i - 1];
 
39
 
 
40
  /* Put the new element in its proper place. */
 
41
  ptr_array->pdata[index] = data;
 
42
}
 
43
 
 
44
 
 
45
/* Glib doesn't provide a g_ptr_array_move() so we need our own one. */
 
46
void
 
47
goo_canvas_util_ptr_array_move (GPtrArray *ptr_array,
 
48
                                gint       old_index,
 
49
                                gint       new_index)
 
50
{
 
51
  gpointer data;
 
52
  gint i;
 
53
 
 
54
  data = ptr_array->pdata[old_index];
 
55
 
 
56
  if (new_index > old_index)
 
57
    {
 
58
      /* Move the pointers down one place. */
 
59
      for (i = old_index; i < new_index; i++)
 
60
        ptr_array->pdata[i] = ptr_array->pdata[i + 1];
 
61
    }
 
62
  else
 
63
    {
 
64
      /* Move the pointers up one place. */
 
65
      for (i = old_index; i > new_index; i--)
 
66
        ptr_array->pdata[i] = ptr_array->pdata[i - 1];
 
67
    }
 
68
 
 
69
  ptr_array->pdata[new_index] = data;
 
70
}
 
71
 
 
72
 
 
73
/* Glib doesn't provide a g_ptr_array_move() so we need our own one. */
 
74
gint
 
75
goo_canvas_util_ptr_array_find_index (GPtrArray *ptr_array,
 
76
                                      gpointer   data)
 
77
{
 
78
  gint i;
 
79
 
 
80
  for (i = 0; i < ptr_array->len; i++)
 
81
    {
 
82
      if (ptr_array->pdata[i] == data)
 
83
        return i;
 
84
    }
 
85
 
 
86
  return -1;
 
87
}
 
88
 
 
89
 
 
90
/*
 
91
 * Cairo utilities.
 
92
 */
 
93
 
 
94
cairo_surface_t*
 
95
goo_canvas_cairo_surface_from_pixbuf (GdkPixbuf *pixbuf)
 
96
{
 
97
  gint width = gdk_pixbuf_get_width (pixbuf);
 
98
  gint height = gdk_pixbuf_get_height (pixbuf);
 
99
  guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
 
100
  int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
 
101
  int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
 
102
  guchar *cairo_pixels;
 
103
  cairo_format_t format;
 
104
  cairo_surface_t *surface;
 
105
  static const cairo_user_data_key_t key;
 
106
  int j;
 
107
 
 
108
  if (n_channels == 3)
 
109
    format = CAIRO_FORMAT_RGB24;
 
110
  else
 
111
    format = CAIRO_FORMAT_ARGB32;
 
112
 
 
113
  cairo_pixels = g_malloc (4 * width * height);
 
114
  surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels,
 
115
                                                 format,
 
116
                                                 width, height, 4 * width);
 
117
  cairo_surface_set_user_data (surface, &key,
 
118
                               cairo_pixels, (cairo_destroy_func_t)g_free);
 
119
 
 
120
  for (j = height; j; j--)
 
121
    {
 
122
      guchar *p = gdk_pixels;
 
123
      guchar *q = cairo_pixels;
 
124
 
 
125
      if (n_channels == 3)
 
126
        {
 
127
          guchar *end = p + 3 * width;
 
128
          
 
129
          while (p < end)
 
130
            {
 
131
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
 
132
              q[0] = p[2];
 
133
              q[1] = p[1];
 
134
              q[2] = p[0];
 
135
#else     
 
136
              q[1] = p[0];
 
137
              q[2] = p[1];
 
138
              q[3] = p[2];
 
139
#endif
 
140
              p += 3;
 
141
              q += 4;
 
142
            }
 
143
        }
 
144
      else
 
145
        {
 
146
          guchar *end = p + 4 * width;
 
147
          guint t1,t2,t3;
 
148
            
 
149
#define MULT(d,c,a,t) G_STMT_START { t = c * a; d = ((t >> 8) + t) >> 8; } G_STMT_END
 
150
 
 
151
          while (p < end)
 
152
            {
 
153
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
 
154
              MULT(q[0], p[2], p[3], t1);
 
155
              MULT(q[1], p[1], p[3], t2);
 
156
              MULT(q[2], p[0], p[3], t3);
 
157
              q[3] = p[3];
 
158
#else     
 
159
              q[0] = p[3];
 
160
              MULT(q[1], p[0], p[3], t1);
 
161
              MULT(q[2], p[1], p[3], t2);
 
162
              MULT(q[3], p[2], p[3], t3);
 
163
#endif
 
164
              
 
165
              p += 4;
 
166
              q += 4;
 
167
            }
 
168
          
 
169
#undef MULT
 
170
        }
 
171
 
 
172
      gdk_pixels += gdk_rowstride;
 
173
      cairo_pixels += 4 * width;
 
174
    }
 
175
 
 
176
  return surface;
 
177
}
 
178
 
 
179
 
 
180
cairo_pattern_t*
 
181
goo_canvas_cairo_pattern_from_pixbuf (GdkPixbuf *pixbuf)
 
182
{
 
183
  cairo_surface_t *surface;
 
184
  cairo_pattern_t *pattern;
 
185
 
 
186
  surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf);
 
187
  pattern = cairo_pattern_create_for_surface (surface);
 
188
  cairo_surface_destroy (surface);
 
189
 
 
190
  return pattern;
 
191
}
 
192
 
 
193
 
 
194
/*
 
195
 * Cairo types.
 
196
 */
 
197
GType
 
198
goo_cairo_pattern_get_type (void)
 
199
{
 
200
  static GType cairo_pattern_type = 0;
 
201
  
 
202
  if (cairo_pattern_type == 0)
 
203
    cairo_pattern_type = g_boxed_type_register_static
 
204
      ("GooCairoPattern",
 
205
       (GBoxedCopyFunc) cairo_pattern_reference,
 
206
       (GBoxedFreeFunc) cairo_pattern_destroy);
 
207
 
 
208
  return cairo_pattern_type;
 
209
}
 
210
 
 
211
 
 
212
GType
 
213
goo_cairo_fill_rule_get_type (void)
 
214
{
 
215
  static GType etype = 0;
 
216
  if (etype == 0) {
 
217
    static const GEnumValue values[] = {
 
218
      { CAIRO_FILL_RULE_WINDING, "CAIRO_FILL_RULE_WINDING", "winding" },
 
219
      { CAIRO_FILL_RULE_EVEN_ODD, "CAIRO_FILL_RULE_EVEN_ODD", "even-odd" },
 
220
      { 0, NULL, NULL }
 
221
    };
 
222
    etype = g_enum_register_static ("GooCairoFillRule", values);
 
223
  }
 
224
  return etype;
 
225
}
 
226
 
 
227
 
 
228
GType
 
229
goo_cairo_operator_get_type (void)
 
230
{
 
231
  static GType etype = 0;
 
232
  if (etype == 0) {
 
233
    static const GEnumValue values[] = {
 
234
      { CAIRO_OPERATOR_CLEAR, "CAIRO_OPERATOR_CLEAR", "clear" },
 
235
 
 
236
      { CAIRO_OPERATOR_SOURCE, "CAIRO_OPERATOR_SOURCE", "source" },
 
237
      { CAIRO_OPERATOR_OVER, "CAIRO_OPERATOR_OVER", "over" },
 
238
      { CAIRO_OPERATOR_IN, "CAIRO_OPERATOR_IN", "in" },
 
239
      { CAIRO_OPERATOR_OUT, "CAIRO_OPERATOR_OUT", "out" },
 
240
      { CAIRO_OPERATOR_ATOP, "CAIRO_OPERATOR_ATOP", "atop" },
 
241
 
 
242
      { CAIRO_OPERATOR_DEST, "CAIRO_OPERATOR_DEST", "dest" },
 
243
      { CAIRO_OPERATOR_DEST_OVER, "CAIRO_OPERATOR_DEST_OVER", "dest-over" },
 
244
      { CAIRO_OPERATOR_DEST_IN, "CAIRO_OPERATOR_DEST_IN", "dest-in" },
 
245
      { CAIRO_OPERATOR_DEST_OUT, "CAIRO_OPERATOR_DEST_OUT", "dest-out" },
 
246
      { CAIRO_OPERATOR_DEST_ATOP, "CAIRO_OPERATOR_DEST_ATOP", "dest-atop" },
 
247
 
 
248
      { CAIRO_OPERATOR_XOR, "CAIRO_OPERATOR_XOR", "xor" },
 
249
      { CAIRO_OPERATOR_ADD, "CAIRO_OPERATOR_ADD", "add" },
 
250
      { CAIRO_OPERATOR_SATURATE, "CAIRO_OPERATOR_SATURATE", "saturate" },
 
251
      { 0, NULL, NULL }
 
252
    };
 
253
    etype = g_enum_register_static ("GooCairoOperator", values);
 
254
  }
 
255
  return etype;
 
256
}
 
257
 
 
258
 
 
259
GType
 
260
goo_cairo_antialias_get_type (void)
 
261
{
 
262
  static GType etype = 0;
 
263
  if (etype == 0) {
 
264
    static const GEnumValue values[] = {
 
265
      { CAIRO_ANTIALIAS_DEFAULT, "CAIRO_ANTIALIAS_DEFAULT", "default" },
 
266
      { CAIRO_ANTIALIAS_NONE, "CAIRO_ANTIALIAS_NONE", "none" },
 
267
      { CAIRO_ANTIALIAS_GRAY, "CAIRO_ANTIALIAS_GRAY", "gray" },
 
268
      { CAIRO_ANTIALIAS_SUBPIXEL, "CAIRO_ANTIALIAS_SUBPIXEL", "subpixel" },
 
269
      { 0, NULL, NULL }
 
270
    };
 
271
    etype = g_enum_register_static ("GooCairoAntialias", values);
 
272
  }
 
273
  return etype;
 
274
}
 
275
 
 
276
 
 
277
GType
 
278
goo_cairo_line_cap_get_type (void)
 
279
{
 
280
  static GType etype = 0;
 
281
  if (etype == 0) {
 
282
    static const GEnumValue values[] = {
 
283
      { CAIRO_LINE_CAP_BUTT, "CAIRO_LINE_CAP_BUTT", "butt" },
 
284
      { CAIRO_LINE_CAP_ROUND, "CAIRO_LINE_CAP_ROUND", "round" },
 
285
      { CAIRO_LINE_CAP_SQUARE, "CAIRO_LINE_CAP_SQUARE", "square" },
 
286
      { 0, NULL, NULL }
 
287
    };
 
288
    etype = g_enum_register_static ("GooCairoLineCap", values);
 
289
  }
 
290
  return etype;
 
291
}
 
292
 
 
293
 
 
294
GType
 
295
goo_cairo_line_join_get_type (void)
 
296
{
 
297
  static GType etype = 0;
 
298
  if (etype == 0) {
 
299
    static const GEnumValue values[] = {
 
300
      { CAIRO_LINE_JOIN_MITER, "CAIRO_LINE_JOIN_MITER", "miter" },
 
301
      { CAIRO_LINE_JOIN_ROUND, "CAIRO_LINE_JOIN_ROUND", "round" },
 
302
      { CAIRO_LINE_JOIN_BEVEL, "CAIRO_LINE_JOIN_BEVEL", "bevel" },
 
303
      { 0, NULL, NULL }
 
304
    };
 
305
    etype = g_enum_register_static ("GooCairoLineJoin", values);
 
306
  }
 
307
  return etype;
 
308
}
 
309
 
 
310
 
 
311
GType
 
312
goo_cairo_hint_metrics_get_type (void)
 
313
{
 
314
  static GType etype = 0;
 
315
  if (etype == 0) {
 
316
    static const GEnumValue values[] = {
 
317
      { CAIRO_HINT_METRICS_DEFAULT, "CAIRO_HINT_METRICS_DEFAULT", "default" },
 
318
      { CAIRO_HINT_METRICS_OFF, "CAIRO_HINT_METRICS_OFF", "off" },
 
319
      { CAIRO_HINT_METRICS_ON, "CAIRO_HINT_METRICS_ON", "on" },
 
320
      { 0, NULL, NULL }
 
321
    };
 
322
    etype = g_enum_register_static ("GooCairoHintMetrics", values);
 
323
  }
 
324
  return etype;
 
325
}
 
326
 
 
327
 
 
328
/**
 
329
 * goo_canvas_line_dash_ref:
 
330
 * @dash: a #GooCanvasLineDash.
 
331
 * 
 
332
 * Increments the reference count of the dash pattern.
 
333
 * 
 
334
 * Returns: the dash pattern.
 
335
 **/
 
336
GooCanvasLineDash*
 
337
goo_canvas_line_dash_ref   (GooCanvasLineDash *dash)
 
338
{
 
339
  if (dash)
 
340
    dash->ref_count++;
 
341
  return dash;
 
342
}
 
343
 
 
344
 
 
345
/**
 
346
 * goo_canvas_line_dash_unref:
 
347
 * @dash: a #GooCanvasLineDash.
 
348
 * 
 
349
 * Decrements the reference count of the dash pattern. If it falls to 0
 
350
 * it is freed.
 
351
 **/
 
352
void
 
353
goo_canvas_line_dash_unref (GooCanvasLineDash *dash)
 
354
{
 
355
  if (dash && --dash->ref_count == 0) {
 
356
    g_free (dash->dashes);
 
357
    g_free (dash);
 
358
  }
 
359
}
 
360
 
 
361
 
 
362
GType
 
363
goo_canvas_line_dash_get_type (void)
 
364
{
 
365
  static GType cairo_line_dash_type = 0;
 
366
  
 
367
  if (cairo_line_dash_type == 0)
 
368
    cairo_line_dash_type = g_boxed_type_register_static
 
369
      ("GooCanvasLineDash",
 
370
       (GBoxedCopyFunc) goo_canvas_line_dash_ref,
 
371
       (GBoxedFreeFunc) goo_canvas_line_dash_unref);
 
372
 
 
373
  return cairo_line_dash_type;
 
374
}
 
375
 
 
376
 
 
377
/**
 
378
 * goo_canvas_line_dash_new:
 
379
 * @num_dashes: the number of dashes and gaps in the pattern.
 
380
 * @...: the length of each dash and gap.
 
381
 * 
 
382
 * Creates a new dash pattern.
 
383
 * 
 
384
 * Returns: a new dash pattern.
 
385
 **/
 
386
GooCanvasLineDash*
 
387
goo_canvas_line_dash_new (gint num_dashes,
 
388
                          ...)
 
389
{
 
390
  GooCanvasLineDash *dash;
 
391
  va_list var_args;
 
392
  gint i;
 
393
 
 
394
  dash = g_new (GooCanvasLineDash, 1);
 
395
  dash->ref_count = 1;
 
396
  dash->num_dashes = num_dashes;
 
397
  dash->dashes = g_new (double, num_dashes);
 
398
  dash->dash_offset = 0.0;
 
399
 
 
400
  va_start (var_args, num_dashes);
 
401
 
 
402
  for (i = 0; i < num_dashes; i++)
 
403
    {
 
404
      dash->dashes[i] = va_arg (var_args, double);
 
405
    }
 
406
 
 
407
  va_end (var_args);
 
408
 
 
409
  return dash;
 
410
}
 
411
 
 
412
/**
 
413
 * goo_canvas_line_dash_newv:
 
414
 * @num_dashes: the number of dashes and gaps in the pattern.
 
415
 * @dashes: a g_new-allocated vector of doubles, the length of each
 
416
 * dash and gap.
 
417
 * 
 
418
 * Creates a new dash pattern.  Takes ownership of the @dashes vector.
 
419
 * 
 
420
 * Returns: a new dash pattern.
 
421
 **/
 
422
GooCanvasLineDash*
 
423
goo_canvas_line_dash_newv (gint    num_dashes,
 
424
                           double *dashes)
 
425
{
 
426
  GooCanvasLineDash *dash;
 
427
 
 
428
  dash = g_new (GooCanvasLineDash, 1);
 
429
  dash->ref_count = 1;
 
430
  dash->num_dashes = num_dashes;
 
431
  dash->dashes = dashes;
 
432
  dash->dash_offset = 0.0;
 
433
 
 
434
  return dash;
 
435
}
 
436
 
 
437
cairo_matrix_t*
 
438
goo_cairo_matrix_copy   (const cairo_matrix_t *matrix)
 
439
{
 
440
  cairo_matrix_t *matrix_copy;
 
441
 
 
442
  if (!matrix)
 
443
    return NULL;
 
444
 
 
445
  matrix_copy = g_slice_new (cairo_matrix_t);
 
446
  *matrix_copy = *matrix;
 
447
 
 
448
  return matrix_copy;
 
449
}
 
450
 
 
451
 
 
452
void
 
453
goo_cairo_matrix_free   (cairo_matrix_t *matrix)
 
454
{
 
455
  g_slice_free (cairo_matrix_t, matrix);
 
456
}
 
457
 
 
458
 
 
459
GType
 
460
goo_cairo_matrix_get_type (void)
 
461
{
 
462
  static GType type_cairo_matrix = 0;
 
463
 
 
464
  if (!type_cairo_matrix)
 
465
    type_cairo_matrix = g_boxed_type_register_static
 
466
      ("GooCairoMatrix", 
 
467
       (GBoxedCopyFunc) goo_cairo_matrix_copy,
 
468
       (GBoxedFreeFunc) goo_cairo_matrix_free);
 
469
 
 
470
  return type_cairo_matrix;
 
471
}
 
472
 
 
473
 
 
474
/* This is a semi-private function to help gtk-doc find child properties. */
 
475
GParamSpec**
 
476
goo_canvas_query_child_properties (gpointer  class,
 
477
                                   guint     *n_properties)
 
478
{
 
479
  if (!G_TYPE_IS_CLASSED (G_TYPE_FROM_CLASS (class)))
 
480
    return NULL;
 
481
 
 
482
  if (g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM))
 
483
    return goo_canvas_item_class_list_child_properties (class,
 
484
                                                        n_properties);
 
485
 
 
486
  if (g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM_MODEL))
 
487
    return goo_canvas_item_model_class_list_child_properties (class,
 
488
                                                              n_properties);
 
489
 
 
490
  return NULL;
 
491
}
 
492
 
 
493
 
 
494
static gdouble
 
495
parse_double (gchar    **pos,
 
496
              gboolean  *error)
 
497
{
 
498
  gdouble result;
 
499
  gchar *p;
 
500
 
 
501
  /* If an error has already occurred, just return. */
 
502
  if (*error)
 
503
    return 0;
 
504
 
 
505
  /* Skip whitespace and commas. */
 
506
  p = *pos;
 
507
  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
 
508
    p++;
 
509
 
 
510
  /* Parse the double, and set pos to the first char after it. */
 
511
  result = g_ascii_strtod (p, pos);
 
512
 
 
513
  /* If no characters were parsed, set the error flag. */
 
514
  if (p == *pos)
 
515
    *error = TRUE;
 
516
 
 
517
  return result;
 
518
}
 
519
 
 
520
 
 
521
static gint
 
522
parse_flag (gchar    **pos,
 
523
            gboolean  *error)
 
524
{
 
525
  gint result = 0;
 
526
  gchar *p;
 
527
 
 
528
  /* If an error has already occurred, just return. */
 
529
  if (*error)
 
530
    return 0;
 
531
 
 
532
  /* Skip whitespace and commas. */
 
533
  p = *pos;
 
534
  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
 
535
    p++;
 
536
 
 
537
  /* The flag must be a '0' or a '1'. */
 
538
  if (*p == '0')
 
539
    result = 0;
 
540
  else if (*p == '1')
 
541
    result = 1;
 
542
  else
 
543
    {
 
544
      *error = TRUE;
 
545
      return 0;
 
546
    }
 
547
 
 
548
  *pos = p + 1;
 
549
 
 
550
  return result;
 
551
}
 
552
 
 
553
 
 
554
/**
 
555
 * goo_canvas_parse_path_data:
 
556
 * @path_data: the sequence of path commands, specified as a string using the
 
557
 *  same syntax as in the <ulink url="http://www.w3.org/Graphics/SVG/">Scalable
 
558
 *  Vector Graphics (SVG)</ulink> path element.
 
559
 * 
 
560
 * Parses the given SVG path specification string.
 
561
 * 
 
562
 * Returns: a #GArray of #GooCanvasPathCommand elements.
 
563
 **/
 
564
GArray*
 
565
goo_canvas_parse_path_data (const gchar       *path_data)
 
566
{
 
567
  GArray *commands;
 
568
  GooCanvasPathCommand cmd;
 
569
  gchar *pos, command = 0, next_command;
 
570
  gboolean error = FALSE;
 
571
 
 
572
  commands = g_array_new (0, 0, sizeof (GooCanvasPathCommand));
 
573
 
 
574
  if (!path_data)
 
575
    return commands;
 
576
 
 
577
  pos = (gchar*) path_data;
 
578
  for (;;)
 
579
    {
 
580
      while (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n')
 
581
        pos++;
 
582
      if (!*pos)
 
583
        break;
 
584
 
 
585
      next_command = *pos;
 
586
 
 
587
      /* If there is no command letter, we use the same command as the last
 
588
         one, except for the first command, and 'moveto' (which becomes
 
589
         'lineto'). */
 
590
      if ((next_command < 'a' || next_command > 'z')
 
591
          && (next_command < 'A' || next_command > 'Z'))
 
592
        {
 
593
          /* If this is the first command, then set the error flag and assume
 
594
             a simple close-path command. */
 
595
          if (!command)
 
596
            {
 
597
              error = TRUE;
 
598
              command = 'Z';
 
599
            }
 
600
          /* moveto commands change to lineto. */
 
601
          else if (command == 'm')
 
602
            command = 'l';
 
603
          else if (command == 'M')
 
604
            command = 'L';
 
605
        }
 
606
      else
 
607
        {
 
608
          command = next_command;
 
609
          pos++;
 
610
        }
 
611
 
 
612
      cmd.simple.relative = 0;
 
613
      switch (command)
 
614
        {
 
615
          /* Simple commands like moveto and lineto: MmZzLlHhVv. */
 
616
        case 'm':
 
617
          cmd.simple.relative = 1;
 
618
        case 'M':
 
619
          cmd.simple.type = GOO_CANVAS_PATH_MOVE_TO;
 
620
          cmd.simple.x = parse_double (&pos, &error);
 
621
          cmd.simple.y = parse_double (&pos, &error);
 
622
          break;
 
623
 
 
624
        case 'Z':
 
625
        case 'z':
 
626
          cmd.simple.type = GOO_CANVAS_PATH_CLOSE_PATH;
 
627
          break;
 
628
 
 
629
        case 'l':
 
630
          cmd.simple.relative = 1;
 
631
        case 'L':
 
632
          cmd.simple.type = GOO_CANVAS_PATH_LINE_TO;
 
633
          cmd.simple.x = parse_double (&pos, &error);
 
634
          cmd.simple.y = parse_double (&pos, &error);
 
635
          break;
 
636
 
 
637
        case 'h':
 
638
          cmd.simple.relative = 1;
 
639
        case 'H':
 
640
          cmd.simple.type = GOO_CANVAS_PATH_HORIZONTAL_LINE_TO;
 
641
          cmd.simple.x = parse_double (&pos, &error);
 
642
          break;
 
643
 
 
644
        case 'v':
 
645
          cmd.simple.relative = 1;
 
646
        case 'V':
 
647
          cmd.simple.type = GOO_CANVAS_PATH_VERTICAL_LINE_TO;
 
648
          cmd.simple.y = parse_double (&pos, &error);
 
649
          break;
 
650
 
 
651
          /* Bezier curve commands: CcSsQqTt. */
 
652
        case 'c':
 
653
          cmd.curve.relative = 1;
 
654
        case 'C':
 
655
          cmd.curve.type = GOO_CANVAS_PATH_CURVE_TO;
 
656
          cmd.curve.x1 = parse_double (&pos, &error);
 
657
          cmd.curve.y1 = parse_double (&pos, &error);
 
658
          cmd.curve.x2 = parse_double (&pos, &error);
 
659
          cmd.curve.y2 = parse_double (&pos, &error);
 
660
          cmd.curve.x = parse_double (&pos, &error);
 
661
          cmd.curve.y = parse_double (&pos, &error);
 
662
          break;
 
663
 
 
664
        case 's':
 
665
          cmd.curve.relative = 1;
 
666
        case 'S':
 
667
          cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_CURVE_TO;
 
668
          cmd.curve.x2 = parse_double (&pos, &error);
 
669
          cmd.curve.y2 = parse_double (&pos, &error);
 
670
          cmd.curve.x = parse_double (&pos, &error);
 
671
          cmd.curve.y = parse_double (&pos, &error);
 
672
          break;
 
673
 
 
674
        case 'q':
 
675
          cmd.curve.relative = 1;
 
676
        case 'Q':
 
677
          cmd.curve.type = GOO_CANVAS_PATH_QUADRATIC_CURVE_TO;
 
678
          cmd.curve.x1 = parse_double (&pos, &error);
 
679
          cmd.curve.y1 = parse_double (&pos, &error);
 
680
          cmd.curve.x = parse_double (&pos, &error);
 
681
          cmd.curve.y = parse_double (&pos, &error);
 
682
          break;
 
683
 
 
684
        case 't':
 
685
          cmd.curve.relative = 1;
 
686
        case 'T':
 
687
          cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO;
 
688
          cmd.curve.x = parse_double (&pos, &error);
 
689
          cmd.curve.y = parse_double (&pos, &error);
 
690
          break;
 
691
 
 
692
          /* The elliptical arc commands: Aa. */
 
693
        case 'a':
 
694
          cmd.arc.relative = 1;
 
695
        case 'A':
 
696
          cmd.arc.type = GOO_CANVAS_PATH_ELLIPTICAL_ARC;
 
697
          cmd.arc.rx = parse_double (&pos, &error);
 
698
          cmd.arc.ry = parse_double (&pos, &error);
 
699
          cmd.arc.x_axis_rotation = parse_double (&pos, &error);
 
700
          cmd.arc.large_arc_flag = parse_flag (&pos, &error);
 
701
          cmd.arc.sweep_flag = parse_flag (&pos, &error);
 
702
          cmd.arc.x = parse_double (&pos, &error);
 
703
          cmd.arc.y = parse_double (&pos, &error);
 
704
          break;
 
705
 
 
706
        default:
 
707
          error = TRUE;
 
708
          break;
 
709
        }
 
710
 
 
711
      /* If an error has occurred, return without adding the new command.
 
712
         Thus we include everything in the path up to the error, like SVG. */
 
713
      if (error)
 
714
        return commands;
 
715
 
 
716
      g_array_append_val (commands, cmd);
 
717
    }
 
718
 
 
719
  return commands;
 
720
}
 
721
 
 
722
 
 
723
static void
 
724
do_curve_to (GooCanvasPathCommand *cmd,
 
725
             cairo_t              *cr,
 
726
             gdouble              *x,
 
727
             gdouble              *y,
 
728
             gdouble              *last_control_point_x,
 
729
             gdouble              *last_control_point_y)
 
730
{
 
731
  if (cmd->curve.relative)
 
732
    {
 
733
      cairo_curve_to (cr, *x + cmd->curve.x1, *y + cmd->curve.y1,
 
734
                      *x + cmd->curve.x2, *y + cmd->curve.y2,
 
735
                      *x + cmd->curve.x, *y + cmd->curve.y);
 
736
      *last_control_point_x = *x + cmd->curve.x2;
 
737
      *last_control_point_y = *y + cmd->curve.y2;
 
738
      *x += cmd->curve.x;
 
739
      *y += cmd->curve.y;
 
740
    }
 
741
  else
 
742
    {
 
743
      cairo_curve_to (cr, cmd->curve.x1, cmd->curve.y1,
 
744
                      cmd->curve.x2, cmd->curve.y2,
 
745
                      cmd->curve.x, cmd->curve.y);
 
746
      *last_control_point_x = cmd->curve.x2;
 
747
      *last_control_point_y = cmd->curve.y2;
 
748
      *x = cmd->curve.x;
 
749
      *y = cmd->curve.y;
 
750
    }
 
751
}
 
752
 
 
753
 
 
754
static void
 
755
do_smooth_curve_to (GooCanvasPathCommand    *cmd,
 
756
                    GooCanvasPathCommandType prev_cmd_type,
 
757
                    cairo_t                 *cr,
 
758
                    gdouble                 *x,
 
759
                    gdouble                 *y,
 
760
                    gdouble                 *last_control_point_x,
 
761
                    gdouble                 *last_control_point_y)
 
762
{
 
763
  gdouble x1, y1;
 
764
 
 
765
  /* If the last command was a curveto or smooth curveto, we use the
 
766
     reflection of the last control point about the current point as
 
767
     the first control point of this curve. Otherwise we use the
 
768
     current point as the first control point. */
 
769
  if (prev_cmd_type == GOO_CANVAS_PATH_CURVE_TO
 
770
      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_CURVE_TO)
 
771
    {
 
772
      x1 = *x + (*x - *last_control_point_x);
 
773
      y1 = *y + (*y - *last_control_point_y);
 
774
    }
 
775
  else
 
776
    {
 
777
      x1 = *x;
 
778
      y1 = *y;
 
779
    }
 
780
 
 
781
  if (cmd->curve.relative)
 
782
    {
 
783
      cairo_curve_to (cr, x1, y1, *x + cmd->curve.x2, *y + cmd->curve.y2,
 
784
                      *x + cmd->curve.x, *y + cmd->curve.y);
 
785
      *last_control_point_x = *x + cmd->curve.x2;
 
786
      *last_control_point_y = *y + cmd->curve.y2;
 
787
      *x += cmd->curve.x;
 
788
      *y += cmd->curve.y;
 
789
    }
 
790
  else
 
791
    {
 
792
      cairo_curve_to (cr, x1, y1, cmd->curve.x2, cmd->curve.y2,
 
793
                      cmd->curve.x, cmd->curve.y);
 
794
      *last_control_point_x = cmd->curve.x2;
 
795
      *last_control_point_y = cmd->curve.y2;
 
796
      *x = cmd->curve.x;
 
797
      *y = cmd->curve.y;
 
798
    }
 
799
}
 
800
 
 
801
 
 
802
static void
 
803
do_quadratic_curve_to (GooCanvasPathCommand *cmd,
 
804
                       cairo_t              *cr,
 
805
                       gdouble              *x,
 
806
                       gdouble              *y,
 
807
                       gdouble              *last_control_point_x,
 
808
                       gdouble              *last_control_point_y)
 
809
{
 
810
  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
 
811
 
 
812
  if (cmd->curve.relative)
 
813
    {
 
814
      qx1 = *x + cmd->curve.x1;
 
815
      qy1 = *y + cmd->curve.y1;
 
816
      qx2 = *x + cmd->curve.x;
 
817
      qy2 = *y + cmd->curve.y;
 
818
    }
 
819
  else
 
820
    {
 
821
      qx1 = cmd->curve.x1;
 
822
      qy1 = cmd->curve.y1;
 
823
      qx2 = cmd->curve.x;
 
824
      qy2 = cmd->curve.y;
 
825
    }
 
826
 
 
827
  /* We need to convert the quadratic into a cubic bezier. */
 
828
  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
 
829
  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
 
830
 
 
831
  x2 = x1 + (qx2 - *x) / 3.0;
 
832
  y2 = y1 + (qy2 - *y) / 3.0;
 
833
 
 
834
  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
 
835
 
 
836
  *x = qx2;
 
837
  *y = qy2;
 
838
  *last_control_point_x = qx1;
 
839
  *last_control_point_y = qy1;
 
840
}
 
841
 
 
842
 
 
843
static void
 
844
do_smooth_quadratic_curve_to (GooCanvasPathCommand    *cmd,
 
845
                              GooCanvasPathCommandType prev_cmd_type,
 
846
                              cairo_t                 *cr,
 
847
                              gdouble                 *x,
 
848
                              gdouble                 *y,
 
849
                              gdouble                 *last_control_point_x,
 
850
                              gdouble                 *last_control_point_y)
 
851
{
 
852
  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
 
853
 
 
854
  /* If the last command was a quadratic or smooth quadratic, we use
 
855
     the reflection of the last control point about the current point
 
856
     as the first control point of this curve. Otherwise we use the
 
857
     current point as the first control point. */
 
858
  if (prev_cmd_type == GOO_CANVAS_PATH_QUADRATIC_CURVE_TO
 
859
      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO)
 
860
    {
 
861
      qx1 = *x + (*x - *last_control_point_x);
 
862
      qy1 = *y + (*y - *last_control_point_y);
 
863
    }
 
864
  else
 
865
    {
 
866
      qx1 = *x;
 
867
      qy1 = *y;
 
868
    }
 
869
 
 
870
  if (cmd->curve.relative)
 
871
    {
 
872
      qx2 = *x + cmd->curve.x;
 
873
      qy2 = *y + cmd->curve.y;
 
874
    }
 
875
  else
 
876
    {
 
877
      qx2 = cmd->curve.x;
 
878
      qy2 = cmd->curve.y;
 
879
    }
 
880
 
 
881
  /* We need to convert the quadratic into a cubic bezier. */
 
882
  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
 
883
  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
 
884
 
 
885
  x2 = x1 + (qx2 - *x) / 3.0;
 
886
  y2 = y1 + (qy2 - *y) / 3.0;
 
887
 
 
888
  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
 
889
 
 
890
  *x = qx2;
 
891
  *y = qy2;
 
892
  *last_control_point_x = qx1;
 
893
  *last_control_point_y = qy1;
 
894
}
 
895
 
 
896
 
 
897
static gdouble
 
898
calc_angle (gdouble ux, gdouble uy, gdouble vx, gdouble vy)
 
899
{
 
900
  gdouble top, u_magnitude, v_magnitude, angle_cos, angle;
 
901
 
 
902
  top = ux * vx + uy * vy;
 
903
  u_magnitude = sqrt (ux * ux + uy * uy);
 
904
  v_magnitude = sqrt (vx * vx + vy * vy);
 
905
  angle_cos = top / (u_magnitude * v_magnitude);
 
906
 
 
907
  /* We check if the cosine is slightly out-of-bounds. */
 
908
  if (angle_cos >= 1.0)
 
909
    angle = 0.0;
 
910
  if (angle_cos <= -1.0)
 
911
    angle = M_PI;
 
912
  else
 
913
    angle = acos (angle_cos);
 
914
 
 
915
  if (ux * vy - uy * vx < 0)
 
916
    angle = - angle;
 
917
 
 
918
  return angle;
 
919
}
 
920
 
 
921
 
 
922
/* FIXME: Maybe we should do these calculations once when the path data is
 
923
   parsed, and keep the cairo parameters we need in the command instead. */
 
924
static void
 
925
do_elliptical_arc (GooCanvasPathCommand    *cmd,
 
926
                   cairo_t                 *cr,
 
927
                   gdouble                 *x,
 
928
                   gdouble                 *y)
 
929
{
 
930
  gdouble x1 = *x, y1 = *y, x2, y2, rx, ry, lambda;
 
931
  gdouble v1, v2, angle, angle_sin, angle_cos, x11, y11;
 
932
  gdouble rx_squared, ry_squared, x11_squared, y11_squared, top, bottom;
 
933
  gdouble c, cx1, cy1, cx, cy, start_angle, angle_delta;
 
934
 
 
935
  /* Calculate the end point of the arc - x2,y2. */
 
936
  if (cmd->arc.relative)
 
937
    {
 
938
      x2 = x1 + cmd->arc.x;
 
939
      y2 = y1 + cmd->arc.y;
 
940
    }
 
941
  else
 
942
    {
 
943
      x2 = cmd->arc.x;
 
944
      y2 = cmd->arc.y;
 
945
    }
 
946
 
 
947
  *x = x2;
 
948
  *y = y2;
 
949
 
 
950
  /* If the endpoints are exactly the same, just return (see SVG spec). */
 
951
  if (x1 == x2 && y1 == y2)
 
952
    return;
 
953
 
 
954
  /* If either rx or ry is 0, do a simple lineto (see SVG spec). */
 
955
  if (cmd->arc.rx == 0.0 || cmd->arc.ry == 0.0)
 
956
    {
 
957
      cairo_line_to (cr, x2, y2);
 
958
      return;
 
959
    }
 
960
 
 
961
  /* Calculate x1' and y1' (as per SVG implementation notes). */
 
962
  v1 = (x1 - x2) / 2.0;
 
963
  v2 = (y1 - y2) / 2.0;
 
964
 
 
965
  angle = cmd->arc.x_axis_rotation * (M_PI / 180.0);
 
966
  angle_sin = sin (angle);
 
967
  angle_cos = cos (angle);
 
968
 
 
969
  x11 = (angle_cos * v1) + (angle_sin * v2);
 
970
  y11 = - (angle_sin * v1) + (angle_cos * v2);
 
971
 
 
972
  /* Ensure rx and ry are positive and large enough. */
 
973
  rx = cmd->arc.rx > 0.0 ? cmd->arc.rx : - cmd->arc.rx;
 
974
  ry = cmd->arc.ry > 0.0 ? cmd->arc.ry : - cmd->arc.ry;
 
975
  lambda = (x11 * x11) / (rx * rx) + (y11 * y11) / (ry * ry);
 
976
  if (lambda > 1.0)
 
977
    {
 
978
      gdouble square_root = sqrt (lambda);
 
979
      rx *= square_root;
 
980
      ry *= square_root;
 
981
    }
 
982
 
 
983
  /* Calculate cx' and cy'. */
 
984
  rx_squared = rx * rx;
 
985
  ry_squared = ry * ry;
 
986
  x11_squared = x11 * x11;
 
987
  y11_squared = y11 * y11;
 
988
 
 
989
  top = (rx_squared * ry_squared) - (rx_squared * y11_squared)
 
990
    - (ry_squared * x11_squared);
 
991
  if (top < 0.0)
 
992
    {
 
993
      c = 0.0;
 
994
    }
 
995
  else
 
996
    {
 
997
      bottom = (rx_squared * y11_squared) + (ry_squared * x11_squared);
 
998
      c = sqrt (top / bottom);
 
999
    }
 
1000
 
 
1001
  if (cmd->arc.large_arc_flag == cmd->arc.sweep_flag)
 
1002
    c = - c;
 
1003
 
 
1004
  cx1 = c * ((rx * y11) / ry);
 
1005
  cy1 = c * (- (ry * x11) / rx);
 
1006
 
 
1007
  /* Calculate cx and cy. */
 
1008
  cx = (angle_cos * cx1) - (angle_sin * cy1) + (x1 + x2) / 2;
 
1009
  cy = (angle_sin * cx1) + (angle_cos * cy1) + (y1 + y2) / 2;
 
1010
 
 
1011
  /* Calculate the start and end angles. */
 
1012
  v1 = (x11 - cx1) / rx;
 
1013
  v2 = (y11 - cy1) / ry;
 
1014
 
 
1015
  start_angle = calc_angle (1, 0, v1, v2);
 
1016
  angle_delta = calc_angle (v1, v2, (-x11 - cx1) / rx, (-y11 - cy1) / ry);
 
1017
 
 
1018
  if (cmd->arc.sweep_flag == 0 && angle_delta > 0.0)
 
1019
    angle_delta -= 2 * M_PI;
 
1020
  else if (cmd->arc.sweep_flag == 1 && angle_delta < 0.0)
 
1021
    angle_delta += 2 * M_PI;
 
1022
 
 
1023
  /* Now draw the arc. */
 
1024
  cairo_save (cr);
 
1025
  cairo_translate (cr, cx, cy);
 
1026
  cairo_rotate (cr, angle);
 
1027
  cairo_scale (cr, rx, ry);
 
1028
 
 
1029
  if (angle_delta > 0.0)
 
1030
    cairo_arc (cr, 0.0, 0.0, 1.0,
 
1031
               start_angle, start_angle + angle_delta);
 
1032
  else
 
1033
    cairo_arc_negative (cr, 0.0, 0.0, 1.0,
 
1034
                        start_angle, start_angle + angle_delta);
 
1035
 
 
1036
  cairo_restore (cr);
 
1037
}
 
1038
 
 
1039
 
 
1040
/**
 
1041
 * goo_canvas_create_path:
 
1042
 * @commands: an array of #GooCanvasPathCommand.
 
1043
 * @cr: a cairo context.
 
1044
 * 
 
1045
 * Creates the path specified by the given #GooCanvasPathCommand array.
 
1046
 **/
 
1047
void
 
1048
goo_canvas_create_path (GArray              *commands,
 
1049
                        cairo_t             *cr)
 
1050
{
 
1051
  GooCanvasPathCommand *cmd;
 
1052
  GooCanvasPathCommandType prev_cmd_type = GOO_CANVAS_PATH_CLOSE_PATH;
 
1053
  gdouble x = 0, y = 0, path_start_x = 0, path_start_y = 0;
 
1054
  gdouble last_control_point_x = 0.0, last_control_point_y = 0.0;
 
1055
  gint i;
 
1056
 
 
1057
  cairo_new_path (cr);
 
1058
 
 
1059
  if (!commands || commands->len == 0)
 
1060
    return;
 
1061
 
 
1062
  for (i = 0; i < commands->len; i++)
 
1063
    {
 
1064
      cmd = &g_array_index (commands, GooCanvasPathCommand, i);
 
1065
      switch (cmd->simple.type)
 
1066
        {
 
1067
          /* Simple commands like moveto and lineto: MmZzLlHhVv. */
 
1068
        case GOO_CANVAS_PATH_MOVE_TO:
 
1069
          if (cmd->simple.relative)
 
1070
            {
 
1071
              x += cmd->simple.x;
 
1072
              y += cmd->simple.y;
 
1073
            }
 
1074
          else
 
1075
            {
 
1076
              x = cmd->simple.x;
 
1077
              y = cmd->simple.y;
 
1078
            }
 
1079
          path_start_x = x;
 
1080
          path_start_y = y;
 
1081
          cairo_move_to (cr, x, y);
 
1082
          break;
 
1083
 
 
1084
        case GOO_CANVAS_PATH_CLOSE_PATH:
 
1085
          x = path_start_x;
 
1086
          y = path_start_y;
 
1087
          cairo_close_path (cr);
 
1088
          break;
 
1089
 
 
1090
        case GOO_CANVAS_PATH_LINE_TO:
 
1091
          if (cmd->simple.relative)
 
1092
            {
 
1093
              x += cmd->simple.x;
 
1094
              y += cmd->simple.y;
 
1095
            }
 
1096
          else
 
1097
            {
 
1098
              x = cmd->simple.x;
 
1099
              y = cmd->simple.y;
 
1100
            }
 
1101
          cairo_line_to (cr, x, y);
 
1102
          break;
 
1103
 
 
1104
        case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO:
 
1105
          if (cmd->simple.relative)
 
1106
            x += cmd->simple.x;
 
1107
          else
 
1108
            x = cmd->simple.x;
 
1109
          cairo_line_to (cr, x, y);
 
1110
          break;
 
1111
 
 
1112
        case GOO_CANVAS_PATH_VERTICAL_LINE_TO:
 
1113
          if (cmd->simple.relative)
 
1114
            y += cmd->simple.y;
 
1115
          else
 
1116
            y = cmd->simple.y;
 
1117
          cairo_line_to (cr, x, y);
 
1118
          break;
 
1119
 
 
1120
          /* Bezier curve commands: CcSsQqTt. */
 
1121
        case GOO_CANVAS_PATH_CURVE_TO:
 
1122
          do_curve_to (cmd, cr, &x, &y,
 
1123
                       &last_control_point_x, &last_control_point_y);
 
1124
          break;
 
1125
 
 
1126
        case GOO_CANVAS_PATH_SMOOTH_CURVE_TO:
 
1127
          do_smooth_curve_to (cmd, prev_cmd_type, cr, &x, &y,
 
1128
                              &last_control_point_x, &last_control_point_y);
 
1129
          break;
 
1130
 
 
1131
        case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO:
 
1132
          do_quadratic_curve_to (cmd, cr, &x, &y,
 
1133
                                 &last_control_point_x, &last_control_point_y);
 
1134
          break;
 
1135
 
 
1136
        case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO:
 
1137
          do_smooth_quadratic_curve_to (cmd, prev_cmd_type, cr, &x, &y,
 
1138
                                        &last_control_point_x,
 
1139
                                        &last_control_point_y);
 
1140
          break;
 
1141
 
 
1142
          /* The elliptical arc commands: Aa. */
 
1143
        case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
 
1144
          do_elliptical_arc (cmd, cr, &x, &y);
 
1145
          break;
 
1146
        }
 
1147
 
 
1148
      prev_cmd_type = cmd->simple.type;
 
1149
    }
 
1150
}
 
1151
 
 
1152
 
 
1153
/* This is a copy of _gtk_boolean_handled_accumulator. */
 
1154
gboolean
 
1155
goo_canvas_boolean_handled_accumulator (GSignalInvocationHint *ihint,
 
1156
                                        GValue                *return_accu,
 
1157
                                        const GValue          *handler_return,
 
1158
                                        gpointer               dummy)
 
1159
{
 
1160
  gboolean continue_emission;
 
1161
  gboolean signal_handled;
 
1162
  
 
1163
  signal_handled = g_value_get_boolean (handler_return);
 
1164
  g_value_set_boolean (return_accu, signal_handled);
 
1165
  continue_emission = !signal_handled;
 
1166
  
 
1167
  return continue_emission;
 
1168
}
 
1169
 
 
1170
 
 
1171
static GooCanvasBounds *
 
1172
goo_canvas_bounds_copy (const GooCanvasBounds *bounds)
 
1173
{
 
1174
  GooCanvasBounds *result = g_new (GooCanvasBounds, 1);
 
1175
  *result = *bounds;
 
1176
 
 
1177
  return result;
 
1178
}
 
1179
 
 
1180
GType
 
1181
goo_canvas_bounds_get_type (void)
 
1182
{
 
1183
  static GType our_type = 0;
 
1184
  
 
1185
  if (our_type == 0)
 
1186
    our_type = g_boxed_type_register_static
 
1187
      ("GooCanvasBounds",
 
1188
       (GBoxedCopyFunc) goo_canvas_bounds_copy,
 
1189
       (GBoxedFreeFunc) g_free);
 
1190
 
 
1191
  return our_type;
 
1192
}
 
1193
 
 
1194
 
 
1195
/* Converts red, green, blue and alpha doubles to an RGBA guint. */
 
1196
guint
 
1197
goo_canvas_convert_colors_to_rgba (double red,
 
1198
                                   double green,
 
1199
                                   double blue,
 
1200
                                   double alpha)
 
1201
{
 
1202
  guint red_byte, green_byte, blue_byte, alpha_byte;
 
1203
 
 
1204
  red_byte = red * 256;
 
1205
  red_byte -= red_byte >> 8;
 
1206
 
 
1207
  green_byte = green * 256;
 
1208
  green_byte -= green_byte >> 8;
 
1209
 
 
1210
  blue_byte = blue * 256;
 
1211
  blue_byte -= blue_byte >> 8;
 
1212
 
 
1213
  alpha_byte = alpha * 256;
 
1214
  alpha_byte -= alpha_byte >> 8;
 
1215
 
 
1216
  return (red_byte << 24) + (green_byte << 16) + (blue_byte << 8) + alpha_byte;
 
1217
}
 
1218
 
 
1219
 
 
1220
void
 
1221
goo_canvas_get_rgba_value_from_pattern (cairo_pattern_t *pattern,
 
1222
                                        GValue          *value)
 
1223
{
 
1224
  double red, green, blue, alpha;
 
1225
  guint rgba = 0;
 
1226
 
 
1227
  if (pattern && cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID)
 
1228
    {
 
1229
      cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha);
 
1230
      rgba = goo_canvas_convert_colors_to_rgba (red, green, blue, alpha);
 
1231
    }
 
1232
  g_value_set_uint (value, rgba);
 
1233
}
 
1234
 
 
1235
 
 
1236
/* Sets a style property to the given pattern, taking ownership of it. */
 
1237
void
 
1238
goo_canvas_set_style_property_from_pattern (GooCanvasStyle  *style,
 
1239
                                            GQuark           property_id,
 
1240
                                            cairo_pattern_t *pattern)
 
1241
{
 
1242
  GValue tmpval = { 0 };
 
1243
 
 
1244
  g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN);
 
1245
  g_value_take_boxed (&tmpval, pattern);
 
1246
  goo_canvas_style_set_property (style, property_id, &tmpval);
 
1247
  g_value_unset (&tmpval);
 
1248
}
 
1249
 
 
1250
 
 
1251
cairo_pattern_t*
 
1252
goo_canvas_create_pattern_from_color_value (const GValue *value)
 
1253
{
 
1254
  GdkColor color = { 0, 0, 0, 0, };
 
1255
 
 
1256
  if (g_value_get_string (value))
 
1257
    gdk_color_parse (g_value_get_string (value), &color);
 
1258
 
 
1259
  return cairo_pattern_create_rgb (color.red / 65535.0,
 
1260
                                   color.green / 65535.0,
 
1261
                                   color.blue / 65535.0);
 
1262
}
 
1263
  
 
1264
 
 
1265
cairo_pattern_t*
 
1266
goo_canvas_create_pattern_from_rgba_value (const GValue *value)
 
1267
{
 
1268
  guint rgba, red, green, blue, alpha;
 
1269
 
 
1270
  rgba = g_value_get_uint (value);
 
1271
  red   = (rgba >> 24) & 0xFF;
 
1272
  green = (rgba >> 16) & 0xFF;
 
1273
  blue  = (rgba >> 8)  & 0xFF;
 
1274
  alpha = (rgba)       & 0xFF;
 
1275
 
 
1276
  return cairo_pattern_create_rgba (red / 255.0, green / 255.0,
 
1277
                                    blue / 255.0, alpha / 255.0);
 
1278
}
 
1279
 
 
1280
 
 
1281
cairo_pattern_t*
 
1282
goo_canvas_create_pattern_from_pixbuf_value (const GValue *value)
 
1283
{
 
1284
  GdkPixbuf *pixbuf;
 
1285
  cairo_surface_t *surface;
 
1286
  cairo_pattern_t *pattern;
 
1287
 
 
1288
  pixbuf = g_value_get_object (value);
 
1289
  surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf);
 
1290
  pattern = cairo_pattern_create_for_surface (surface);
 
1291
  cairo_surface_destroy (surface);
 
1292
  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
 
1293
  return pattern;
 
1294
}