Line data Source code
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
3 : *
4 : * Copyright © 2002 University of Southern California
5 : * Copyright © 2005 Red Hat, Inc.
6 : *
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.
14 : *
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18 : * You should have received a copy of the MPL along with this library
19 : * in the file COPYING-MPL-1.1
20 : *
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/
25 : *
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.
29 : *
30 : * The Original Code is the cairo graphics library.
31 : *
32 : * The Initial Developer of the Original Code is University of Southern
33 : * California.
34 : *
35 : * Contributor(s):
36 : * Carl D. Worth <cworth@cworth.org>
37 : */
38 :
39 : #include "cairoint.h"
40 :
41 : #include "cairo-error-private.h"
42 : #include "cairo-path-fixed-private.h"
43 : #include "cairo-slope-private.h"
44 :
45 : static cairo_status_t
46 : _cairo_path_fixed_add (cairo_path_fixed_t *path,
47 : cairo_path_op_t op,
48 : const cairo_point_t *points,
49 : int num_points);
50 :
51 : static void
52 : _cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
53 : cairo_path_buf_t *buf);
54 :
55 : static cairo_path_buf_t *
56 : _cairo_path_buf_create (int size_ops, int size_points);
57 :
58 : static void
59 : _cairo_path_buf_destroy (cairo_path_buf_t *buf);
60 :
61 : static void
62 : _cairo_path_buf_add_op (cairo_path_buf_t *buf,
63 : cairo_path_op_t op);
64 :
65 : static void
66 : _cairo_path_buf_add_points (cairo_path_buf_t *buf,
67 : const cairo_point_t *points,
68 : int num_points);
69 :
70 : #define cairo_path_head(path__) (&(path__)->buf.base)
71 : #define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
72 :
73 : #define cairo_path_buf_next(pos__) \
74 : cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
75 : #define cairo_path_buf_prev(pos__) \
76 : cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
77 :
78 : #define cairo_path_foreach_buf_start(pos__, path__) \
79 : pos__ = cairo_path_head (path__); do
80 : #define cairo_path_foreach_buf_end(pos__, path__) \
81 : while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__))
82 :
83 : void
84 2 : _cairo_path_fixed_init (cairo_path_fixed_t *path)
85 : {
86 : VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
87 :
88 2 : cairo_list_init (&path->buf.base.link);
89 :
90 2 : path->buf.base.num_ops = 0;
91 2 : path->buf.base.num_points = 0;
92 2 : path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
93 2 : path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
94 2 : path->buf.base.op = path->buf.op;
95 2 : path->buf.base.points = path->buf.points;
96 :
97 2 : path->current_point.x = 0;
98 2 : path->current_point.y = 0;
99 2 : path->last_move_point = path->current_point;
100 2 : path->has_last_move_point = FALSE;
101 2 : path->has_current_point = FALSE;
102 2 : path->has_curve_to = FALSE;
103 2 : path->is_rectilinear = TRUE;
104 2 : path->maybe_fill_region = TRUE;
105 2 : path->is_empty_fill = TRUE;
106 :
107 2 : path->extents.p1.x = path->extents.p1.y = INT_MAX;
108 2 : path->extents.p2.x = path->extents.p2.y = INT_MIN;
109 2 : }
110 :
111 : cairo_status_t
112 0 : _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
113 : const cairo_path_fixed_t *other)
114 : {
115 : cairo_path_buf_t *buf, *other_buf;
116 : unsigned int num_points, num_ops;
117 :
118 : VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
119 :
120 0 : cairo_list_init (&path->buf.base.link);
121 :
122 0 : path->buf.base.op = path->buf.op;
123 0 : path->buf.base.points = path->buf.points;
124 0 : path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
125 0 : path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
126 :
127 0 : path->current_point = other->current_point;
128 0 : path->last_move_point = other->last_move_point;
129 0 : path->has_last_move_point = other->has_last_move_point;
130 0 : path->has_current_point = other->has_current_point;
131 0 : path->has_curve_to = other->has_curve_to;
132 0 : path->is_rectilinear = other->is_rectilinear;
133 0 : path->maybe_fill_region = other->maybe_fill_region;
134 0 : path->is_empty_fill = other->is_empty_fill;
135 :
136 0 : path->extents = other->extents;
137 :
138 0 : path->buf.base.num_ops = other->buf.base.num_ops;
139 0 : path->buf.base.num_points = other->buf.base.num_points;
140 0 : memcpy (path->buf.op, other->buf.base.op,
141 0 : other->buf.base.num_ops * sizeof (other->buf.op[0]));
142 0 : memcpy (path->buf.points, other->buf.points,
143 0 : other->buf.base.num_points * sizeof (other->buf.points[0]));
144 :
145 0 : num_points = num_ops = 0;
146 0 : for (other_buf = cairo_path_buf_next (cairo_path_head (other));
147 0 : other_buf != cairo_path_head (other);
148 0 : other_buf = cairo_path_buf_next (other_buf))
149 : {
150 0 : num_ops += other_buf->num_ops;
151 0 : num_points += other_buf->num_points;
152 : }
153 :
154 0 : if (num_ops) {
155 0 : buf = _cairo_path_buf_create (num_ops, num_points);
156 0 : if (unlikely (buf == NULL)) {
157 0 : _cairo_path_fixed_fini (path);
158 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
159 : }
160 :
161 0 : for (other_buf = cairo_path_buf_next (cairo_path_head (other));
162 0 : other_buf != cairo_path_head (other);
163 0 : other_buf = cairo_path_buf_next (other_buf))
164 : {
165 0 : memcpy (buf->op + buf->num_ops, other_buf->op,
166 0 : other_buf->num_ops * sizeof (buf->op[0]));
167 0 : buf->num_ops += other_buf->num_ops;
168 :
169 0 : memcpy (buf->points + buf->num_points, other_buf->points,
170 0 : other_buf->num_points * sizeof (buf->points[0]));
171 0 : buf->num_points += other_buf->num_points;
172 : }
173 :
174 0 : _cairo_path_fixed_add_buf (path, buf);
175 : }
176 :
177 0 : return CAIRO_STATUS_SUCCESS;
178 : }
179 :
180 : unsigned long
181 0 : _cairo_path_fixed_hash (const cairo_path_fixed_t *path)
182 : {
183 0 : unsigned long hash = _CAIRO_HASH_INIT_VALUE;
184 : const cairo_path_buf_t *buf;
185 : int num_points, num_ops;
186 :
187 0 : hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents));
188 :
189 0 : num_ops = num_points = 0;
190 0 : cairo_path_foreach_buf_start (buf, path) {
191 0 : hash = _cairo_hash_bytes (hash, buf->op,
192 0 : buf->num_ops * sizeof (buf->op[0]));
193 0 : hash = _cairo_hash_bytes (hash, buf->points,
194 0 : buf->num_points * sizeof (buf->points[0]));
195 :
196 0 : num_ops += buf->num_ops;
197 0 : num_points += buf->num_points;
198 0 : } cairo_path_foreach_buf_end (buf, path);
199 :
200 0 : hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops));
201 0 : hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points));
202 :
203 0 : return hash;
204 : }
205 :
206 : unsigned long
207 0 : _cairo_path_fixed_size (const cairo_path_fixed_t *path)
208 : {
209 : const cairo_path_buf_t *buf;
210 : int num_points, num_ops;
211 :
212 0 : num_ops = num_points = 0;
213 0 : cairo_path_foreach_buf_start (buf, path) {
214 0 : num_ops += buf->num_ops;
215 0 : num_points += buf->num_points;
216 0 : } cairo_path_foreach_buf_end (buf, path);
217 :
218 0 : return num_ops * sizeof (buf->op[0]) +
219 0 : num_points * sizeof (buf->points[0]);
220 : }
221 :
222 : cairo_bool_t
223 0 : _cairo_path_fixed_equal (const cairo_path_fixed_t *a,
224 : const cairo_path_fixed_t *b)
225 : {
226 : const cairo_path_buf_t *buf_a, *buf_b;
227 : const cairo_path_op_t *ops_a, *ops_b;
228 : const cairo_point_t *points_a, *points_b;
229 : int num_points_a, num_ops_a;
230 : int num_points_b, num_ops_b;
231 :
232 0 : if (a == b)
233 0 : return TRUE;
234 :
235 : /* use the flags to quickly differentiate based on contents */
236 0 : if (a->is_empty_fill != b->is_empty_fill ||
237 0 : a->has_curve_to != b->has_curve_to ||
238 0 : a->maybe_fill_region != b->maybe_fill_region ||
239 0 : a->is_rectilinear != b->is_rectilinear)
240 : {
241 0 : return FALSE;
242 : }
243 :
244 0 : if (a->extents.p1.x != b->extents.p1.x ||
245 0 : a->extents.p1.y != b->extents.p1.y ||
246 0 : a->extents.p2.x != b->extents.p2.x ||
247 0 : a->extents.p2.y != b->extents.p2.y)
248 : {
249 0 : return FALSE;
250 : }
251 :
252 0 : num_ops_a = num_points_a = 0;
253 0 : cairo_path_foreach_buf_start (buf_a, a) {
254 0 : num_ops_a += buf_a->num_ops;
255 0 : num_points_a += buf_a->num_points;
256 0 : } cairo_path_foreach_buf_end (buf_a, a);
257 :
258 0 : num_ops_b = num_points_b = 0;
259 0 : cairo_path_foreach_buf_start (buf_b, b) {
260 0 : num_ops_b += buf_b->num_ops;
261 0 : num_points_b += buf_b->num_points;
262 0 : } cairo_path_foreach_buf_end (buf_b, b);
263 :
264 0 : if (num_ops_a == 0 && num_ops_b == 0)
265 0 : return TRUE;
266 :
267 0 : if (num_ops_a != num_ops_b || num_points_a != num_points_b)
268 0 : return FALSE;
269 :
270 0 : buf_a = cairo_path_head (a);
271 0 : num_points_a = buf_a->num_points;
272 0 : num_ops_a = buf_a->num_ops;
273 0 : ops_a = buf_a->op;
274 0 : points_a = buf_a->points;
275 :
276 0 : buf_b = cairo_path_head (b);
277 0 : num_points_b = buf_b->num_points;
278 0 : num_ops_b = buf_b->num_ops;
279 0 : ops_b = buf_b->op;
280 0 : points_b = buf_b->points;
281 :
282 0 : while (TRUE) {
283 0 : int num_ops = MIN (num_ops_a, num_ops_b);
284 0 : int num_points = MIN (num_points_a, num_points_b);
285 :
286 0 : if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t)))
287 0 : return FALSE;
288 0 : if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t)))
289 0 : return FALSE;
290 :
291 0 : num_ops_a -= num_ops;
292 0 : ops_a += num_ops;
293 0 : num_points_a -= num_points;
294 0 : points_a += num_points;
295 0 : if (num_ops_a == 0 || num_points_a == 0) {
296 0 : if (num_ops_a || num_points_a)
297 0 : return FALSE;
298 :
299 0 : buf_a = cairo_path_buf_next (buf_a);
300 0 : if (buf_a == cairo_path_head (a))
301 0 : break;
302 :
303 0 : num_points_a = buf_a->num_points;
304 0 : num_ops_a = buf_a->num_ops;
305 0 : ops_a = buf_a->op;
306 0 : points_a = buf_a->points;
307 : }
308 :
309 0 : num_ops_b -= num_ops;
310 0 : ops_b += num_ops;
311 0 : num_points_b -= num_points;
312 0 : points_b += num_points;
313 0 : if (num_ops_b == 0 || num_points_b == 0) {
314 0 : if (num_ops_b || num_points_b)
315 0 : return FALSE;
316 :
317 0 : buf_b = cairo_path_buf_next (buf_b);
318 0 : if (buf_b == cairo_path_head (b))
319 0 : break;
320 :
321 0 : num_points_b = buf_b->num_points;
322 0 : num_ops_b = buf_b->num_ops;
323 0 : ops_b = buf_b->op;
324 0 : points_b = buf_b->points;
325 : }
326 : }
327 :
328 0 : return TRUE;
329 : }
330 :
331 : cairo_path_fixed_t *
332 0 : _cairo_path_fixed_create (void)
333 : {
334 : cairo_path_fixed_t *path;
335 :
336 0 : path = malloc (sizeof (cairo_path_fixed_t));
337 0 : if (!path) {
338 0 : _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
339 0 : return NULL;
340 : }
341 :
342 0 : _cairo_path_fixed_init (path);
343 0 : return path;
344 : }
345 :
346 : void
347 0 : _cairo_path_fixed_fini (cairo_path_fixed_t *path)
348 : {
349 : cairo_path_buf_t *buf;
350 :
351 0 : buf = cairo_path_buf_next (cairo_path_head (path));
352 0 : while (buf != cairo_path_head (path)) {
353 0 : cairo_path_buf_t *this = buf;
354 0 : buf = cairo_path_buf_next (buf);
355 0 : _cairo_path_buf_destroy (this);
356 : }
357 :
358 : VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t)));
359 0 : }
360 :
361 : void
362 0 : _cairo_path_fixed_destroy (cairo_path_fixed_t *path)
363 : {
364 0 : _cairo_path_fixed_fini (path);
365 0 : free (path);
366 0 : }
367 :
368 : static cairo_path_op_t
369 0 : _cairo_path_last_op (cairo_path_fixed_t *path)
370 : {
371 : cairo_path_buf_t *buf;
372 :
373 0 : buf = cairo_path_tail (path);
374 0 : if (buf->num_ops == 0)
375 0 : return -1;
376 :
377 0 : return buf->op[buf->num_ops - 1];
378 : }
379 :
380 : static inline void
381 0 : _cairo_path_fixed_extents_add (cairo_path_fixed_t *path,
382 : const cairo_point_t *point)
383 : {
384 0 : if (point->x < path->extents.p1.x)
385 0 : path->extents.p1.x = point->x;
386 0 : if (point->y < path->extents.p1.y)
387 0 : path->extents.p1.y = point->y;
388 :
389 0 : if (point->x > path->extents.p2.x)
390 0 : path->extents.p2.x = point->x;
391 0 : if (point->y > path->extents.p2.y)
392 0 : path->extents.p2.y = point->y;
393 0 : }
394 :
395 : cairo_status_t
396 0 : _cairo_path_fixed_move_to (cairo_path_fixed_t *path,
397 : cairo_fixed_t x,
398 : cairo_fixed_t y)
399 : {
400 : cairo_status_t status;
401 : cairo_point_t point;
402 :
403 0 : point.x = x;
404 0 : point.y = y;
405 :
406 : /* If the previous op was also a MOVE_TO, then just change its
407 : * point rather than adding a new op. */
408 0 : if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
409 : cairo_path_buf_t *buf;
410 :
411 0 : buf = cairo_path_tail (path);
412 0 : buf->points[buf->num_points - 1] = point;
413 : } else {
414 0 : status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
415 0 : if (unlikely (status))
416 0 : return status;
417 :
418 0 : if (path->has_current_point && path->is_rectilinear) {
419 : /* a move-to is first an implicit close */
420 0 : path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
421 0 : path->current_point.y == path->last_move_point.y;
422 0 : path->maybe_fill_region &= path->is_rectilinear;
423 : }
424 0 : if (path->maybe_fill_region) {
425 0 : path->maybe_fill_region =
426 0 : _cairo_fixed_is_integer (path->last_move_point.x) &&
427 0 : _cairo_fixed_is_integer (path->last_move_point.y);
428 : }
429 : }
430 :
431 0 : path->current_point = point;
432 0 : path->last_move_point = point;
433 0 : path->has_last_move_point = TRUE;
434 0 : path->has_current_point = TRUE;
435 :
436 0 : return CAIRO_STATUS_SUCCESS;
437 : }
438 :
439 : void
440 0 : _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
441 : {
442 0 : path->has_current_point = FALSE;
443 0 : }
444 :
445 : cairo_status_t
446 0 : _cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
447 : cairo_fixed_t dx,
448 : cairo_fixed_t dy)
449 : {
450 0 : if (unlikely (! path->has_current_point))
451 0 : return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
452 :
453 0 : return _cairo_path_fixed_move_to (path,
454 0 : path->current_point.x + dx,
455 0 : path->current_point.y + dy);
456 :
457 : }
458 :
459 : cairo_status_t
460 0 : _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
461 : cairo_fixed_t x,
462 : cairo_fixed_t y)
463 : {
464 : cairo_status_t status;
465 : cairo_point_t point;
466 :
467 0 : point.x = x;
468 0 : point.y = y;
469 :
470 : /* When there is not yet a current point, the line_to operation
471 : * becomes a move_to instead. Note: We have to do this by
472 : * explicitly calling into _cairo_path_fixed_move_to to ensure
473 : * that the last_move_point state is updated properly.
474 : */
475 0 : if (! path->has_current_point)
476 0 : return _cairo_path_fixed_move_to (path, point.x, point.y);
477 :
478 : /* If the previous op was but the initial MOVE_TO and this segment
479 : * is degenerate, then we can simply skip this point. Note that
480 : * a move-to followed by a degenerate line-to is a valid path for
481 : * stroking, but at all other times is simply a degenerate segment.
482 : */
483 0 : if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
484 0 : if (x == path->current_point.x && y == path->current_point.y)
485 0 : return CAIRO_STATUS_SUCCESS;
486 : }
487 :
488 : /* If the previous op was also a LINE_TO with the same gradient,
489 : * then just change its end-point rather than adding a new op.
490 : */
491 0 : if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
492 : cairo_path_buf_t *buf;
493 : const cairo_point_t *p;
494 :
495 0 : buf = cairo_path_tail (path);
496 0 : if (likely (buf->num_points >= 2)) {
497 0 : p = &buf->points[buf->num_points-2];
498 : } else {
499 0 : cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
500 0 : p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
501 : }
502 :
503 0 : if (p->x == path->current_point.x && p->y == path->current_point.y) {
504 : /* previous line element was degenerate, replace */
505 0 : buf->points[buf->num_points - 1] = point;
506 0 : goto FLAGS;
507 : } else {
508 : cairo_slope_t prev, self;
509 :
510 0 : _cairo_slope_init (&prev, p, &path->current_point);
511 0 : _cairo_slope_init (&self, &path->current_point, &point);
512 0 : if (_cairo_slope_equal (&prev, &self) &&
513 : /* cannot trim anti-parallel segments whilst stroking */
514 0 : ! _cairo_slope_backwards (&prev, &self))
515 : {
516 0 : buf->points[buf->num_points - 1] = point;
517 0 : goto FLAGS;
518 : }
519 : }
520 : }
521 :
522 0 : status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
523 0 : if (unlikely (status))
524 0 : return status;
525 :
526 : FLAGS:
527 0 : if (path->is_rectilinear) {
528 0 : path->is_rectilinear = path->current_point.x == x ||
529 0 : path->current_point.y == y;
530 0 : path->maybe_fill_region &= path->is_rectilinear;
531 : }
532 0 : if (path->maybe_fill_region) {
533 0 : path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
534 0 : _cairo_fixed_is_integer (y);
535 : }
536 0 : if (path->is_empty_fill) {
537 0 : path->is_empty_fill = path->current_point.x == x &&
538 0 : path->current_point.y == y;
539 : }
540 :
541 0 : path->current_point = point;
542 0 : if (path->has_last_move_point) {
543 0 : _cairo_path_fixed_extents_add (path, &path->last_move_point);
544 0 : path->has_last_move_point = FALSE;
545 : }
546 0 : _cairo_path_fixed_extents_add (path, &point);
547 0 : return CAIRO_STATUS_SUCCESS;
548 : }
549 :
550 : cairo_status_t
551 0 : _cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
552 : cairo_fixed_t dx,
553 : cairo_fixed_t dy)
554 : {
555 0 : if (unlikely (! path->has_current_point))
556 0 : return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
557 :
558 0 : return _cairo_path_fixed_line_to (path,
559 0 : path->current_point.x + dx,
560 0 : path->current_point.y + dy);
561 : }
562 :
563 : cairo_status_t
564 0 : _cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
565 : cairo_fixed_t x0, cairo_fixed_t y0,
566 : cairo_fixed_t x1, cairo_fixed_t y1,
567 : cairo_fixed_t x2, cairo_fixed_t y2)
568 : {
569 : cairo_status_t status;
570 : cairo_point_t point[3];
571 :
572 : /* make sure subpaths are started properly */
573 0 : if (! path->has_current_point) {
574 0 : status = _cairo_path_fixed_move_to (path, x0, y0);
575 0 : if (unlikely (status))
576 0 : return status;
577 : }
578 :
579 0 : point[0].x = x0; point[0].y = y0;
580 0 : point[1].x = x1; point[1].y = y1;
581 0 : point[2].x = x2; point[2].y = y2;
582 0 : status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
583 0 : if (unlikely (status))
584 0 : return status;
585 :
586 0 : path->current_point = point[2];
587 0 : path->has_current_point = TRUE;
588 0 : path->is_empty_fill = FALSE;
589 0 : path->has_curve_to = TRUE;
590 0 : path->is_rectilinear = FALSE;
591 0 : path->maybe_fill_region = FALSE;
592 :
593 : /* coarse bounds */
594 0 : if (path->has_last_move_point) {
595 0 : _cairo_path_fixed_extents_add (path, &path->last_move_point);
596 0 : path->has_last_move_point = FALSE;
597 : }
598 0 : _cairo_path_fixed_extents_add (path, &point[0]);
599 0 : _cairo_path_fixed_extents_add (path, &point[1]);
600 0 : _cairo_path_fixed_extents_add (path, &point[2]);
601 :
602 0 : return CAIRO_STATUS_SUCCESS;
603 : }
604 :
605 : cairo_status_t
606 0 : _cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
607 : cairo_fixed_t dx0, cairo_fixed_t dy0,
608 : cairo_fixed_t dx1, cairo_fixed_t dy1,
609 : cairo_fixed_t dx2, cairo_fixed_t dy2)
610 : {
611 0 : if (unlikely (! path->has_current_point))
612 0 : return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
613 :
614 0 : return _cairo_path_fixed_curve_to (path,
615 0 : path->current_point.x + dx0,
616 0 : path->current_point.y + dy0,
617 :
618 0 : path->current_point.x + dx1,
619 0 : path->current_point.y + dy1,
620 :
621 0 : path->current_point.x + dx2,
622 0 : path->current_point.y + dy2);
623 : }
624 :
625 : cairo_status_t
626 0 : _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
627 : {
628 : cairo_status_t status;
629 :
630 0 : if (! path->has_current_point)
631 0 : return CAIRO_STATUS_SUCCESS;
632 :
633 : /* If the previous op was also a LINE_TO back to the start, discard it */
634 0 : if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
635 0 : if (path->current_point.x == path->last_move_point.x &&
636 0 : path->current_point.y == path->last_move_point.y)
637 : {
638 : cairo_path_buf_t *buf;
639 : cairo_point_t *p;
640 :
641 0 : buf = cairo_path_tail (path);
642 0 : if (likely (buf->num_points >= 2)) {
643 0 : p = &buf->points[buf->num_points-2];
644 : } else {
645 0 : cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
646 0 : p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
647 : }
648 :
649 0 : path->current_point = *p;
650 0 : buf->num_ops--;
651 0 : buf->num_points--;
652 : }
653 : }
654 :
655 0 : status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
656 0 : if (unlikely (status))
657 0 : return status;
658 :
659 0 : return _cairo_path_fixed_move_to (path,
660 : path->last_move_point.x,
661 : path->last_move_point.y);
662 : }
663 :
664 : cairo_bool_t
665 0 : _cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
666 : cairo_fixed_t *x,
667 : cairo_fixed_t *y)
668 : {
669 0 : if (! path->has_current_point)
670 0 : return FALSE;
671 :
672 0 : *x = path->current_point.x;
673 0 : *y = path->current_point.y;
674 :
675 0 : return TRUE;
676 : }
677 :
678 : static cairo_status_t
679 0 : _cairo_path_fixed_add (cairo_path_fixed_t *path,
680 : cairo_path_op_t op,
681 : const cairo_point_t *points,
682 : int num_points)
683 : {
684 0 : cairo_path_buf_t *buf = cairo_path_tail (path);
685 :
686 0 : if (buf->num_ops + 1 > buf->size_ops ||
687 0 : buf->num_points + num_points > buf->size_points)
688 : {
689 0 : buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2);
690 0 : if (unlikely (buf == NULL))
691 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
692 :
693 0 : _cairo_path_fixed_add_buf (path, buf);
694 : }
695 :
696 : if (WATCH_PATH) {
697 : const char *op_str[] = {
698 : "move-to",
699 : "line-to",
700 : "curve-to",
701 : "close-path",
702 : };
703 : char buf[1024];
704 : int len = 0;
705 : int i;
706 :
707 : len += snprintf (buf + len, sizeof (buf), "[");
708 : for (i = 0; i < num_points; i++) {
709 : if (i != 0)
710 : len += snprintf (buf + len, sizeof (buf), " ");
711 : len += snprintf (buf + len, sizeof (buf), "(%f, %f)",
712 : _cairo_fixed_to_double (points[i].x),
713 : _cairo_fixed_to_double (points[i].y));
714 : }
715 : len += snprintf (buf + len, sizeof (buf), "]");
716 :
717 : fprintf (stderr,
718 : "_cairo_path_fixed_add (%s, %s)\n",
719 : op_str[(int) op], buf);
720 : }
721 :
722 0 : _cairo_path_buf_add_op (buf, op);
723 0 : _cairo_path_buf_add_points (buf, points, num_points);
724 :
725 0 : return CAIRO_STATUS_SUCCESS;
726 : }
727 :
728 : static void
729 0 : _cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
730 : cairo_path_buf_t *buf)
731 : {
732 0 : cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link);
733 0 : }
734 :
735 : COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1);
736 : static cairo_path_buf_t *
737 0 : _cairo_path_buf_create (int size_ops, int size_points)
738 : {
739 : cairo_path_buf_t *buf;
740 :
741 : /* adjust size_ops to ensure that buf->points is naturally aligned */
742 0 : size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double));
743 0 : buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t));
744 0 : if (buf) {
745 0 : buf->num_ops = 0;
746 0 : buf->num_points = 0;
747 0 : buf->size_ops = size_ops;
748 0 : buf->size_points = size_points;
749 :
750 0 : buf->op = (cairo_path_op_t *) (buf + 1);
751 0 : buf->points = (cairo_point_t *) (buf->op + size_ops);
752 : }
753 :
754 0 : return buf;
755 : }
756 :
757 : static void
758 0 : _cairo_path_buf_destroy (cairo_path_buf_t *buf)
759 : {
760 0 : free (buf);
761 0 : }
762 :
763 : static void
764 0 : _cairo_path_buf_add_op (cairo_path_buf_t *buf,
765 : cairo_path_op_t op)
766 : {
767 0 : buf->op[buf->num_ops++] = op;
768 0 : }
769 :
770 : static void
771 0 : _cairo_path_buf_add_points (cairo_path_buf_t *buf,
772 : const cairo_point_t *points,
773 : int num_points)
774 : {
775 0 : memcpy (buf->points + buf->num_points,
776 : points,
777 : sizeof (points[0]) * num_points);
778 0 : buf->num_points += num_points;
779 0 : }
780 :
781 : cairo_status_t
782 0 : _cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
783 : cairo_direction_t dir,
784 : cairo_path_fixed_move_to_func_t *move_to,
785 : cairo_path_fixed_line_to_func_t *line_to,
786 : cairo_path_fixed_curve_to_func_t *curve_to,
787 : cairo_path_fixed_close_path_func_t *close_path,
788 : void *closure)
789 : {
790 0 : const uint8_t num_args[] = {
791 : 1, /* cairo_path_move_to */
792 : 1, /* cairo_path_op_line_to */
793 : 3, /* cairo_path_op_curve_to */
794 : 0, /* cairo_path_op_close_path */
795 : };
796 : cairo_status_t status;
797 : const cairo_path_buf_t *buf, *first;
798 0 : cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD);
799 0 : int step = forward ? 1 : -1;
800 :
801 0 : buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path);
802 : do {
803 : cairo_point_t *points;
804 : int start, stop, i;
805 :
806 0 : if (forward) {
807 0 : start = 0;
808 0 : stop = buf->num_ops;
809 0 : points = buf->points;
810 : } else {
811 0 : start = buf->num_ops - 1;
812 0 : stop = -1;
813 0 : points = buf->points + buf->num_points;
814 : }
815 :
816 0 : for (i = start; i != stop; i += step) {
817 0 : cairo_path_op_t op = buf->op[i];
818 :
819 0 : if (! forward)
820 0 : points -= num_args[(int) op];
821 :
822 0 : switch (op) {
823 : case CAIRO_PATH_OP_MOVE_TO:
824 0 : status = (*move_to) (closure, &points[0]);
825 0 : break;
826 : case CAIRO_PATH_OP_LINE_TO:
827 0 : status = (*line_to) (closure, &points[0]);
828 0 : break;
829 : case CAIRO_PATH_OP_CURVE_TO:
830 0 : status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
831 0 : break;
832 : default:
833 0 : ASSERT_NOT_REACHED;
834 : case CAIRO_PATH_OP_CLOSE_PATH:
835 0 : status = (*close_path) (closure);
836 0 : break;
837 : }
838 0 : if (unlikely (status))
839 0 : return status;
840 :
841 0 : if (forward)
842 0 : points += num_args[(int) op];
843 : }
844 0 : } while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first);
845 :
846 0 : return CAIRO_STATUS_SUCCESS;
847 : }
848 :
849 : typedef struct _cairo_path_fixed_append_closure {
850 : cairo_point_t offset;
851 : cairo_path_fixed_t *path;
852 : } cairo_path_fixed_append_closure_t;
853 :
854 : static cairo_status_t
855 0 : _append_move_to (void *abstract_closure,
856 : const cairo_point_t *point)
857 : {
858 0 : cairo_path_fixed_append_closure_t *closure = abstract_closure;
859 :
860 0 : return _cairo_path_fixed_move_to (closure->path,
861 0 : point->x + closure->offset.x,
862 0 : point->y + closure->offset.y);
863 : }
864 :
865 : static cairo_status_t
866 0 : _append_line_to (void *abstract_closure,
867 : const cairo_point_t *point)
868 : {
869 0 : cairo_path_fixed_append_closure_t *closure = abstract_closure;
870 :
871 0 : return _cairo_path_fixed_line_to (closure->path,
872 0 : point->x + closure->offset.x,
873 0 : point->y + closure->offset.y);
874 : }
875 :
876 : static cairo_status_t
877 0 : _append_curve_to (void *abstract_closure,
878 : const cairo_point_t *p0,
879 : const cairo_point_t *p1,
880 : const cairo_point_t *p2)
881 : {
882 0 : cairo_path_fixed_append_closure_t *closure = abstract_closure;
883 :
884 0 : return _cairo_path_fixed_curve_to (closure->path,
885 0 : p0->x + closure->offset.x,
886 0 : p0->y + closure->offset.y,
887 0 : p1->x + closure->offset.x,
888 0 : p1->y + closure->offset.y,
889 0 : p2->x + closure->offset.x,
890 0 : p2->y + closure->offset.y);
891 : }
892 :
893 : static cairo_status_t
894 0 : _append_close_path (void *abstract_closure)
895 : {
896 0 : cairo_path_fixed_append_closure_t *closure = abstract_closure;
897 :
898 0 : return _cairo_path_fixed_close_path (closure->path);
899 : }
900 :
901 : cairo_status_t
902 0 : _cairo_path_fixed_append (cairo_path_fixed_t *path,
903 : const cairo_path_fixed_t *other,
904 : cairo_direction_t dir,
905 : cairo_fixed_t tx,
906 : cairo_fixed_t ty)
907 : {
908 : cairo_path_fixed_append_closure_t closure;
909 :
910 0 : closure.path = path;
911 0 : closure.offset.x = tx;
912 0 : closure.offset.y = ty;
913 :
914 0 : return _cairo_path_fixed_interpret (other, dir,
915 : _append_move_to,
916 : _append_line_to,
917 : _append_curve_to,
918 : _append_close_path,
919 : &closure);
920 : }
921 :
922 : static void
923 0 : _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
924 : cairo_fixed_t offx,
925 : cairo_fixed_t offy,
926 : cairo_fixed_t scalex,
927 : cairo_fixed_t scaley)
928 : {
929 : cairo_path_buf_t *buf;
930 : unsigned int i;
931 :
932 0 : if (path->maybe_fill_region) {
933 0 : path->maybe_fill_region = _cairo_fixed_is_integer (offx) &&
934 0 : _cairo_fixed_is_integer (offy) &&
935 0 : _cairo_fixed_is_integer (scalex) &&
936 0 : _cairo_fixed_is_integer (scaley);
937 : }
938 :
939 0 : cairo_path_foreach_buf_start (buf, path) {
940 0 : for (i = 0; i < buf->num_points; i++) {
941 0 : if (scalex != CAIRO_FIXED_ONE)
942 0 : buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex);
943 0 : buf->points[i].x += offx;
944 :
945 0 : if (scaley != CAIRO_FIXED_ONE)
946 0 : buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
947 0 : buf->points[i].y += offy;
948 : }
949 0 : } cairo_path_foreach_buf_end (buf, path);
950 :
951 0 : path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx;
952 0 : path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx;
953 :
954 0 : path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy;
955 0 : path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy;
956 0 : }
957 :
958 : void
959 0 : _cairo_path_fixed_translate (cairo_path_fixed_t *path,
960 : cairo_fixed_t offx,
961 : cairo_fixed_t offy)
962 : {
963 : cairo_path_buf_t *buf;
964 : unsigned int i;
965 :
966 0 : if (offx == 0 && offy == 0)
967 0 : return;
968 :
969 0 : if (path->maybe_fill_region &&
970 0 : ! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy)))
971 : {
972 0 : path->maybe_fill_region = FALSE;
973 : }
974 :
975 0 : path->last_move_point.x += offx;
976 0 : path->last_move_point.y += offy;
977 0 : path->current_point.x += offx;
978 0 : path->current_point.y += offy;
979 :
980 0 : cairo_path_foreach_buf_start (buf, path) {
981 0 : for (i = 0; i < buf->num_points; i++) {
982 0 : buf->points[i].x += offx;
983 0 : buf->points[i].y += offy;
984 : }
985 0 : } cairo_path_foreach_buf_end (buf, path);
986 :
987 0 : path->extents.p1.x += offx;
988 0 : path->extents.p1.y += offy;
989 0 : path->extents.p2.x += offx;
990 0 : path->extents.p2.y += offy;
991 : }
992 :
993 : /**
994 : * _cairo_path_fixed_transform:
995 : * @path: a #cairo_path_fixed_t to be transformed
996 : * @matrix: a #cairo_matrix_t
997 : *
998 : * Transform the fixed-point path according to the given matrix.
999 : * There is a fast path for the case where @matrix has no rotation
1000 : * or shear.
1001 : **/
1002 : void
1003 0 : _cairo_path_fixed_transform (cairo_path_fixed_t *path,
1004 : const cairo_matrix_t *matrix)
1005 : {
1006 : cairo_path_buf_t *buf;
1007 : unsigned int i;
1008 : double dx, dy;
1009 :
1010 : /* XXX current_point, last_move_to */
1011 :
1012 0 : if (matrix->yx == 0.0 && matrix->xy == 0.0) {
1013 : /* Fast path for the common case of scale+transform */
1014 0 : if (matrix->xx == 1. && matrix->yy == 1.) {
1015 0 : _cairo_path_fixed_translate (path,
1016 : _cairo_fixed_from_double (matrix->x0),
1017 : _cairo_fixed_from_double (matrix->y0));
1018 : } else {
1019 0 : _cairo_path_fixed_offset_and_scale (path,
1020 : _cairo_fixed_from_double (matrix->x0),
1021 : _cairo_fixed_from_double (matrix->y0),
1022 : _cairo_fixed_from_double (matrix->xx),
1023 : _cairo_fixed_from_double (matrix->yy));
1024 : }
1025 0 : return;
1026 : }
1027 :
1028 0 : path->extents.p1.x = path->extents.p1.y = INT_MAX;
1029 0 : path->extents.p2.x = path->extents.p2.y = INT_MIN;
1030 0 : path->maybe_fill_region = FALSE;
1031 0 : cairo_path_foreach_buf_start (buf, path) {
1032 0 : for (i = 0; i < buf->num_points; i++) {
1033 0 : dx = _cairo_fixed_to_double (buf->points[i].x);
1034 0 : dy = _cairo_fixed_to_double (buf->points[i].y);
1035 :
1036 0 : cairo_matrix_transform_point (matrix, &dx, &dy);
1037 :
1038 0 : buf->points[i].x = _cairo_fixed_from_double (dx);
1039 0 : buf->points[i].y = _cairo_fixed_from_double (dy);
1040 :
1041 : /* XXX need to eliminate surplus move-to's? */
1042 0 : _cairo_path_fixed_extents_add (path, &buf->points[i]);
1043 : }
1044 0 : } cairo_path_foreach_buf_end (buf, path);
1045 : }
1046 :
1047 : cairo_bool_t
1048 0 : _cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
1049 : const cairo_path_fixed_t *other)
1050 : {
1051 : const cairo_path_buf_t *path_buf, *other_buf;
1052 :
1053 0 : if (path->current_point.x != other->current_point.x ||
1054 0 : path->current_point.y != other->current_point.y ||
1055 0 : path->has_current_point != other->has_current_point ||
1056 0 : path->has_curve_to != other->has_curve_to ||
1057 0 : path->is_rectilinear != other->is_rectilinear ||
1058 0 : path->maybe_fill_region != other->maybe_fill_region ||
1059 0 : path->last_move_point.x != other->last_move_point.x ||
1060 0 : path->last_move_point.y != other->last_move_point.y)
1061 : {
1062 0 : return FALSE;
1063 : }
1064 :
1065 0 : other_buf = cairo_path_head (other);
1066 0 : cairo_path_foreach_buf_start (path_buf, path) {
1067 0 : if (path_buf->num_ops != other_buf->num_ops ||
1068 0 : path_buf->num_points != other_buf->num_points ||
1069 0 : memcmp (path_buf->op, other_buf->op,
1070 0 : sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 ||
1071 0 : memcmp (path_buf->points, other_buf->points,
1072 0 : sizeof (cairo_point_t) * path_buf->num_points) != 0)
1073 : {
1074 0 : return FALSE;
1075 : }
1076 0 : other_buf = cairo_path_buf_next (other_buf);
1077 0 : } cairo_path_foreach_buf_end (path_buf, path);
1078 :
1079 0 : return TRUE;
1080 : }
1081 :
1082 : /* Closure for path flattening */
1083 : typedef struct cairo_path_flattener {
1084 : double tolerance;
1085 : cairo_point_t current_point;
1086 : cairo_path_fixed_move_to_func_t *move_to;
1087 : cairo_path_fixed_line_to_func_t *line_to;
1088 : cairo_path_fixed_close_path_func_t *close_path;
1089 : void *closure;
1090 : } cpf_t;
1091 :
1092 : static cairo_status_t
1093 0 : _cpf_move_to (void *closure,
1094 : const cairo_point_t *point)
1095 : {
1096 0 : cpf_t *cpf = closure;
1097 :
1098 0 : cpf->current_point = *point;
1099 :
1100 0 : return cpf->move_to (cpf->closure, point);
1101 : }
1102 :
1103 : static cairo_status_t
1104 0 : _cpf_line_to (void *closure,
1105 : const cairo_point_t *point)
1106 : {
1107 0 : cpf_t *cpf = closure;
1108 :
1109 0 : cpf->current_point = *point;
1110 :
1111 0 : return cpf->line_to (cpf->closure, point);
1112 : }
1113 :
1114 : static cairo_status_t
1115 0 : _cpf_curve_to (void *closure,
1116 : const cairo_point_t *p1,
1117 : const cairo_point_t *p2,
1118 : const cairo_point_t *p3)
1119 : {
1120 0 : cpf_t *cpf = closure;
1121 : cairo_spline_t spline;
1122 :
1123 0 : cairo_point_t *p0 = &cpf->current_point;
1124 :
1125 0 : if (! _cairo_spline_init (&spline,
1126 0 : cpf->line_to,
1127 : cpf->closure,
1128 : p0, p1, p2, p3))
1129 : {
1130 0 : return _cpf_line_to (closure, p3);
1131 : }
1132 :
1133 0 : cpf->current_point = *p3;
1134 :
1135 0 : return _cairo_spline_decompose (&spline, cpf->tolerance);
1136 : }
1137 :
1138 : static cairo_status_t
1139 0 : _cpf_close_path (void *closure)
1140 : {
1141 0 : cpf_t *cpf = closure;
1142 :
1143 0 : return cpf->close_path (cpf->closure);
1144 : }
1145 :
1146 : cairo_status_t
1147 0 : _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
1148 : cairo_direction_t dir,
1149 : cairo_path_fixed_move_to_func_t *move_to,
1150 : cairo_path_fixed_line_to_func_t *line_to,
1151 : cairo_path_fixed_close_path_func_t *close_path,
1152 : void *closure,
1153 : double tolerance)
1154 : {
1155 : cpf_t flattener;
1156 :
1157 0 : if (! path->has_curve_to) {
1158 0 : return _cairo_path_fixed_interpret (path, dir,
1159 : move_to,
1160 : line_to,
1161 : NULL,
1162 : close_path,
1163 : closure);
1164 : }
1165 :
1166 0 : flattener.tolerance = tolerance;
1167 0 : flattener.move_to = move_to;
1168 0 : flattener.line_to = line_to;
1169 0 : flattener.close_path = close_path;
1170 0 : flattener.closure = closure;
1171 0 : return _cairo_path_fixed_interpret (path, dir,
1172 : _cpf_move_to,
1173 : _cpf_line_to,
1174 : _cpf_curve_to,
1175 : _cpf_close_path,
1176 : &flattener);
1177 : }
1178 :
1179 : static inline void
1180 0 : _canonical_box (cairo_box_t *box,
1181 : const cairo_point_t *p1,
1182 : const cairo_point_t *p2)
1183 : {
1184 0 : if (p1->x <= p2->x) {
1185 0 : box->p1.x = p1->x;
1186 0 : box->p2.x = p2->x;
1187 : } else {
1188 0 : box->p1.x = p2->x;
1189 0 : box->p2.x = p1->x;
1190 : }
1191 :
1192 0 : if (p1->y <= p2->y) {
1193 0 : box->p1.y = p1->y;
1194 0 : box->p2.y = p2->y;
1195 : } else {
1196 0 : box->p1.y = p2->y;
1197 0 : box->p2.y = p1->y;
1198 : }
1199 0 : }
1200 :
1201 : /*
1202 : * Check whether the given path contains a single rectangle.
1203 : */
1204 : cairo_bool_t
1205 0 : _cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
1206 : cairo_box_t *box)
1207 : {
1208 0 : const cairo_path_buf_t *buf = cairo_path_head (path);
1209 :
1210 0 : if (! path->is_rectilinear)
1211 0 : return FALSE;
1212 :
1213 : /* Do we have the right number of ops? */
1214 0 : if (buf->num_ops < 4 || buf->num_ops > 6)
1215 0 : return FALSE;
1216 :
1217 : /* Check whether the ops are those that would be used for a rectangle */
1218 0 : if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
1219 0 : buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
1220 0 : buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
1221 0 : buf->op[3] != CAIRO_PATH_OP_LINE_TO)
1222 : {
1223 0 : return FALSE;
1224 : }
1225 :
1226 : /* we accept an implicit close for filled paths */
1227 0 : if (buf->num_ops > 4) {
1228 : /* Now, there are choices. The rectangle might end with a LINE_TO
1229 : * (to the original point), but this isn't required. If it
1230 : * doesn't, then it must end with a CLOSE_PATH. */
1231 0 : if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
1232 0 : if (buf->points[4].x != buf->points[0].x ||
1233 0 : buf->points[4].y != buf->points[0].y)
1234 0 : return FALSE;
1235 0 : } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
1236 0 : return FALSE;
1237 : }
1238 :
1239 0 : if (buf->num_ops == 6) {
1240 : /* A trailing CLOSE_PATH or MOVE_TO is ok */
1241 0 : if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO &&
1242 0 : buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH)
1243 0 : return FALSE;
1244 : }
1245 : }
1246 :
1247 : /* Ok, we may have a box, if the points line up */
1248 0 : if (buf->points[0].y == buf->points[1].y &&
1249 0 : buf->points[1].x == buf->points[2].x &&
1250 0 : buf->points[2].y == buf->points[3].y &&
1251 0 : buf->points[3].x == buf->points[0].x)
1252 : {
1253 0 : _canonical_box (box, &buf->points[0], &buf->points[2]);
1254 0 : return TRUE;
1255 : }
1256 :
1257 0 : if (buf->points[0].x == buf->points[1].x &&
1258 0 : buf->points[1].y == buf->points[2].y &&
1259 0 : buf->points[2].x == buf->points[3].x &&
1260 0 : buf->points[3].y == buf->points[0].y)
1261 : {
1262 0 : _canonical_box (box, &buf->points[0], &buf->points[2]);
1263 0 : return TRUE;
1264 : }
1265 :
1266 0 : return FALSE;
1267 : }
1268 :
1269 : /*
1270 : * Check whether the given path contains a single rectangle
1271 : * that is logically equivalent to:
1272 : * <informalexample><programlisting>
1273 : * cairo_move_to (cr, x, y);
1274 : * cairo_rel_line_to (cr, width, 0);
1275 : * cairo_rel_line_to (cr, 0, height);
1276 : * cairo_rel_line_to (cr, -width, 0);
1277 : * cairo_close_path (cr);
1278 : * </programlisting></informalexample>
1279 : */
1280 : cairo_bool_t
1281 0 : _cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path,
1282 : cairo_box_t *box)
1283 : {
1284 : const cairo_path_buf_t *buf;
1285 :
1286 0 : if (! _cairo_path_fixed_is_box (path, box))
1287 0 : return FALSE;
1288 :
1289 0 : buf = cairo_path_head (path);
1290 0 : if (buf->points[0].y == buf->points[1].y)
1291 0 : return TRUE;
1292 :
1293 0 : return FALSE;
1294 : }
1295 :
1296 : void
1297 0 : _cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
1298 : const cairo_path_fixed_t *path)
1299 : {
1300 0 : iter->first = iter->buf = cairo_path_head (path);
1301 0 : iter->n_op = 0;
1302 0 : iter->n_point = 0;
1303 0 : }
1304 :
1305 : static cairo_bool_t
1306 0 : _cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter)
1307 : {
1308 0 : if (++iter->n_op >= iter->buf->num_ops) {
1309 0 : iter->buf = cairo_path_buf_next (iter->buf);
1310 0 : if (iter->buf == iter->first) {
1311 0 : iter->buf = NULL;
1312 0 : return FALSE;
1313 : }
1314 :
1315 0 : iter->n_op = 0;
1316 0 : iter->n_point = 0;
1317 : }
1318 :
1319 0 : return TRUE;
1320 : }
1321 :
1322 : cairo_bool_t
1323 0 : _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
1324 : cairo_box_t *box)
1325 : {
1326 : cairo_point_t points[5];
1327 : cairo_path_fixed_iter_t iter;
1328 :
1329 0 : if (_iter->buf == NULL)
1330 0 : return FALSE;
1331 :
1332 0 : iter = *_iter;
1333 :
1334 0 : if (iter.n_op == iter.buf->num_ops &&
1335 0 : ! _cairo_path_fixed_iter_next_op (&iter))
1336 : {
1337 0 : return FALSE;
1338 : }
1339 :
1340 : /* Check whether the ops are those that would be used for a rectangle */
1341 0 : if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
1342 0 : return FALSE;
1343 0 : points[0] = iter.buf->points[iter.n_point++];
1344 0 : if (! _cairo_path_fixed_iter_next_op (&iter))
1345 0 : return FALSE;
1346 :
1347 0 : if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
1348 0 : return FALSE;
1349 0 : points[1] = iter.buf->points[iter.n_point++];
1350 0 : if (! _cairo_path_fixed_iter_next_op (&iter))
1351 0 : return FALSE;
1352 :
1353 0 : if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
1354 0 : return FALSE;
1355 0 : points[2] = iter.buf->points[iter.n_point++];
1356 0 : if (! _cairo_path_fixed_iter_next_op (&iter))
1357 0 : return FALSE;
1358 :
1359 0 : if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
1360 0 : return FALSE;
1361 0 : points[3] = iter.buf->points[iter.n_point++];
1362 0 : if (! _cairo_path_fixed_iter_next_op (&iter))
1363 0 : return FALSE;
1364 :
1365 : /* Now, there are choices. The rectangle might end with a LINE_TO
1366 : * (to the original point), but this isn't required. If it
1367 : * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
1368 0 : if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO)
1369 : {
1370 0 : points[4] = iter.buf->points[iter.n_point++];
1371 0 : if (points[4].x != points[0].x || points[4].y != points[0].y)
1372 0 : return FALSE;
1373 : }
1374 0 : else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH ||
1375 0 : iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO))
1376 : {
1377 0 : return FALSE;
1378 : }
1379 0 : if (! _cairo_path_fixed_iter_next_op (&iter))
1380 0 : return FALSE;
1381 :
1382 : /* Ok, we may have a box, if the points line up */
1383 0 : if (points[0].y == points[1].y &&
1384 0 : points[1].x == points[2].x &&
1385 0 : points[2].y == points[3].y &&
1386 0 : points[3].x == points[0].x)
1387 : {
1388 0 : box->p1 = points[0];
1389 0 : box->p2 = points[2];
1390 0 : *_iter = iter;
1391 0 : return TRUE;
1392 : }
1393 :
1394 0 : if (points[0].x == points[1].x &&
1395 0 : points[1].y == points[2].y &&
1396 0 : points[2].x == points[3].x &&
1397 0 : points[3].y == points[0].y)
1398 : {
1399 0 : box->p1 = points[1];
1400 0 : box->p2 = points[3];
1401 0 : *_iter = iter;
1402 0 : return TRUE;
1403 : }
1404 :
1405 0 : return FALSE;
1406 : }
1407 :
1408 : cairo_bool_t
1409 0 : _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
1410 : {
1411 0 : if (iter->buf == NULL)
1412 0 : return TRUE;
1413 :
1414 0 : if (iter->n_op == iter->buf->num_ops)
1415 0 : return TRUE;
1416 :
1417 0 : if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO &&
1418 0 : iter->buf->num_ops == iter->n_op + 1)
1419 : {
1420 0 : return TRUE;
1421 : }
1422 :
1423 0 : return FALSE;
1424 : }
|