2
* GooCanvas. Copyright (C) 2005 Damon Chaplin.
3
* Released under the GNU LGPL license. See COPYING for details.
5
* goocanvasutils.c - utility functions.
9
* SECTION:goocanvasutils
10
* @Title: GooCanvas Types
11
* @Short_Description: types used in GooCanvas.
13
* This section describes the types used throughout GooCanvas.
18
#include "goocanvas.h"
21
/* Glib doesn't provide a g_ptr_array_index() so we need our own one. */
23
goo_canvas_util_ptr_array_insert (GPtrArray *ptr_array,
29
/* Add the pointer at the end so there is enough room. */
30
g_ptr_array_add (ptr_array, data);
32
/* If index is -1, we are done. */
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];
40
/* Put the new element in its proper place. */
41
ptr_array->pdata[index] = data;
45
/* Glib doesn't provide a g_ptr_array_move() so we need our own one. */
47
goo_canvas_util_ptr_array_move (GPtrArray *ptr_array,
54
data = ptr_array->pdata[old_index];
56
if (new_index > old_index)
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];
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];
69
ptr_array->pdata[new_index] = data;
73
/* Glib doesn't provide a g_ptr_array_move() so we need our own one. */
75
goo_canvas_util_ptr_array_find_index (GPtrArray *ptr_array,
80
for (i = 0; i < ptr_array->len; i++)
82
if (ptr_array->pdata[i] == data)
95
goo_canvas_cairo_surface_from_pixbuf (GdkPixbuf *pixbuf)
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;
109
format = CAIRO_FORMAT_RGB24;
111
format = CAIRO_FORMAT_ARGB32;
113
cairo_pixels = g_malloc (4 * width * height);
114
surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels,
116
width, height, 4 * width);
117
cairo_surface_set_user_data (surface, &key,
118
cairo_pixels, (cairo_destroy_func_t)g_free);
120
for (j = height; j; j--)
122
guchar *p = gdk_pixels;
123
guchar *q = cairo_pixels;
127
guchar *end = p + 3 * width;
131
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
146
guchar *end = p + 4 * width;
149
#define MULT(d,c,a,t) G_STMT_START { t = c * a; d = ((t >> 8) + t) >> 8; } G_STMT_END
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);
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);
172
gdk_pixels += gdk_rowstride;
173
cairo_pixels += 4 * width;
181
goo_canvas_cairo_pattern_from_pixbuf (GdkPixbuf *pixbuf)
183
cairo_surface_t *surface;
184
cairo_pattern_t *pattern;
186
surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf);
187
pattern = cairo_pattern_create_for_surface (surface);
188
cairo_surface_destroy (surface);
198
goo_cairo_pattern_get_type (void)
200
static GType cairo_pattern_type = 0;
202
if (cairo_pattern_type == 0)
203
cairo_pattern_type = g_boxed_type_register_static
205
(GBoxedCopyFunc) cairo_pattern_reference,
206
(GBoxedFreeFunc) cairo_pattern_destroy);
208
return cairo_pattern_type;
213
goo_cairo_fill_rule_get_type (void)
215
static GType 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" },
222
etype = g_enum_register_static ("GooCairoFillRule", values);
229
goo_cairo_operator_get_type (void)
231
static GType etype = 0;
233
static const GEnumValue values[] = {
234
{ CAIRO_OPERATOR_CLEAR, "CAIRO_OPERATOR_CLEAR", "clear" },
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" },
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" },
248
{ CAIRO_OPERATOR_XOR, "CAIRO_OPERATOR_XOR", "xor" },
249
{ CAIRO_OPERATOR_ADD, "CAIRO_OPERATOR_ADD", "add" },
250
{ CAIRO_OPERATOR_SATURATE, "CAIRO_OPERATOR_SATURATE", "saturate" },
253
etype = g_enum_register_static ("GooCairoOperator", values);
260
goo_cairo_antialias_get_type (void)
262
static GType 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" },
271
etype = g_enum_register_static ("GooCairoAntialias", values);
278
goo_cairo_line_cap_get_type (void)
280
static GType 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" },
288
etype = g_enum_register_static ("GooCairoLineCap", values);
295
goo_cairo_line_join_get_type (void)
297
static GType 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" },
305
etype = g_enum_register_static ("GooCairoLineJoin", values);
312
goo_cairo_hint_metrics_get_type (void)
314
static GType 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" },
322
etype = g_enum_register_static ("GooCairoHintMetrics", values);
329
* goo_canvas_line_dash_ref:
330
* @dash: a #GooCanvasLineDash.
332
* Increments the reference count of the dash pattern.
334
* Returns: the dash pattern.
337
goo_canvas_line_dash_ref (GooCanvasLineDash *dash)
346
* goo_canvas_line_dash_unref:
347
* @dash: a #GooCanvasLineDash.
349
* Decrements the reference count of the dash pattern. If it falls to 0
353
goo_canvas_line_dash_unref (GooCanvasLineDash *dash)
355
if (dash && --dash->ref_count == 0) {
356
g_free (dash->dashes);
363
goo_canvas_line_dash_get_type (void)
365
static GType cairo_line_dash_type = 0;
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);
373
return cairo_line_dash_type;
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.
382
* Creates a new dash pattern.
384
* Returns: a new dash pattern.
387
goo_canvas_line_dash_new (gint num_dashes,
390
GooCanvasLineDash *dash;
394
dash = g_new (GooCanvasLineDash, 1);
396
dash->num_dashes = num_dashes;
397
dash->dashes = g_new (double, num_dashes);
398
dash->dash_offset = 0.0;
400
va_start (var_args, num_dashes);
402
for (i = 0; i < num_dashes; i++)
404
dash->dashes[i] = va_arg (var_args, double);
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
418
* Creates a new dash pattern. Takes ownership of the @dashes vector.
420
* Returns: a new dash pattern.
423
goo_canvas_line_dash_newv (gint num_dashes,
426
GooCanvasLineDash *dash;
428
dash = g_new (GooCanvasLineDash, 1);
430
dash->num_dashes = num_dashes;
431
dash->dashes = dashes;
432
dash->dash_offset = 0.0;
438
goo_cairo_matrix_copy (const cairo_matrix_t *matrix)
440
cairo_matrix_t *matrix_copy;
445
matrix_copy = g_slice_new (cairo_matrix_t);
446
*matrix_copy = *matrix;
453
goo_cairo_matrix_free (cairo_matrix_t *matrix)
455
g_slice_free (cairo_matrix_t, matrix);
460
goo_cairo_matrix_get_type (void)
462
static GType type_cairo_matrix = 0;
464
if (!type_cairo_matrix)
465
type_cairo_matrix = g_boxed_type_register_static
467
(GBoxedCopyFunc) goo_cairo_matrix_copy,
468
(GBoxedFreeFunc) goo_cairo_matrix_free);
470
return type_cairo_matrix;
474
/* This is a semi-private function to help gtk-doc find child properties. */
476
goo_canvas_query_child_properties (gpointer class,
479
if (!G_TYPE_IS_CLASSED (G_TYPE_FROM_CLASS (class)))
482
if (g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM))
483
return goo_canvas_item_class_list_child_properties (class,
486
if (g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM_MODEL))
487
return goo_canvas_item_model_class_list_child_properties (class,
495
parse_double (gchar **pos,
501
/* If an error has already occurred, just return. */
505
/* Skip whitespace and commas. */
507
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
510
/* Parse the double, and set pos to the first char after it. */
511
result = g_ascii_strtod (p, pos);
513
/* If no characters were parsed, set the error flag. */
522
parse_flag (gchar **pos,
528
/* If an error has already occurred, just return. */
532
/* Skip whitespace and commas. */
534
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
537
/* The flag must be a '0' or a '1'. */
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.
560
* Parses the given SVG path specification string.
562
* Returns: a #GArray of #GooCanvasPathCommand elements.
565
goo_canvas_parse_path_data (const gchar *path_data)
568
GooCanvasPathCommand cmd;
569
gchar *pos, command = 0, next_command;
570
gboolean error = FALSE;
572
commands = g_array_new (0, 0, sizeof (GooCanvasPathCommand));
577
pos = (gchar*) path_data;
580
while (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n')
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
590
if ((next_command < 'a' || next_command > 'z')
591
&& (next_command < 'A' || next_command > 'Z'))
593
/* If this is the first command, then set the error flag and assume
594
a simple close-path command. */
600
/* moveto commands change to lineto. */
601
else if (command == 'm')
603
else if (command == 'M')
608
command = next_command;
612
cmd.simple.relative = 0;
615
/* Simple commands like moveto and lineto: MmZzLlHhVv. */
617
cmd.simple.relative = 1;
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);
626
cmd.simple.type = GOO_CANVAS_PATH_CLOSE_PATH;
630
cmd.simple.relative = 1;
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);
638
cmd.simple.relative = 1;
640
cmd.simple.type = GOO_CANVAS_PATH_HORIZONTAL_LINE_TO;
641
cmd.simple.x = parse_double (&pos, &error);
645
cmd.simple.relative = 1;
647
cmd.simple.type = GOO_CANVAS_PATH_VERTICAL_LINE_TO;
648
cmd.simple.y = parse_double (&pos, &error);
651
/* Bezier curve commands: CcSsQqTt. */
653
cmd.curve.relative = 1;
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);
665
cmd.curve.relative = 1;
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);
675
cmd.curve.relative = 1;
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);
685
cmd.curve.relative = 1;
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);
692
/* The elliptical arc commands: Aa. */
694
cmd.arc.relative = 1;
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);
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. */
716
g_array_append_val (commands, cmd);
724
do_curve_to (GooCanvasPathCommand *cmd,
728
gdouble *last_control_point_x,
729
gdouble *last_control_point_y)
731
if (cmd->curve.relative)
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;
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;
755
do_smooth_curve_to (GooCanvasPathCommand *cmd,
756
GooCanvasPathCommandType prev_cmd_type,
760
gdouble *last_control_point_x,
761
gdouble *last_control_point_y)
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)
772
x1 = *x + (*x - *last_control_point_x);
773
y1 = *y + (*y - *last_control_point_y);
781
if (cmd->curve.relative)
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;
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;
803
do_quadratic_curve_to (GooCanvasPathCommand *cmd,
807
gdouble *last_control_point_x,
808
gdouble *last_control_point_y)
810
gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
812
if (cmd->curve.relative)
814
qx1 = *x + cmd->curve.x1;
815
qy1 = *y + cmd->curve.y1;
816
qx2 = *x + cmd->curve.x;
817
qy2 = *y + cmd->curve.y;
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;
831
x2 = x1 + (qx2 - *x) / 3.0;
832
y2 = y1 + (qy2 - *y) / 3.0;
834
cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
838
*last_control_point_x = qx1;
839
*last_control_point_y = qy1;
844
do_smooth_quadratic_curve_to (GooCanvasPathCommand *cmd,
845
GooCanvasPathCommandType prev_cmd_type,
849
gdouble *last_control_point_x,
850
gdouble *last_control_point_y)
852
gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
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)
861
qx1 = *x + (*x - *last_control_point_x);
862
qy1 = *y + (*y - *last_control_point_y);
870
if (cmd->curve.relative)
872
qx2 = *x + cmd->curve.x;
873
qy2 = *y + cmd->curve.y;
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;
885
x2 = x1 + (qx2 - *x) / 3.0;
886
y2 = y1 + (qy2 - *y) / 3.0;
888
cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
892
*last_control_point_x = qx1;
893
*last_control_point_y = qy1;
898
calc_angle (gdouble ux, gdouble uy, gdouble vx, gdouble vy)
900
gdouble top, u_magnitude, v_magnitude, angle_cos, angle;
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);
907
/* We check if the cosine is slightly out-of-bounds. */
908
if (angle_cos >= 1.0)
910
if (angle_cos <= -1.0)
913
angle = acos (angle_cos);
915
if (ux * vy - uy * vx < 0)
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. */
925
do_elliptical_arc (GooCanvasPathCommand *cmd,
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;
935
/* Calculate the end point of the arc - x2,y2. */
936
if (cmd->arc.relative)
938
x2 = x1 + cmd->arc.x;
939
y2 = y1 + cmd->arc.y;
950
/* If the endpoints are exactly the same, just return (see SVG spec). */
951
if (x1 == x2 && y1 == y2)
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)
957
cairo_line_to (cr, x2, y2);
961
/* Calculate x1' and y1' (as per SVG implementation notes). */
962
v1 = (x1 - x2) / 2.0;
963
v2 = (y1 - y2) / 2.0;
965
angle = cmd->arc.x_axis_rotation * (M_PI / 180.0);
966
angle_sin = sin (angle);
967
angle_cos = cos (angle);
969
x11 = (angle_cos * v1) + (angle_sin * v2);
970
y11 = - (angle_sin * v1) + (angle_cos * v2);
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);
978
gdouble square_root = sqrt (lambda);
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;
989
top = (rx_squared * ry_squared) - (rx_squared * y11_squared)
990
- (ry_squared * x11_squared);
997
bottom = (rx_squared * y11_squared) + (ry_squared * x11_squared);
998
c = sqrt (top / bottom);
1001
if (cmd->arc.large_arc_flag == cmd->arc.sweep_flag)
1004
cx1 = c * ((rx * y11) / ry);
1005
cy1 = c * (- (ry * x11) / rx);
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;
1011
/* Calculate the start and end angles. */
1012
v1 = (x11 - cx1) / rx;
1013
v2 = (y11 - cy1) / ry;
1015
start_angle = calc_angle (1, 0, v1, v2);
1016
angle_delta = calc_angle (v1, v2, (-x11 - cx1) / rx, (-y11 - cy1) / ry);
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;
1023
/* Now draw the arc. */
1025
cairo_translate (cr, cx, cy);
1026
cairo_rotate (cr, angle);
1027
cairo_scale (cr, rx, ry);
1029
if (angle_delta > 0.0)
1030
cairo_arc (cr, 0.0, 0.0, 1.0,
1031
start_angle, start_angle + angle_delta);
1033
cairo_arc_negative (cr, 0.0, 0.0, 1.0,
1034
start_angle, start_angle + angle_delta);
1041
* goo_canvas_create_path:
1042
* @commands: an array of #GooCanvasPathCommand.
1043
* @cr: a cairo context.
1045
* Creates the path specified by the given #GooCanvasPathCommand array.
1048
goo_canvas_create_path (GArray *commands,
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;
1057
cairo_new_path (cr);
1059
if (!commands || commands->len == 0)
1062
for (i = 0; i < commands->len; i++)
1064
cmd = &g_array_index (commands, GooCanvasPathCommand, i);
1065
switch (cmd->simple.type)
1067
/* Simple commands like moveto and lineto: MmZzLlHhVv. */
1068
case GOO_CANVAS_PATH_MOVE_TO:
1069
if (cmd->simple.relative)
1081
cairo_move_to (cr, x, y);
1084
case GOO_CANVAS_PATH_CLOSE_PATH:
1087
cairo_close_path (cr);
1090
case GOO_CANVAS_PATH_LINE_TO:
1091
if (cmd->simple.relative)
1101
cairo_line_to (cr, x, y);
1104
case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO:
1105
if (cmd->simple.relative)
1109
cairo_line_to (cr, x, y);
1112
case GOO_CANVAS_PATH_VERTICAL_LINE_TO:
1113
if (cmd->simple.relative)
1117
cairo_line_to (cr, x, y);
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);
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);
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);
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);
1142
/* The elliptical arc commands: Aa. */
1143
case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
1144
do_elliptical_arc (cmd, cr, &x, &y);
1148
prev_cmd_type = cmd->simple.type;
1153
/* This is a copy of _gtk_boolean_handled_accumulator. */
1155
goo_canvas_boolean_handled_accumulator (GSignalInvocationHint *ihint,
1156
GValue *return_accu,
1157
const GValue *handler_return,
1160
gboolean continue_emission;
1161
gboolean signal_handled;
1163
signal_handled = g_value_get_boolean (handler_return);
1164
g_value_set_boolean (return_accu, signal_handled);
1165
continue_emission = !signal_handled;
1167
return continue_emission;
1171
static GooCanvasBounds *
1172
goo_canvas_bounds_copy (const GooCanvasBounds *bounds)
1174
GooCanvasBounds *result = g_new (GooCanvasBounds, 1);
1181
goo_canvas_bounds_get_type (void)
1183
static GType our_type = 0;
1186
our_type = g_boxed_type_register_static
1188
(GBoxedCopyFunc) goo_canvas_bounds_copy,
1189
(GBoxedFreeFunc) g_free);
1195
/* Converts red, green, blue and alpha doubles to an RGBA guint. */
1197
goo_canvas_convert_colors_to_rgba (double red,
1202
guint red_byte, green_byte, blue_byte, alpha_byte;
1204
red_byte = red * 256;
1205
red_byte -= red_byte >> 8;
1207
green_byte = green * 256;
1208
green_byte -= green_byte >> 8;
1210
blue_byte = blue * 256;
1211
blue_byte -= blue_byte >> 8;
1213
alpha_byte = alpha * 256;
1214
alpha_byte -= alpha_byte >> 8;
1216
return (red_byte << 24) + (green_byte << 16) + (blue_byte << 8) + alpha_byte;
1221
goo_canvas_get_rgba_value_from_pattern (cairo_pattern_t *pattern,
1224
double red, green, blue, alpha;
1227
if (pattern && cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID)
1229
cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha);
1230
rgba = goo_canvas_convert_colors_to_rgba (red, green, blue, alpha);
1232
g_value_set_uint (value, rgba);
1236
/* Sets a style property to the given pattern, taking ownership of it. */
1238
goo_canvas_set_style_property_from_pattern (GooCanvasStyle *style,
1240
cairo_pattern_t *pattern)
1242
GValue tmpval = { 0 };
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);
1252
goo_canvas_create_pattern_from_color_value (const GValue *value)
1254
GdkColor color = { 0, 0, 0, 0, };
1256
if (g_value_get_string (value))
1257
gdk_color_parse (g_value_get_string (value), &color);
1259
return cairo_pattern_create_rgb (color.red / 65535.0,
1260
color.green / 65535.0,
1261
color.blue / 65535.0);
1266
goo_canvas_create_pattern_from_rgba_value (const GValue *value)
1268
guint rgba, red, green, blue, alpha;
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;
1276
return cairo_pattern_create_rgba (red / 255.0, green / 255.0,
1277
blue / 255.0, alpha / 255.0);
1282
goo_canvas_create_pattern_from_pixbuf_value (const GValue *value)
1285
cairo_surface_t *surface;
1286
cairo_pattern_t *pattern;
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);