Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1892 serge 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 
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
_cairo_path_fixed_init (cairo_path_fixed_t *path)
85
{
86
    VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
87
 
88
    cairo_list_init (&path->buf.base.link);
89
 
90
    path->buf.base.num_ops = 0;
91
    path->buf.base.num_points = 0;
92
    path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
93
    path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
94
    path->buf.base.op = path->buf.op;
95
    path->buf.base.points = path->buf.points;
96
 
97
    path->current_point.x = 0;
98
    path->current_point.y = 0;
99
    path->last_move_point = path->current_point;
100
    path->has_last_move_point = FALSE;
101
    path->has_current_point = FALSE;
102
    path->has_curve_to = FALSE;
103
    path->is_rectilinear = TRUE;
104
    path->maybe_fill_region = TRUE;
105
    path->is_empty_fill = TRUE;
106
 
107
    path->extents.p1.x = path->extents.p1.y = INT_MAX;
108
    path->extents.p2.x = path->extents.p2.y = INT_MIN;
109
}
110
 
111
cairo_status_t
112
_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
    cairo_list_init (&path->buf.base.link);
121
 
122
    path->buf.base.op = path->buf.op;
123
    path->buf.base.points = path->buf.points;
124
    path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
125
    path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
126
 
127
    path->current_point = other->current_point;
128
    path->last_move_point = other->last_move_point;
129
    path->has_last_move_point = other->has_last_move_point;
130
    path->has_current_point = other->has_current_point;
131
    path->has_curve_to = other->has_curve_to;
132
    path->is_rectilinear = other->is_rectilinear;
133
    path->maybe_fill_region = other->maybe_fill_region;
134
    path->is_empty_fill = other->is_empty_fill;
135
 
136
    path->extents = other->extents;
137
 
138
    path->buf.base.num_ops = other->buf.base.num_ops;
139
    path->buf.base.num_points = other->buf.base.num_points;
140
    memcpy (path->buf.op, other->buf.base.op,
141
	    other->buf.base.num_ops * sizeof (other->buf.op[0]));
142
    memcpy (path->buf.points, other->buf.points,
143
	    other->buf.base.num_points * sizeof (other->buf.points[0]));
144
 
145
    num_points = num_ops = 0;
146
    for (other_buf = cairo_path_buf_next (cairo_path_head (other));
147
	 other_buf != cairo_path_head (other);
148
	 other_buf = cairo_path_buf_next (other_buf))
149
    {
150
	num_ops    += other_buf->num_ops;
151
	num_points += other_buf->num_points;
152
    }
153
 
154
    if (num_ops) {
155
	buf = _cairo_path_buf_create (num_ops, num_points);
156
	if (unlikely (buf == NULL)) {
157
	    _cairo_path_fixed_fini (path);
158
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
159
	}
160
 
161
	for (other_buf = cairo_path_buf_next (cairo_path_head (other));
162
	     other_buf != cairo_path_head (other);
163
	     other_buf = cairo_path_buf_next (other_buf))
164
	{
165
	    memcpy (buf->op + buf->num_ops, other_buf->op,
166
		    other_buf->num_ops * sizeof (buf->op[0]));
167
	    buf->num_ops += other_buf->num_ops;
168
 
169
	    memcpy (buf->points + buf->num_points, other_buf->points,
170
		    other_buf->num_points * sizeof (buf->points[0]));
171
	    buf->num_points += other_buf->num_points;
172
	}
173
 
174
	_cairo_path_fixed_add_buf (path, buf);
175
    }
176
 
177
    return CAIRO_STATUS_SUCCESS;
178
}
179
 
180
unsigned long
181
_cairo_path_fixed_hash (const cairo_path_fixed_t *path)
182
{
183
    unsigned long hash = _CAIRO_HASH_INIT_VALUE;
184
    const cairo_path_buf_t *buf;
185
    int num_points, num_ops;
186
 
187
    hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents));
188
 
189
    num_ops = num_points = 0;
190
    cairo_path_foreach_buf_start (buf, path) {
191
	hash = _cairo_hash_bytes (hash, buf->op,
192
			          buf->num_ops * sizeof (buf->op[0]));
193
	hash = _cairo_hash_bytes (hash, buf->points,
194
			          buf->num_points * sizeof (buf->points[0]));
195
 
196
	num_ops    += buf->num_ops;
197
	num_points += buf->num_points;
198
    } cairo_path_foreach_buf_end (buf, path);
199
 
200
    hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops));
201
    hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points));
202
 
203
    return hash;
204
}
205
 
206
unsigned long
207
_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
    num_ops = num_points = 0;
213
    cairo_path_foreach_buf_start (buf, path) {
214
	num_ops    += buf->num_ops;
215
	num_points += buf->num_points;
216
    } cairo_path_foreach_buf_end (buf, path);
217
 
218
    return num_ops * sizeof (buf->op[0]) +
219
	   num_points * sizeof (buf->points[0]);
220
}
221
 
222
cairo_bool_t
223
_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
    if (a == b)
233
	return TRUE;
234
 
235
    /* use the flags to quickly differentiate based on contents */
236
    if (a->is_empty_fill != b->is_empty_fill ||
237
	a->has_curve_to != b->has_curve_to ||
238
	a->maybe_fill_region != b->maybe_fill_region ||
239
	a->is_rectilinear != b->is_rectilinear)
240
    {
241
	return FALSE;
242
    }
243
 
244
    if (a->extents.p1.x != b->extents.p1.x ||
245
	a->extents.p1.y != b->extents.p1.y ||
246
	a->extents.p2.x != b->extents.p2.x ||
247
	a->extents.p2.y != b->extents.p2.y)
248
    {
249
	return FALSE;
250
    }
251
 
252
    num_ops_a = num_points_a = 0;
253
    cairo_path_foreach_buf_start (buf_a, a) {
254
	num_ops_a    += buf_a->num_ops;
255
	num_points_a += buf_a->num_points;
256
    } cairo_path_foreach_buf_end (buf_a, a);
257
 
258
    num_ops_b = num_points_b = 0;
259
    cairo_path_foreach_buf_start (buf_b, b) {
260
	num_ops_b    += buf_b->num_ops;
261
	num_points_b += buf_b->num_points;
262
    } cairo_path_foreach_buf_end (buf_b, b);
263
 
264
    if (num_ops_a == 0 && num_ops_b == 0)
265
	return TRUE;
266
 
267
    if (num_ops_a != num_ops_b || num_points_a != num_points_b)
268
	return FALSE;
269
 
270
    buf_a = cairo_path_head (a);
271
    num_points_a = buf_a->num_points;
272
    num_ops_a = buf_a->num_ops;
273
    ops_a = buf_a->op;
274
    points_a = buf_a->points;
275
 
276
    buf_b = cairo_path_head (b);
277
    num_points_b = buf_b->num_points;
278
    num_ops_b = buf_b->num_ops;
279
    ops_b = buf_b->op;
280
    points_b = buf_b->points;
281
 
282
    while (TRUE) {
283
	int num_ops = MIN (num_ops_a, num_ops_b);
284
	int num_points = MIN (num_points_a, num_points_b);
285
 
286
	if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t)))
287
	    return FALSE;
288
	if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t)))
289
	    return FALSE;
290
 
291
	num_ops_a -= num_ops;
292
	ops_a += num_ops;
293
	num_points_a -= num_points;
294
	points_a += num_points;
295
	if (num_ops_a == 0 || num_points_a == 0) {
296
	    if (num_ops_a || num_points_a)
297
		return FALSE;
298
 
299
	    buf_a = cairo_path_buf_next (buf_a);
300
	    if (buf_a == cairo_path_head (a))
301
		break;
302
 
303
	    num_points_a = buf_a->num_points;
304
	    num_ops_a = buf_a->num_ops;
305
	    ops_a = buf_a->op;
306
	    points_a = buf_a->points;
307
	}
308
 
309
	num_ops_b -= num_ops;
310
	ops_b += num_ops;
311
	num_points_b -= num_points;
312
	points_b += num_points;
313
	if (num_ops_b == 0 || num_points_b == 0) {
314
	    if (num_ops_b || num_points_b)
315
		return FALSE;
316
 
317
	    buf_b = cairo_path_buf_next (buf_b);
318
	    if (buf_b == cairo_path_head (b))
319
		break;
320
 
321
	    num_points_b = buf_b->num_points;
322
	    num_ops_b = buf_b->num_ops;
323
	    ops_b = buf_b->op;
324
	    points_b = buf_b->points;
325
	}
326
    }
327
 
328
    return TRUE;
329
}
330
 
331
cairo_path_fixed_t *
332
_cairo_path_fixed_create (void)
333
{
334
    cairo_path_fixed_t	*path;
335
 
336
    path = malloc (sizeof (cairo_path_fixed_t));
337
    if (!path) {
338
	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
339
	return NULL;
340
    }
341
 
342
    _cairo_path_fixed_init (path);
343
    return path;
344
}
345
 
346
void
347
_cairo_path_fixed_fini (cairo_path_fixed_t *path)
348
{
349
    cairo_path_buf_t *buf;
350
 
351
    buf = cairo_path_buf_next (cairo_path_head (path));
352
    while (buf != cairo_path_head (path)) {
353
	cairo_path_buf_t *this = buf;
354
	buf = cairo_path_buf_next (buf);
355
	_cairo_path_buf_destroy (this);
356
    }
357
 
358
    VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t)));
359
}
360
 
361
void
362
_cairo_path_fixed_destroy (cairo_path_fixed_t *path)
363
{
364
    _cairo_path_fixed_fini (path);
365
    free (path);
366
}
367
 
368
static cairo_path_op_t
369
_cairo_path_last_op (cairo_path_fixed_t *path)
370
{
371
    cairo_path_buf_t *buf;
372
 
373
    buf = cairo_path_tail (path);
374
    if (buf->num_ops == 0)
375
	return -1;
376
 
377
    return buf->op[buf->num_ops - 1];
378
}
379
 
380
static inline void
381
_cairo_path_fixed_extents_add (cairo_path_fixed_t *path,
382
			       const cairo_point_t *point)
383
{
384
    if (point->x < path->extents.p1.x)
385
	path->extents.p1.x = point->x;
386
    if (point->y < path->extents.p1.y)
387
	path->extents.p1.y = point->y;
388
 
389
    if (point->x > path->extents.p2.x)
390
	path->extents.p2.x = point->x;
391
    if (point->y > path->extents.p2.y)
392
	path->extents.p2.y = point->y;
393
}
394
 
395
cairo_status_t
396
_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
    point.x = x;
404
    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
    if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
409
	cairo_path_buf_t *buf;
410
 
411
	buf = cairo_path_tail (path);
412
	buf->points[buf->num_points - 1] = point;
413
    } else {
414
	status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
415
	if (unlikely (status))
416
	    return status;
417
 
418
	if (path->has_current_point && path->is_rectilinear) {
419
	    /* a move-to is first an implicit close */
420
	    path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
421
				   path->current_point.y == path->last_move_point.y;
422
	    path->maybe_fill_region &= path->is_rectilinear;
423
	}
424
	if (path->maybe_fill_region) {
425
	    path->maybe_fill_region =
426
		_cairo_fixed_is_integer (path->last_move_point.x) &&
427
		_cairo_fixed_is_integer (path->last_move_point.y);
428
	}
429
    }
430
 
431
    path->current_point = point;
432
    path->last_move_point = point;
433
    path->has_last_move_point = TRUE;
434
    path->has_current_point = TRUE;
435
 
436
    return CAIRO_STATUS_SUCCESS;
437
}
438
 
439
void
440
_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
441
{
442
    path->has_current_point = FALSE;
443
}
444
 
445
cairo_status_t
446
_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
447
			       cairo_fixed_t	   dx,
448
			       cairo_fixed_t	   dy)
449
{
450
    if (unlikely (! path->has_current_point))
451
	return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
452
 
453
    return _cairo_path_fixed_move_to (path,
454
				      path->current_point.x + dx,
455
				      path->current_point.y + dy);
456
 
457
}
458
 
459
cairo_status_t
460
_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
    point.x = x;
468
    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
    if (! path->has_current_point)
476
	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
    if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
484
	if (x == path->current_point.x && y == path->current_point.y)
485
	    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
    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
	buf = cairo_path_tail (path);
496
	if (likely (buf->num_points >= 2)) {
497
	    p = &buf->points[buf->num_points-2];
498
	} else {
499
	    cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
500
	    p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
501
	}
502
 
503
	if (p->x == path->current_point.x && p->y == path->current_point.y) {
504
	    /* previous line element was degenerate, replace */
505
	    buf->points[buf->num_points - 1] = point;
506
	    goto FLAGS;
507
	} else {
508
	    cairo_slope_t prev, self;
509
 
510
	    _cairo_slope_init (&prev, p, &path->current_point);
511
	    _cairo_slope_init (&self, &path->current_point, &point);
512
	    if (_cairo_slope_equal (&prev, &self) &&
513
		/* cannot trim anti-parallel segments whilst stroking */
514
		! _cairo_slope_backwards (&prev, &self))
515
	    {
516
		buf->points[buf->num_points - 1] = point;
517
		goto FLAGS;
518
	    }
519
	}
520
    }
521
 
522
    status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
523
    if (unlikely (status))
524
	return status;
525
 
526
  FLAGS:
527
    if (path->is_rectilinear) {
528
	path->is_rectilinear = path->current_point.x == x ||
529
			       path->current_point.y == y;
530
	path->maybe_fill_region &= path->is_rectilinear;
531
    }
532
    if (path->maybe_fill_region) {
533
	path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
534
				  _cairo_fixed_is_integer (y);
535
    }
536
    if (path->is_empty_fill) {
537
	path->is_empty_fill = path->current_point.x == x &&
538
			      path->current_point.y == y;
539
    }
540
 
541
    path->current_point = point;
542
    if (path->has_last_move_point) {
543
	_cairo_path_fixed_extents_add (path, &path->last_move_point);
544
	path->has_last_move_point = FALSE;
545
    }
546
    _cairo_path_fixed_extents_add (path, &point);
547
    return CAIRO_STATUS_SUCCESS;
548
}
549
 
550
cairo_status_t
551
_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
552
			       cairo_fixed_t	   dx,
553
			       cairo_fixed_t	   dy)
554
{
555
    if (unlikely (! path->has_current_point))
556
	return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
557
 
558
    return _cairo_path_fixed_line_to (path,
559
				      path->current_point.x + dx,
560
				      path->current_point.y + dy);
561
}
562
 
563
cairo_status_t
564
_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
    if (! path->has_current_point) {
574
	status = _cairo_path_fixed_move_to (path, x0, y0);
575
	if (unlikely (status))
576
	    return status;
577
    }
578
 
579
    point[0].x = x0; point[0].y = y0;
580
    point[1].x = x1; point[1].y = y1;
581
    point[2].x = x2; point[2].y = y2;
582
    status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
583
    if (unlikely (status))
584
	return status;
585
 
586
    path->current_point = point[2];
587
    path->has_current_point = TRUE;
588
    path->is_empty_fill = FALSE;
589
    path->has_curve_to = TRUE;
590
    path->is_rectilinear = FALSE;
591
    path->maybe_fill_region = FALSE;
592
 
593
    /* coarse bounds */
594
    if (path->has_last_move_point) {
595
	_cairo_path_fixed_extents_add (path, &path->last_move_point);
596
	path->has_last_move_point = FALSE;
597
    }
598
    _cairo_path_fixed_extents_add (path, &point[0]);
599
    _cairo_path_fixed_extents_add (path, &point[1]);
600
    _cairo_path_fixed_extents_add (path, &point[2]);
601
 
602
    return CAIRO_STATUS_SUCCESS;
603
}
604
 
605
cairo_status_t
606
_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
    if (unlikely (! path->has_current_point))
612
	return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
613
 
614
    return _cairo_path_fixed_curve_to (path,
615
				       path->current_point.x + dx0,
616
				       path->current_point.y + dy0,
617
 
618
				       path->current_point.x + dx1,
619
				       path->current_point.y + dy1,
620
 
621
				       path->current_point.x + dx2,
622
				       path->current_point.y + dy2);
623
}
624
 
625
cairo_status_t
626
_cairo_path_fixed_close_path (cairo_path_fixed_t *path)
627
{
628
    cairo_status_t status;
629
 
630
    if (! path->has_current_point)
631
	return CAIRO_STATUS_SUCCESS;
632
 
633
    /* If the previous op was also a LINE_TO back to the start, discard it */
634
    if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
635
	if (path->current_point.x == path->last_move_point.x &&
636
	    path->current_point.y == path->last_move_point.y)
637
	{
638
	    cairo_path_buf_t *buf;
639
	    cairo_point_t *p;
640
 
641
	    buf = cairo_path_tail (path);
642
	    if (likely (buf->num_points >= 2)) {
643
		p = &buf->points[buf->num_points-2];
644
	    } else {
645
		cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
646
		p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
647
	    }
648
 
649
	    path->current_point = *p;
650
	    buf->num_ops--;
651
	    buf->num_points--;
652
	}
653
    }
654
 
655
    status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
656
    if (unlikely (status))
657
	return status;
658
 
659
    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
_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
666
				     cairo_fixed_t	*x,
667
				     cairo_fixed_t	*y)
668
{
669
    if (! path->has_current_point)
670
	return FALSE;
671
 
672
    *x = path->current_point.x;
673
    *y = path->current_point.y;
674
 
675
    return TRUE;
676
}
677
 
678
static cairo_status_t
679
_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
    cairo_path_buf_t *buf = cairo_path_tail (path);
685
 
686
    if (buf->num_ops + 1 > buf->size_ops ||
687
	buf->num_points + num_points > buf->size_points)
688
    {
689
	buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2);
690
	if (unlikely (buf == NULL))
691
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
692
 
693
	_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
    _cairo_path_buf_add_op (buf, op);
723
    _cairo_path_buf_add_points (buf, points, num_points);
724
 
725
    return CAIRO_STATUS_SUCCESS;
726
}
727
 
728
static void
729
_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
730
			   cairo_path_buf_t   *buf)
731
{
732
    cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link);
733
}
734
 
735
COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1);
736
static cairo_path_buf_t *
737
_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
    size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double));
743
    buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t));
744
    if (buf) {
745
	buf->num_ops = 0;
746
	buf->num_points = 0;
747
	buf->size_ops = size_ops;
748
	buf->size_points = size_points;
749
 
750
	buf->op = (cairo_path_op_t *) (buf + 1);
751
	buf->points = (cairo_point_t *) (buf->op + size_ops);
752
    }
753
 
754
    return buf;
755
}
756
 
757
static void
758
_cairo_path_buf_destroy (cairo_path_buf_t *buf)
759
{
760
    free (buf);
761
}
762
 
763
static void
764
_cairo_path_buf_add_op (cairo_path_buf_t *buf,
765
			cairo_path_op_t	  op)
766
{
767
    buf->op[buf->num_ops++] = op;
768
}
769
 
770
static void
771
_cairo_path_buf_add_points (cairo_path_buf_t       *buf,
772
			    const cairo_point_t    *points,
773
			    int		            num_points)
774
{
775
    memcpy (buf->points + buf->num_points,
776
	    points,
777
	    sizeof (points[0]) * num_points);
778
    buf->num_points += num_points;
779
}
780
 
781
cairo_status_t
782
_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
    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
    cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD);
799
    int step = forward ? 1 : -1;
800
 
801
    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
	if (forward) {
807
	    start = 0;
808
	    stop = buf->num_ops;
809
	    points = buf->points;
810
	} else {
811
	    start = buf->num_ops - 1;
812
	    stop = -1;
813
	    points = buf->points + buf->num_points;
814
	}
815
 
816
	for (i = start; i != stop; i += step) {
817
	    cairo_path_op_t op = buf->op[i];
818
 
819
	    if (! forward)
820
		points -= num_args[(int) op];
821
 
822
	    switch (op) {
823
	    case CAIRO_PATH_OP_MOVE_TO:
824
		status = (*move_to) (closure, &points[0]);
825
		break;
826
	    case CAIRO_PATH_OP_LINE_TO:
827
		status = (*line_to) (closure, &points[0]);
828
		break;
829
	    case CAIRO_PATH_OP_CURVE_TO:
830
		status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
831
		break;
832
	    default:
833
		ASSERT_NOT_REACHED;
834
	    case CAIRO_PATH_OP_CLOSE_PATH:
835
		status = (*close_path) (closure);
836
		break;
837
	    }
838
	    if (unlikely (status))
839
		return status;
840
 
841
	    if (forward)
842
		points += num_args[(int) op];
843
	}
844
    } while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first);
845
 
846
    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
_append_move_to (void		 *abstract_closure,
856
		 const cairo_point_t  *point)
857
{
858
    cairo_path_fixed_append_closure_t	*closure = abstract_closure;
859
 
860
    return _cairo_path_fixed_move_to (closure->path,
861
				      point->x + closure->offset.x,
862
				      point->y + closure->offset.y);
863
}
864
 
865
static cairo_status_t
866
_append_line_to (void		 *abstract_closure,
867
		 const cairo_point_t *point)
868
{
869
    cairo_path_fixed_append_closure_t	*closure = abstract_closure;
870
 
871
    return _cairo_path_fixed_line_to (closure->path,
872
				      point->x + closure->offset.x,
873
				      point->y + closure->offset.y);
874
}
875
 
876
static cairo_status_t
877
_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
    cairo_path_fixed_append_closure_t	*closure = abstract_closure;
883
 
884
    return _cairo_path_fixed_curve_to (closure->path,
885
				       p0->x + closure->offset.x,
886
				       p0->y + closure->offset.y,
887
				       p1->x + closure->offset.x,
888
				       p1->y + closure->offset.y,
889
				       p2->x + closure->offset.x,
890
				       p2->y + closure->offset.y);
891
}
892
 
893
static cairo_status_t
894
_append_close_path (void *abstract_closure)
895
{
896
    cairo_path_fixed_append_closure_t	*closure = abstract_closure;
897
 
898
    return _cairo_path_fixed_close_path (closure->path);
899
}
900
 
901
cairo_status_t
902
_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
    closure.path = path;
911
    closure.offset.x = tx;
912
    closure.offset.y = ty;
913
 
914
    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
_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
    if (path->maybe_fill_region) {
933
	path->maybe_fill_region = _cairo_fixed_is_integer (offx) &&
934
	                          _cairo_fixed_is_integer (offy) &&
935
			          _cairo_fixed_is_integer (scalex) &&
936
			          _cairo_fixed_is_integer (scaley);
937
    }
938
 
939
    cairo_path_foreach_buf_start (buf, path) {
940
	 for (i = 0; i < buf->num_points; i++) {
941
	     if (scalex != CAIRO_FIXED_ONE)
942
		 buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex);
943
	     buf->points[i].x += offx;
944
 
945
	     if (scaley != CAIRO_FIXED_ONE)
946
		 buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
947
	     buf->points[i].y += offy;
948
	 }
949
    } cairo_path_foreach_buf_end (buf, path);
950
 
951
    path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx;
952
    path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx;
953
 
954
    path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy;
955
    path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy;
956
}
957
 
958
void
959
_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
    if (offx == 0 && offy == 0)
967
	return;
968
 
969
    if (path->maybe_fill_region &&
970
	! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy)))
971
    {
972
	path->maybe_fill_region = FALSE;
973
    }
974
 
975
    path->last_move_point.x += offx;
976
    path->last_move_point.y += offy;
977
    path->current_point.x += offx;
978
    path->current_point.y += offy;
979
 
980
    cairo_path_foreach_buf_start (buf, path) {
981
	 for (i = 0; i < buf->num_points; i++) {
982
	     buf->points[i].x += offx;
983
	     buf->points[i].y += offy;
984
	 }
985
    } cairo_path_foreach_buf_end (buf, path);
986
 
987
    path->extents.p1.x += offx;
988
    path->extents.p1.y += offy;
989
    path->extents.p2.x += offx;
990
    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
_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
    if (matrix->yx == 0.0 && matrix->xy == 0.0) {
1013
	/* Fast path for the common case of scale+transform */
1014
	 if (matrix->xx == 1. && matrix->yy == 1.) {
1015
	     _cairo_path_fixed_translate (path,
1016
					  _cairo_fixed_from_double (matrix->x0),
1017
					  _cairo_fixed_from_double (matrix->y0));
1018
	 } else {
1019
	     _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
	return;
1026
    }
1027
 
1028
    path->extents.p1.x = path->extents.p1.y = INT_MAX;
1029
    path->extents.p2.x = path->extents.p2.y = INT_MIN;
1030
    path->maybe_fill_region = FALSE;
1031
    cairo_path_foreach_buf_start (buf, path) {
1032
	 for (i = 0; i < buf->num_points; i++) {
1033
	    dx = _cairo_fixed_to_double (buf->points[i].x);
1034
	    dy = _cairo_fixed_to_double (buf->points[i].y);
1035
 
1036
	    cairo_matrix_transform_point (matrix, &dx, &dy);
1037
 
1038
	    buf->points[i].x = _cairo_fixed_from_double (dx);
1039
	    buf->points[i].y = _cairo_fixed_from_double (dy);
1040
 
1041
	    /* XXX need to eliminate surplus move-to's? */
1042
	    _cairo_path_fixed_extents_add (path, &buf->points[i]);
1043
	 }
1044
    } cairo_path_foreach_buf_end (buf, path);
1045
}
1046
 
1047
cairo_bool_t
1048
_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
    if (path->current_point.x != other->current_point.x ||
1054
	path->current_point.y != other->current_point.y ||
1055
	path->has_current_point != other->has_current_point ||
1056
	path->has_curve_to != other->has_curve_to ||
1057
	path->is_rectilinear != other->is_rectilinear ||
1058
	path->maybe_fill_region != other->maybe_fill_region ||
1059
	path->last_move_point.x != other->last_move_point.x ||
1060
	path->last_move_point.y != other->last_move_point.y)
1061
    {
1062
	return FALSE;
1063
    }
1064
 
1065
    other_buf = cairo_path_head (other);
1066
    cairo_path_foreach_buf_start (path_buf, path) {
1067
	if (path_buf->num_ops != other_buf->num_ops ||
1068
	    path_buf->num_points != other_buf->num_points ||
1069
	    memcmp (path_buf->op, other_buf->op,
1070
		    sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 ||
1071
	    memcmp (path_buf->points, other_buf->points,
1072
		    sizeof (cairo_point_t) * path_buf->num_points) != 0)
1073
	{
1074
	    return FALSE;
1075
	}
1076
	other_buf = cairo_path_buf_next (other_buf);
1077
    } cairo_path_foreach_buf_end (path_buf, path);
1078
 
1079
    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
_cpf_move_to (void *closure,
1094
	      const cairo_point_t *point)
1095
{
1096
    cpf_t *cpf = closure;
1097
 
1098
    cpf->current_point = *point;
1099
 
1100
    return cpf->move_to (cpf->closure, point);
1101
}
1102
 
1103
static cairo_status_t
1104
_cpf_line_to (void *closure,
1105
	      const cairo_point_t *point)
1106
{
1107
    cpf_t *cpf = closure;
1108
 
1109
    cpf->current_point = *point;
1110
 
1111
    return cpf->line_to (cpf->closure, point);
1112
}
1113
 
1114
static cairo_status_t
1115
_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
    cpf_t *cpf = closure;
1121
    cairo_spline_t spline;
1122
 
1123
    cairo_point_t *p0 = &cpf->current_point;
1124
 
1125
    if (! _cairo_spline_init (&spline,
1126
			      cpf->line_to,
1127
			      cpf->closure,
1128
			      p0, p1, p2, p3))
1129
    {
1130
	return _cpf_line_to (closure, p3);
1131
    }
1132
 
1133
    cpf->current_point = *p3;
1134
 
1135
    return _cairo_spline_decompose (&spline, cpf->tolerance);
1136
}
1137
 
1138
static cairo_status_t
1139
_cpf_close_path (void *closure)
1140
{
1141
    cpf_t *cpf = closure;
1142
 
1143
    return cpf->close_path (cpf->closure);
1144
}
1145
 
1146
cairo_status_t
1147
_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
    if (! path->has_curve_to) {
1158
	return _cairo_path_fixed_interpret (path, dir,
1159
					    move_to,
1160
					    line_to,
1161
					    NULL,
1162
					    close_path,
1163
					    closure);
1164
    }
1165
 
1166
    flattener.tolerance = tolerance;
1167
    flattener.move_to = move_to;
1168
    flattener.line_to = line_to;
1169
    flattener.close_path = close_path;
1170
    flattener.closure = closure;
1171
    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
_canonical_box (cairo_box_t *box,
1181
		const cairo_point_t *p1,
1182
		const cairo_point_t *p2)
1183
{
1184
    if (p1->x <= p2->x) {
1185
	box->p1.x = p1->x;
1186
	box->p2.x = p2->x;
1187
    } else {
1188
	box->p1.x = p2->x;
1189
	box->p2.x = p1->x;
1190
    }
1191
 
1192
    if (p1->y <= p2->y) {
1193
	box->p1.y = p1->y;
1194
	box->p2.y = p2->y;
1195
    } else {
1196
	box->p1.y = p2->y;
1197
	box->p2.y = p1->y;
1198
    }
1199
}
1200
 
1201
/*
1202
 * Check whether the given path contains a single rectangle.
1203
 */
1204
cairo_bool_t
1205
_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
1206
			  cairo_box_t *box)
1207
{
1208
    const cairo_path_buf_t *buf = cairo_path_head (path);
1209
 
1210
    if (! path->is_rectilinear)
1211
	return FALSE;
1212
 
1213
    /* Do we have the right number of ops? */
1214
    if (buf->num_ops < 4 || buf->num_ops > 6)
1215
	return FALSE;
1216
 
1217
    /* Check whether the ops are those that would be used for a rectangle */
1218
    if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
1219
	buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
1220
	buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
1221
	buf->op[3] != CAIRO_PATH_OP_LINE_TO)
1222
    {
1223
	return FALSE;
1224
    }
1225
 
1226
    /* we accept an implicit close for filled paths */
1227
    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
	if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
1232
	    if (buf->points[4].x != buf->points[0].x ||
1233
		buf->points[4].y != buf->points[0].y)
1234
		return FALSE;
1235
	} else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
1236
	    return FALSE;
1237
	}
1238
 
1239
	if (buf->num_ops == 6) {
1240
	    /* A trailing CLOSE_PATH or MOVE_TO is ok */
1241
	    if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO &&
1242
		buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH)
1243
		return FALSE;
1244
	}
1245
    }
1246
 
1247
    /* Ok, we may have a box, if the points line up */
1248
    if (buf->points[0].y == buf->points[1].y &&
1249
	buf->points[1].x == buf->points[2].x &&
1250
	buf->points[2].y == buf->points[3].y &&
1251
	buf->points[3].x == buf->points[0].x)
1252
    {
1253
	_canonical_box (box, &buf->points[0], &buf->points[2]);
1254
	return TRUE;
1255
    }
1256
 
1257
    if (buf->points[0].x == buf->points[1].x &&
1258
	buf->points[1].y == buf->points[2].y &&
1259
	buf->points[2].x == buf->points[3].x &&
1260
	buf->points[3].y == buf->points[0].y)
1261
    {
1262
	_canonical_box (box, &buf->points[0], &buf->points[2]);
1263
	return TRUE;
1264
    }
1265
 
1266
    return FALSE;
1267
}
1268
 
1269
/*
1270
 * Check whether the given path contains a single rectangle
1271
 * that is logically equivalent to:
1272
 * 
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
 * 
1279
 */
1280
cairo_bool_t
1281
_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
    if (! _cairo_path_fixed_is_box (path, box))
1287
	return FALSE;
1288
 
1289
    buf = cairo_path_head (path);
1290
    if (buf->points[0].y == buf->points[1].y)
1291
	return TRUE;
1292
 
1293
    return FALSE;
1294
}
1295
 
1296
void
1297
_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
1298
			     const cairo_path_fixed_t *path)
1299
{
1300
    iter->first = iter->buf = cairo_path_head (path);
1301
    iter->n_op = 0;
1302
    iter->n_point = 0;
1303
}
1304
 
1305
static cairo_bool_t
1306
_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter)
1307
{
1308
    if (++iter->n_op >= iter->buf->num_ops) {
1309
	iter->buf = cairo_path_buf_next (iter->buf);
1310
	if (iter->buf == iter->first) {
1311
	    iter->buf = NULL;
1312
	    return FALSE;
1313
	}
1314
 
1315
	iter->n_op = 0;
1316
	iter->n_point = 0;
1317
    }
1318
 
1319
    return TRUE;
1320
}
1321
 
1322
cairo_bool_t
1323
_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
    if (_iter->buf == NULL)
1330
	return FALSE;
1331
 
1332
    iter = *_iter;
1333
 
1334
    if (iter.n_op == iter.buf->num_ops &&
1335
	! _cairo_path_fixed_iter_next_op (&iter))
1336
    {
1337
	return FALSE;
1338
    }
1339
 
1340
    /* Check whether the ops are those that would be used for a rectangle */
1341
    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
1342
	return FALSE;
1343
    points[0] = iter.buf->points[iter.n_point++];
1344
    if (! _cairo_path_fixed_iter_next_op (&iter))
1345
	return FALSE;
1346
 
1347
    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
1348
	return FALSE;
1349
    points[1] = iter.buf->points[iter.n_point++];
1350
    if (! _cairo_path_fixed_iter_next_op (&iter))
1351
	return FALSE;
1352
 
1353
    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
1354
	return FALSE;
1355
    points[2] = iter.buf->points[iter.n_point++];
1356
    if (! _cairo_path_fixed_iter_next_op (&iter))
1357
	return FALSE;
1358
 
1359
    if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
1360
	return FALSE;
1361
    points[3] = iter.buf->points[iter.n_point++];
1362
    if (! _cairo_path_fixed_iter_next_op (&iter))
1363
	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
    if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO)
1369
    {
1370
	points[4] = iter.buf->points[iter.n_point++];
1371
	if (points[4].x != points[0].x || points[4].y != points[0].y)
1372
	    return FALSE;
1373
    }
1374
    else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH ||
1375
		iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO))
1376
    {
1377
	return FALSE;
1378
    }
1379
    if (! _cairo_path_fixed_iter_next_op (&iter))
1380
	return FALSE;
1381
 
1382
    /* Ok, we may have a box, if the points line up */
1383
    if (points[0].y == points[1].y &&
1384
	points[1].x == points[2].x &&
1385
	points[2].y == points[3].y &&
1386
	points[3].x == points[0].x)
1387
    {
1388
	box->p1 = points[0];
1389
	box->p2 = points[2];
1390
	*_iter = iter;
1391
	return TRUE;
1392
    }
1393
 
1394
    if (points[0].x == points[1].x &&
1395
	points[1].y == points[2].y &&
1396
	points[2].x == points[3].x &&
1397
	points[3].y == points[0].y)
1398
    {
1399
	box->p1 = points[1];
1400
	box->p2 = points[3];
1401
	*_iter = iter;
1402
	return TRUE;
1403
    }
1404
 
1405
    return FALSE;
1406
}
1407
 
1408
cairo_bool_t
1409
_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
1410
{
1411
    if (iter->buf == NULL)
1412
	return TRUE;
1413
 
1414
    if (iter->n_op == iter->buf->num_ops)
1415
	return TRUE;
1416
 
1417
    if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO &&
1418
	iter->buf->num_ops == iter->n_op + 1)
1419
    {
1420
	return TRUE;
1421
    }
1422
 
1423
    return FALSE;
1424
}