1 /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright © 2002 University of Southern California
5 * Copyright © 2005 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it either under the terms of the GNU Lesser General Public
9 * License version 2.1 as published by the Free Software Foundation
10 * (the "LGPL") or, at your option, under the terms of the Mozilla
11 * Public License Version 1.1 (the "MPL"). If you do not alter this
12 * notice, a recipient may use your version of this file under either
13 * the MPL or the LGPL.
15 * You should have received a copy of the LGPL along with this library
16 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
21 * The contents of this file are subject to the Mozilla Public License
22 * Version 1.1 (the "License"); you may not use this file except in
23 * compliance with the License. You may obtain a copy of the License at
24 * http://www.mozilla.org/MPL/
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28 * the specific language governing rights and limitations.
30 * The Original Code is the cairo graphics library.
32 * The Initial Developer of the Original Code is University of Southern
36 * Carl D. Worth <cworth@cworth.org>
41 #include "cairo-path-fixed-private.h"
43 /* private functions */
45 _cairo_path_fixed_add (cairo_path_fixed_t
*path
,
47 cairo_point_t
*points
,
51 _cairo_path_fixed_add_buf (cairo_path_fixed_t
*path
,
52 cairo_path_buf_t
*buf
);
54 static cairo_path_buf_t
*
55 _cairo_path_buf_create (int buf_size
);
58 _cairo_path_buf_destroy (cairo_path_buf_t
*buf
);
61 _cairo_path_buf_add_op (cairo_path_buf_t
*buf
,
65 _cairo_path_buf_add_points (cairo_path_buf_t
*buf
,
66 cairo_point_t
*points
,
70 _cairo_path_fixed_init (cairo_path_fixed_t
*path
)
72 VG (VALGRIND_MAKE_MEM_UNDEFINED (path
, sizeof (cairo_path_fixed_t
)));
74 path
->buf_head
.base
.next
= NULL
;
75 path
->buf_head
.base
.prev
= NULL
;
76 path
->buf_tail
= &path
->buf_head
.base
;
78 path
->buf_head
.base
.num_ops
= 0;
79 path
->buf_head
.base
.num_points
= 0;
80 path
->buf_head
.base
.buf_size
= CAIRO_PATH_BUF_SIZE
;
81 path
->buf_head
.base
.op
= path
->buf_head
.op
;
82 path
->buf_head
.base
.points
= path
->buf_head
.points
;
84 path
->current_point
.x
= 0;
85 path
->current_point
.y
= 0;
86 path
->has_current_point
= FALSE
;
87 path
->has_curve_to
= FALSE
;
88 path
->last_move_point
= path
->current_point
;
92 _cairo_path_fixed_init_copy (cairo_path_fixed_t
*path
,
93 cairo_path_fixed_t
*other
)
95 cairo_path_buf_t
*buf
, *other_buf
;
96 unsigned int num_points
, num_ops
, buf_size
;
98 _cairo_path_fixed_init (path
);
100 path
->current_point
= other
->current_point
;
101 path
->has_current_point
= other
->has_current_point
;
102 path
->has_curve_to
= other
->has_curve_to
;
103 path
->last_move_point
= other
->last_move_point
;
105 path
->buf_head
.base
.num_ops
= other
->buf_head
.base
.num_ops
;
106 path
->buf_head
.base
.num_points
= other
->buf_head
.base
.num_points
;
107 path
->buf_head
.base
.buf_size
= other
->buf_head
.base
.buf_size
;
108 memcpy (path
->buf_head
.op
, other
->buf_head
.base
.op
,
109 other
->buf_head
.base
.num_ops
* sizeof (other
->buf_head
.op
[0]));
110 memcpy (path
->buf_head
.points
, other
->buf_head
.points
,
111 other
->buf_head
.base
.num_points
* sizeof (other
->buf_head
.points
[0]));
113 num_points
= num_ops
= 0;
114 for (other_buf
= other
->buf_head
.base
.next
;
116 other_buf
= other_buf
->next
)
118 num_ops
+= other_buf
->num_ops
;
119 num_points
+= other_buf
->num_points
;
122 buf_size
= MAX (num_ops
, (num_points
+ 1) / 2);
124 buf
= _cairo_path_buf_create (buf_size
);
125 if (unlikely (buf
== NULL
)) {
126 _cairo_path_fixed_fini (path
);
127 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
130 for (other_buf
= other
->buf_head
.base
.next
;
132 other_buf
= other_buf
->next
)
134 memcpy (buf
->op
+ buf
->num_ops
, other_buf
->op
,
135 other_buf
->num_ops
* sizeof (buf
->op
[0]));
136 buf
->num_ops
+= other_buf
->num_ops
;
138 memcpy (buf
->points
+ buf
->num_points
, other_buf
->points
,
139 other_buf
->num_points
* sizeof (buf
->points
[0]));
140 buf
->num_points
+= other_buf
->num_points
;
143 _cairo_path_fixed_add_buf (path
, buf
);
146 return CAIRO_STATUS_SUCCESS
;
150 _cairo_path_fixed_hash (const cairo_path_fixed_t
*path
)
152 unsigned long hash
= 0;
153 const cairo_path_buf_t
*buf
;
154 int num_points
, num_ops
;
156 hash
= _cairo_hash_bytes (hash
,
157 &path
->current_point
,
158 sizeof (path
->current_point
));
159 hash
= _cairo_hash_bytes (hash
,
160 &path
->last_move_point
,
161 sizeof (path
->last_move_point
));
163 num_ops
= path
->buf_head
.base
.num_ops
;
164 num_points
= path
->buf_head
.base
.num_points
;
165 for (buf
= path
->buf_head
.base
.next
;
169 hash
= _cairo_hash_bytes (hash
, buf
->op
,
170 buf
->num_ops
* sizeof (buf
->op
[0]));
171 hash
= _cairo_hash_bytes (hash
, buf
->points
,
172 buf
->num_points
* sizeof (buf
->points
[0]));
174 num_ops
+= buf
->num_ops
;
175 num_points
+= buf
->num_points
;
178 hash
= _cairo_hash_bytes (hash
, &num_ops
, sizeof (num_ops
));
179 hash
= _cairo_hash_bytes (hash
, &num_points
, sizeof (num_points
));
185 _cairo_path_fixed_size (const cairo_path_fixed_t
*path
)
187 const cairo_path_buf_t
*buf
;
188 int num_points
, num_ops
;
190 num_ops
= path
->buf_head
.base
.num_ops
;
191 num_points
= path
->buf_head
.base
.num_points
;
192 for (buf
= path
->buf_head
.base
.next
;
196 num_ops
+= buf
->num_ops
;
197 num_points
+= buf
->num_points
;
200 return num_ops
* sizeof (buf
->op
[0]) +
201 num_points
* sizeof (buf
->points
[0]);
205 _cairo_path_fixed_equal (const cairo_path_fixed_t
*a
,
206 const cairo_path_fixed_t
*b
)
208 const cairo_path_buf_t
*buf_a
, *buf_b
;
209 const cairo_path_op_t
*ops_a
, *ops_b
;
210 const cairo_point_t
*points_a
, *points_b
;
211 int num_points_a
, num_ops_a
;
212 int num_points_b
, num_ops_b
;
218 num_ops_a
= a
->buf_head
.base
.num_ops
;
219 num_points_a
= a
->buf_head
.base
.num_points
;
220 for (buf_a
= a
->buf_head
.base
.next
;
224 num_ops_a
+= buf_a
->num_ops
;
225 num_points_a
+= buf_a
->num_points
;
228 num_ops_a
= num_points_a
= 0;
231 num_ops_b
= b
->buf_head
.base
.num_ops
;
232 num_points_b
= b
->buf_head
.base
.num_points
;
233 for (buf_b
= b
->buf_head
.base
.next
;
237 num_ops_b
+= buf_b
->num_ops
;
238 num_points_b
+= buf_b
->num_points
;
241 num_ops_b
= num_points_b
= 0;
243 if (num_ops_a
== 0 && num_ops_b
== 0)
246 if (num_ops_a
!= num_ops_b
|| num_points_a
!= num_points_b
)
249 assert (a
!= NULL
&& b
!= NULL
);
251 buf_a
= &a
->buf_head
.base
;
252 num_points_a
= buf_a
->num_points
;
253 num_ops_a
= buf_a
->num_ops
;
255 points_a
= buf_a
->points
;
257 buf_b
= &b
->buf_head
.base
;
258 num_points_b
= buf_b
->num_points
;
259 num_ops_b
= buf_b
->num_ops
;
261 points_b
= buf_b
->points
;
264 int num_ops
= MIN (num_ops_a
, num_ops_b
);
265 int num_points
= MIN (num_points_a
, num_points_b
);
267 if (memcmp (ops_a
, ops_b
, num_ops
* sizeof (cairo_path_op_t
)))
269 if (memcmp (points_a
, points_b
, num_points
* sizeof (cairo_point_t
)))
272 num_ops_a
-= num_ops
;
274 num_points_a
-= num_points
;
275 points_a
+= num_points
;
276 if (num_ops_a
== 0 || num_points_a
== 0) {
277 if (num_ops_a
|| num_points_a
)
284 num_points_a
= buf_a
->num_points
;
285 num_ops_a
= buf_a
->num_ops
;
287 points_a
= buf_a
->points
;
290 num_ops_b
-= num_ops
;
292 num_points_b
-= num_points
;
293 points_b
+= num_points
;
294 if (num_ops_b
== 0 || num_points_b
== 0) {
295 if (num_ops_b
|| num_points_b
)
302 num_points_b
= buf_b
->num_points
;
303 num_ops_b
= buf_b
->num_ops
;
305 points_b
= buf_b
->points
;
314 _cairo_path_fixed_create (void)
316 cairo_path_fixed_t
*path
;
318 path
= malloc (sizeof (cairo_path_fixed_t
));
320 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY
);
324 _cairo_path_fixed_init (path
);
329 _cairo_path_fixed_fini (cairo_path_fixed_t
*path
)
331 cairo_path_buf_t
*buf
;
333 buf
= path
->buf_head
.base
.next
;
335 cairo_path_buf_t
*this = buf
;
337 _cairo_path_buf_destroy (this);
340 VG (VALGRIND_MAKE_MEM_NOACCESS (path
, sizeof (cairo_path_fixed_t
)));
344 _cairo_path_fixed_destroy (cairo_path_fixed_t
*path
)
346 _cairo_path_fixed_fini (path
);
351 _cairo_path_fixed_move_to (cairo_path_fixed_t
*path
,
355 cairo_status_t status
;
361 /* If the previous op was also a MOVE_TO, then just change its
362 * point rather than adding a new op. */
363 if (path
->buf_tail
&& path
->buf_tail
->num_ops
&&
364 path
->buf_tail
->op
[path
->buf_tail
->num_ops
- 1] == CAIRO_PATH_OP_MOVE_TO
)
366 cairo_point_t
*last_move_to_point
;
367 last_move_to_point
= &path
->buf_tail
->points
[path
->buf_tail
->num_points
- 1];
368 *last_move_to_point
= point
;
370 status
= _cairo_path_fixed_add (path
, CAIRO_PATH_OP_MOVE_TO
, &point
, 1);
371 if (unlikely (status
))
375 path
->current_point
= point
;
376 path
->has_current_point
= TRUE
;
377 path
->last_move_point
= path
->current_point
;
379 return CAIRO_STATUS_SUCCESS
;
383 _cairo_path_fixed_new_sub_path (cairo_path_fixed_t
*path
)
385 path
->has_current_point
= FALSE
;
389 _cairo_path_fixed_rel_move_to (cairo_path_fixed_t
*path
,
395 if (! path
->has_current_point
)
396 return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT
);
398 x
= path
->current_point
.x
+ dx
;
399 y
= path
->current_point
.y
+ dy
;
401 return _cairo_path_fixed_move_to (path
, x
, y
);
405 _cairo_path_fixed_line_to (cairo_path_fixed_t
*path
,
409 cairo_status_t status
;
415 /* When there is not yet a current point, the line_to operation
416 * becomes a move_to instead. Note: We have to do this by
417 * explicitly calling into _cairo_path_fixed_move_to to ensure
418 * that the last_move_point state is updated properly.
420 if (! path
->has_current_point
)
421 status
= _cairo_path_fixed_move_to (path
, point
.x
, point
.y
);
423 status
= _cairo_path_fixed_add (path
, CAIRO_PATH_OP_LINE_TO
, &point
, 1);
425 if (unlikely (status
))
428 path
->current_point
= point
;
429 path
->has_current_point
= TRUE
;
431 return CAIRO_STATUS_SUCCESS
;
435 _cairo_path_fixed_rel_line_to (cairo_path_fixed_t
*path
,
441 if (! path
->has_current_point
)
442 return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT
);
444 x
= path
->current_point
.x
+ dx
;
445 y
= path
->current_point
.y
+ dy
;
447 return _cairo_path_fixed_line_to (path
, x
, y
);
451 _cairo_path_fixed_curve_to (cairo_path_fixed_t
*path
,
452 cairo_fixed_t x0
, cairo_fixed_t y0
,
453 cairo_fixed_t x1
, cairo_fixed_t y1
,
454 cairo_fixed_t x2
, cairo_fixed_t y2
)
456 cairo_status_t status
;
457 cairo_point_t point
[3];
459 point
[0].x
= x0
; point
[0].y
= y0
;
460 point
[1].x
= x1
; point
[1].y
= y1
;
461 point
[2].x
= x2
; point
[2].y
= y2
;
463 /* make sure subpaths are started properly */
464 if (! path
->has_current_point
) {
465 status
= _cairo_path_fixed_move_to (path
, point
[0].x
, point
[0].y
);
466 if (unlikely (status
))
470 status
= _cairo_path_fixed_add (path
, CAIRO_PATH_OP_CURVE_TO
, point
, 3);
471 if (unlikely (status
))
474 path
->current_point
= point
[2];
475 path
->has_current_point
= TRUE
;
476 path
->has_curve_to
= TRUE
;
478 return CAIRO_STATUS_SUCCESS
;
482 _cairo_path_fixed_rel_curve_to (cairo_path_fixed_t
*path
,
483 cairo_fixed_t dx0
, cairo_fixed_t dy0
,
484 cairo_fixed_t dx1
, cairo_fixed_t dy1
,
485 cairo_fixed_t dx2
, cairo_fixed_t dy2
)
487 cairo_fixed_t x0
, y0
;
488 cairo_fixed_t x1
, y1
;
489 cairo_fixed_t x2
, y2
;
491 if (! path
->has_current_point
)
492 return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT
);
494 x0
= path
->current_point
.x
+ dx0
;
495 y0
= path
->current_point
.y
+ dy0
;
497 x1
= path
->current_point
.x
+ dx1
;
498 y1
= path
->current_point
.y
+ dy1
;
500 x2
= path
->current_point
.x
+ dx2
;
501 y2
= path
->current_point
.y
+ dy2
;
503 return _cairo_path_fixed_curve_to (path
,
510 _cairo_path_fixed_close_path (cairo_path_fixed_t
*path
)
512 cairo_status_t status
;
514 if (! path
->has_current_point
)
515 return CAIRO_STATUS_SUCCESS
;
517 status
= _cairo_path_fixed_add (path
, CAIRO_PATH_OP_CLOSE_PATH
, NULL
, 0);
518 if (unlikely (status
))
521 status
= _cairo_path_fixed_move_to (path
,
522 path
->last_move_point
.x
,
523 path
->last_move_point
.y
);
524 if (unlikely (status
))
527 return CAIRO_STATUS_SUCCESS
;
531 _cairo_path_fixed_get_current_point (cairo_path_fixed_t
*path
,
535 if (! path
->has_current_point
)
538 *x
= path
->current_point
.x
;
539 *y
= path
->current_point
.y
;
544 static cairo_status_t
545 _cairo_path_fixed_add (cairo_path_fixed_t
*path
,
547 cairo_point_t
*points
,
550 cairo_path_buf_t
*buf
= path
->buf_tail
;
552 if (buf
->num_ops
+ 1 > buf
->buf_size
||
553 buf
->num_points
+ num_points
> 2 * buf
->buf_size
)
555 buf
= _cairo_path_buf_create (buf
->buf_size
* 2);
556 if (unlikely (buf
== NULL
))
557 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
559 _cairo_path_fixed_add_buf (path
, buf
);
562 _cairo_path_buf_add_op (buf
, op
);
563 _cairo_path_buf_add_points (buf
, points
, num_points
);
565 return CAIRO_STATUS_SUCCESS
;
569 _cairo_path_fixed_add_buf (cairo_path_fixed_t
*path
,
570 cairo_path_buf_t
*buf
)
573 buf
->prev
= path
->buf_tail
;
575 path
->buf_tail
->next
= buf
;
576 path
->buf_tail
= buf
;
579 static cairo_path_buf_t
*
580 _cairo_path_buf_create (int buf_size
)
582 cairo_path_buf_t
*buf
;
584 /* adjust buf_size to ensure that buf->points is naturally aligned */
585 buf_size
+= sizeof (double)
586 - ((buf_size
+ sizeof (cairo_path_buf_t
)) & (sizeof (double)-1));
587 buf
= _cairo_malloc_ab_plus_c (buf_size
,
588 sizeof (cairo_path_op_t
) +
589 2 * sizeof (cairo_point_t
),
590 sizeof (cairo_path_buf_t
));
596 buf
->buf_size
= buf_size
;
598 buf
->op
= (cairo_path_op_t
*) (buf
+ 1);
599 buf
->points
= (cairo_point_t
*) (buf
->op
+ buf_size
);
606 _cairo_path_buf_destroy (cairo_path_buf_t
*buf
)
612 _cairo_path_buf_add_op (cairo_path_buf_t
*buf
,
615 buf
->op
[buf
->num_ops
++] = op
;
619 _cairo_path_buf_add_points (cairo_path_buf_t
*buf
,
620 cairo_point_t
*points
,
625 for (i
=0; i
< num_points
; i
++) {
626 buf
->points
[buf
->num_points
++] = points
[i
];
630 static int const num_args
[] =
632 1, /* cairo_path_move_to */
633 1, /* cairo_path_op_line_to */
634 3, /* cairo_path_op_curve_to */
635 0, /* cairo_path_op_close_path */
639 _cairo_path_fixed_interpret (const cairo_path_fixed_t
*path
,
640 cairo_direction_t dir
,
641 cairo_path_fixed_move_to_func_t
*move_to
,
642 cairo_path_fixed_line_to_func_t
*line_to
,
643 cairo_path_fixed_curve_to_func_t
*curve_to
,
644 cairo_path_fixed_close_path_func_t
*close_path
,
647 cairo_status_t status
;
648 const cairo_path_buf_t
*buf
;
650 cairo_bool_t forward
= (dir
== CAIRO_DIRECTION_FORWARD
);
651 int step
= forward
? 1 : -1;
653 for (buf
= forward
? &path
->buf_head
.base
: path
->buf_tail
;
655 buf
= forward
? buf
->next
: buf
->prev
)
657 cairo_point_t
*points
;
662 points
= buf
->points
;
664 start
= buf
->num_ops
- 1;
666 points
= buf
->points
+ buf
->num_points
;
669 for (i
=start
; i
!= stop
; i
+= step
) {
673 points
-= num_args
[(int) op
];
677 case CAIRO_PATH_OP_MOVE_TO
:
678 status
= (*move_to
) (closure
, &points
[0]);
680 case CAIRO_PATH_OP_LINE_TO
:
681 status
= (*line_to
) (closure
, &points
[0]);
683 case CAIRO_PATH_OP_CURVE_TO
:
684 status
= (*curve_to
) (closure
, &points
[0], &points
[1], &points
[2]);
686 case CAIRO_PATH_OP_CLOSE_PATH
:
688 status
= (*close_path
) (closure
);
691 if (unlikely (status
))
695 points
+= num_args
[(int) op
];
701 return CAIRO_STATUS_SUCCESS
;
704 static cairo_status_t
705 _append_move_to (void *closure
,
706 const cairo_point_t
*point
)
708 cairo_path_fixed_t
*path
= (cairo_path_fixed_t
*) closure
;
709 return _cairo_path_fixed_move_to (path
, point
->x
, point
->y
);
712 static cairo_status_t
713 _append_line_to (void *closure
,
714 const cairo_point_t
*point
)
716 cairo_path_fixed_t
*path
= (cairo_path_fixed_t
*) closure
;
717 return _cairo_path_fixed_line_to (path
, point
->x
, point
->y
);
720 static cairo_status_t
721 _append_curve_to (void *closure
,
722 const cairo_point_t
*p0
,
723 const cairo_point_t
*p1
,
724 const cairo_point_t
*p2
)
726 cairo_path_fixed_t
*path
= (cairo_path_fixed_t
*) closure
;
727 return _cairo_path_fixed_curve_to (path
, p0
->x
, p0
->y
, p1
->x
, p1
->y
, p2
->x
, p2
->y
);
730 static cairo_status_t
731 _append_close_path (void *closure
)
733 cairo_path_fixed_t
*path
= (cairo_path_fixed_t
*) closure
;
734 return _cairo_path_fixed_close_path (path
);
738 _cairo_path_fixed_append (cairo_path_fixed_t
*path
,
739 const cairo_path_fixed_t
*other
,
740 cairo_direction_t dir
)
742 return _cairo_path_fixed_interpret (other
, dir
,
751 _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t
*path
,
754 cairo_fixed_t scalex
,
755 cairo_fixed_t scaley
)
757 cairo_path_buf_t
*buf
= &path
->buf_head
.base
;
761 for (i
= 0; i
< buf
->num_points
; i
++) {
762 if (scalex
!= CAIRO_FIXED_ONE
)
763 buf
->points
[i
].x
= _cairo_fixed_mul (buf
->points
[i
].x
, scalex
);
764 buf
->points
[i
].x
+= offx
;
766 if (scaley
!= CAIRO_FIXED_ONE
)
767 buf
->points
[i
].y
= _cairo_fixed_mul (buf
->points
[i
].y
, scaley
);
768 buf
->points
[i
].y
+= offy
;
776 * _cairo_path_fixed_transform:
777 * @path: a #cairo_path_fixed_t to be transformed
778 * @matrix: a #cairo_matrix_t
780 * Transform the fixed-point path according to the given matrix.
781 * There is a fast path for the case where @matrix has no rotation
785 _cairo_path_fixed_transform (cairo_path_fixed_t
*path
,
786 cairo_matrix_t
*matrix
)
788 cairo_path_buf_t
*buf
;
792 if (matrix
->yx
== 0.0 && matrix
->xy
== 0.0) {
793 /* Fast path for the common case of scale+transform */
794 _cairo_path_fixed_offset_and_scale (path
,
795 _cairo_fixed_from_double (matrix
->x0
),
796 _cairo_fixed_from_double (matrix
->y0
),
797 _cairo_fixed_from_double (matrix
->xx
),
798 _cairo_fixed_from_double (matrix
->yy
));
802 buf
= &path
->buf_head
.base
;
804 for (i
= 0; i
< buf
->num_points
; i
++) {
805 dx
= _cairo_fixed_to_double (buf
->points
[i
].x
);
806 dy
= _cairo_fixed_to_double (buf
->points
[i
].y
);
808 cairo_matrix_transform_point (matrix
, &dx
, &dy
);
810 buf
->points
[i
].x
= _cairo_fixed_from_double (dx
);
811 buf
->points
[i
].y
= _cairo_fixed_from_double (dy
);
819 _cairo_path_fixed_is_equal (cairo_path_fixed_t
*path
,
820 cairo_path_fixed_t
*other
)
822 cairo_path_buf_t
*path_buf
, *other_buf
;
824 if (path
->current_point
.x
!= other
->current_point
.x
||
825 path
->current_point
.y
!= other
->current_point
.y
||
826 path
->has_current_point
!= other
->has_current_point
||
827 path
->has_curve_to
!= other
->has_curve_to
||
828 path
->last_move_point
.x
!= other
->last_move_point
.x
||
829 path
->last_move_point
.y
!= other
->last_move_point
.y
)
832 other_buf
= &other
->buf_head
.base
;
833 for (path_buf
= &path
->buf_head
.base
;
835 path_buf
= path_buf
->next
)
837 if (other_buf
== NULL
||
838 path_buf
->num_ops
!= other_buf
->num_ops
||
839 path_buf
->num_points
!= other_buf
->num_points
||
840 memcmp (path_buf
->op
, other_buf
->op
,
841 sizeof (cairo_path_op_t
) * path_buf
->num_ops
) != 0 ||
842 memcmp (path_buf
->points
, other_buf
->points
,
843 sizeof (cairo_point_t
) * path_buf
->num_points
) != 0)
847 other_buf
= other_buf
->next
;
852 /* Closure for path flattening */
853 typedef struct cairo_path_flattener
{
855 cairo_point_t current_point
;
856 cairo_path_fixed_move_to_func_t
*move_to
;
857 cairo_path_fixed_line_to_func_t
*line_to
;
858 cairo_path_fixed_close_path_func_t
*close_path
;
862 static cairo_status_t
863 _cpf_move_to (void *closure
,
864 const cairo_point_t
*point
)
866 cpf_t
*cpf
= closure
;
868 cpf
->current_point
= *point
;
870 return cpf
->move_to (cpf
->closure
, point
);
873 static cairo_status_t
874 _cpf_line_to (void *closure
,
875 const cairo_point_t
*point
)
877 cpf_t
*cpf
= closure
;
879 cpf
->current_point
= *point
;
881 return cpf
->line_to (cpf
->closure
, point
);
884 static cairo_status_t
885 _cpf_curve_to (void *closure
,
886 const cairo_point_t
*p1
,
887 const cairo_point_t
*p2
,
888 const cairo_point_t
*p3
)
890 cpf_t
*cpf
= closure
;
891 cairo_spline_t spline
;
893 cairo_point_t
*p0
= &cpf
->current_point
;
895 if (! _cairo_spline_init (&spline
,
900 return _cpf_line_to (closure
, p3
);
903 cpf
->current_point
= *p3
;
905 return _cairo_spline_decompose (&spline
, cpf
->tolerance
);
908 static cairo_status_t
909 _cpf_close_path (void *closure
)
911 cpf_t
*cpf
= closure
;
913 return cpf
->close_path (cpf
->closure
);
918 _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t
*path
,
919 cairo_direction_t dir
,
920 cairo_path_fixed_move_to_func_t
*move_to
,
921 cairo_path_fixed_line_to_func_t
*line_to
,
922 cairo_path_fixed_close_path_func_t
*close_path
,
928 if (!path
->has_curve_to
) {
929 return _cairo_path_fixed_interpret (path
, dir
,
937 flattener
.tolerance
= tolerance
;
938 flattener
.move_to
= move_to
;
939 flattener
.line_to
= line_to
;
940 flattener
.close_path
= close_path
;
941 flattener
.closure
= closure
;
942 return _cairo_path_fixed_interpret (path
, dir
,
951 _cairo_path_fixed_is_empty (cairo_path_fixed_t
*path
)
953 if (path
->buf_head
.base
.num_ops
== 0)
960 * Check whether the given path contains a single rectangle.
963 _cairo_path_fixed_is_box (cairo_path_fixed_t
*path
,
966 cairo_path_buf_t
*buf
= &path
->buf_head
.base
;
968 /* We can't have more than one buf for this check */
969 if (buf
->next
!= NULL
)
972 /* Do we have the right number of ops? */
973 if (buf
->num_ops
!= 5 && buf
->num_ops
!= 6)
976 /* Check whether the ops are those that would be used for a rectangle */
977 if (buf
->op
[0] != CAIRO_PATH_OP_MOVE_TO
||
978 buf
->op
[1] != CAIRO_PATH_OP_LINE_TO
||
979 buf
->op
[2] != CAIRO_PATH_OP_LINE_TO
||
980 buf
->op
[3] != CAIRO_PATH_OP_LINE_TO
)
985 /* Now, there are choices. The rectangle might end with a LINE_TO
986 * (to the original point), but this isn't required. If it
987 * doesn't, then it must end with a CLOSE_PATH. */
988 if (buf
->op
[4] == CAIRO_PATH_OP_LINE_TO
) {
989 if (buf
->points
[4].x
!= buf
->points
[0].x
||
990 buf
->points
[4].y
!= buf
->points
[0].y
)
992 } else if (buf
->op
[4] != CAIRO_PATH_OP_CLOSE_PATH
) {
996 if (buf
->num_ops
== 6) {
997 /* A trailing CLOSE_PATH or MOVE_TO is ok */
998 if (buf
->op
[5] != CAIRO_PATH_OP_MOVE_TO
&&
999 buf
->op
[5] != CAIRO_PATH_OP_CLOSE_PATH
)
1003 /* Ok, we may have a box, if the points line up */
1004 if (buf
->points
[0].y
== buf
->points
[1].y
&&
1005 buf
->points
[1].x
== buf
->points
[2].x
&&
1006 buf
->points
[2].y
== buf
->points
[3].y
&&
1007 buf
->points
[3].x
== buf
->points
[0].x
)
1009 box
->p1
= buf
->points
[0];
1010 box
->p2
= buf
->points
[2];
1014 if (buf
->points
[0].x
== buf
->points
[1].x
&&
1015 buf
->points
[1].y
== buf
->points
[2].y
&&
1016 buf
->points
[2].x
== buf
->points
[3].x
&&
1017 buf
->points
[3].y
== buf
->points
[0].y
)
1019 box
->p1
= buf
->points
[0];
1020 box
->p2
= buf
->points
[2];
1028 * Check whether the given path contains a single rectangle
1029 * that is logically equivalent to:
1030 * <informalexample><programlisting>
1031 * cairo_move_to (cr, x, y);
1032 * cairo_rel_line_to (cr, width, 0);
1033 * cairo_rel_line_to (cr, 0, height);
1034 * cairo_rel_line_to (cr, -width, 0);
1035 * cairo_close_path (cr);
1036 * </programlisting></informalexample>
1039 _cairo_path_fixed_is_rectangle (cairo_path_fixed_t
*path
,
1042 cairo_path_buf_t
*buf
= &path
->buf_head
.base
;
1044 if (!_cairo_path_fixed_is_box (path
, box
))
1047 if (buf
->points
[0].y
== buf
->points
[1].y
)
1054 _cairo_path_fixed_iter_init (cairo_path_fixed_iter_t
*iter
,
1055 cairo_path_fixed_t
*path
)
1057 iter
->buf
= &path
->buf_head
.base
;
1063 _cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t
*iter
)
1065 if (++iter
->n_op
>= iter
->buf
->num_ops
) {
1066 iter
->buf
= iter
->buf
->next
;
1071 return iter
->buf
!= NULL
;
1075 _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t
*_iter
,
1078 cairo_point_t points
[5];
1079 cairo_path_fixed_iter_t iter
;
1081 if (_iter
->buf
== NULL
)
1086 if (iter
.n_op
== iter
.buf
->num_ops
&&
1087 ! _cairo_path_fixed_iter_next_op (&iter
))
1092 /* Check whether the ops are those that would be used for a rectangle */
1093 if (iter
.buf
->op
[iter
.n_op
] != CAIRO_PATH_OP_MOVE_TO
)
1095 points
[0] = iter
.buf
->points
[iter
.n_point
++];
1096 if (! _cairo_path_fixed_iter_next_op (&iter
))
1099 if (iter
.buf
->op
[iter
.n_op
] != CAIRO_PATH_OP_LINE_TO
)
1101 points
[1] = iter
.buf
->points
[iter
.n_point
++];
1102 if (! _cairo_path_fixed_iter_next_op (&iter
))
1105 if (iter
.buf
->op
[iter
.n_op
] != CAIRO_PATH_OP_LINE_TO
)
1107 points
[2] = iter
.buf
->points
[iter
.n_point
++];
1108 if (! _cairo_path_fixed_iter_next_op (&iter
))
1111 if (iter
.buf
->op
[iter
.n_op
] != CAIRO_PATH_OP_LINE_TO
)
1113 points
[3] = iter
.buf
->points
[iter
.n_point
++];
1114 if (! _cairo_path_fixed_iter_next_op (&iter
))
1117 /* Now, there are choices. The rectangle might end with a LINE_TO
1118 * (to the original point), but this isn't required. If it
1119 * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
1120 if (iter
.buf
->op
[iter
.n_op
] == CAIRO_PATH_OP_LINE_TO
)
1122 points
[4] = iter
.buf
->points
[iter
.n_point
++];
1123 if (points
[4].x
!= points
[0].x
|| points
[4].y
!= points
[0].y
)
1126 else if (! (iter
.buf
->op
[iter
.n_op
] == CAIRO_PATH_OP_CLOSE_PATH
||
1127 iter
.buf
->op
[iter
.n_op
] == CAIRO_PATH_OP_MOVE_TO
))
1131 if (! _cairo_path_fixed_iter_next_op (&iter
))
1134 /* Ok, we may have a box, if the points line up */
1135 if (points
[0].y
== points
[1].y
&&
1136 points
[1].x
== points
[2].x
&&
1137 points
[2].y
== points
[3].y
&&
1138 points
[3].x
== points
[0].x
)
1140 box
->p1
= points
[0];
1141 box
->p2
= points
[2];
1146 if (points
[0].x
== points
[1].x
&&
1147 points
[1].y
== points
[2].y
&&
1148 points
[2].x
== points
[3].x
&&
1149 points
[3].y
== points
[0].y
)
1151 box
->p1
= points
[0];
1152 box
->p2
= points
[2];
1161 _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t
*iter
)
1163 if (iter
->buf
== NULL
)
1166 if (iter
->n_op
== iter
->buf
->num_ops
)
1169 if (iter
->buf
->op
[iter
->n_op
] == CAIRO_PATH_OP_MOVE_TO
&&
1170 iter
->buf
->num_ops
== iter
->n_op
+ 1)
1178 /* Closure for path region testing. Every move_to must be to integer
1179 * coordinates, there must be no curves, and every line_to or
1180 * close_path must represent an axis aligned line to an integer point.
1181 * We're relying on the path interpreter always sending a single
1182 * move_to at the start of any subpath, not receiving having any
1183 * superfluous move_tos, and the path intepreter bailing with our
1184 * first non-successful error. */
1185 typedef struct cairo_path_region_tester
{
1186 cairo_point_t last_move_point
;
1187 cairo_point_t current_point
;
1190 static cairo_status_t
1191 _cprt_line_to (void *closure
,
1192 const cairo_point_t
*p2
)
1194 cprt_t
*self
= closure
;
1195 cairo_point_t
*p1
= &self
->current_point
;
1197 if (p2
->x
== p1
->x
) {
1198 if (_cairo_fixed_is_integer (p2
->y
)) {
1200 return CAIRO_STATUS_SUCCESS
;
1202 } else if (p2
->y
== p1
->y
) {
1203 if (_cairo_fixed_is_integer (p2
->x
)) {
1205 return CAIRO_STATUS_SUCCESS
;
1209 return CAIRO_INT_STATUS_UNSUPPORTED
;
1212 static cairo_status_t
1213 _cprt_close_path (void *closure
)
1215 cprt_t
*self
= closure
;
1216 return _cprt_line_to (closure
, &self
->last_move_point
);
1219 static cairo_status_t
1220 _cprt_move_to (void *closure
,
1221 const cairo_point_t
*p
)
1223 cprt_t
*self
= closure
;
1224 cairo_status_t status
;
1226 status
= _cprt_close_path (closure
);
1230 if (_cairo_fixed_is_integer (p
->x
) && _cairo_fixed_is_integer (p
->y
)) {
1231 self
->current_point
= *p
;
1232 self
->last_move_point
= *p
;
1233 return CAIRO_STATUS_SUCCESS
;
1236 return CAIRO_INT_STATUS_UNSUPPORTED
;
1240 * Check whether the given path is representable as a region.
1241 * That is, if the path contains only axis aligned lines between
1242 * integer coordinates in device space.
1245 _cairo_path_fixed_is_region (cairo_path_fixed_t
*path
)
1249 if (path
->has_curve_to
)
1252 cprt
.current_point
.x
= 0;
1253 cprt
.current_point
.y
= 0;
1254 cprt
.last_move_point
.x
= 0;
1255 cprt
.last_move_point
.y
= 0;
1257 return _cairo_path_fixed_interpret (path
,
1258 CAIRO_DIRECTION_FORWARD
,
1263 &cprt
) == CAIRO_STATUS_SUCCESS
;