mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-14 12:40:24 +01:00
Instead of simply doubling the buffer size every time we overflow a point or an op, enlarge the buffer to fit twice the number of used points and ops. We expect paths to be fairly consistent in the mix of operations, and this allows the buffer size to tune itself to actual usage and reduce wastage.
1424 lines
38 KiB
C
1424 lines
38 KiB
C
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
|
|
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2002 University of Southern California
|
|
* Copyright © 2005 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* The Original Code is the cairo graphics library.
|
|
*
|
|
* The Initial Developer of the Original Code is University of Southern
|
|
* California.
|
|
*
|
|
* Contributor(s):
|
|
* Carl D. Worth <cworth@cworth.org>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-error-private.h"
|
|
#include "cairo-path-fixed-private.h"
|
|
#include "cairo-slope-private.h"
|
|
|
|
static cairo_status_t
|
|
_cairo_path_fixed_add (cairo_path_fixed_t *path,
|
|
cairo_path_op_t op,
|
|
const cairo_point_t *points,
|
|
int num_points);
|
|
|
|
static void
|
|
_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
|
|
cairo_path_buf_t *buf);
|
|
|
|
static cairo_path_buf_t *
|
|
_cairo_path_buf_create (int size_ops, int size_points);
|
|
|
|
static void
|
|
_cairo_path_buf_destroy (cairo_path_buf_t *buf);
|
|
|
|
static void
|
|
_cairo_path_buf_add_op (cairo_path_buf_t *buf,
|
|
cairo_path_op_t op);
|
|
|
|
static void
|
|
_cairo_path_buf_add_points (cairo_path_buf_t *buf,
|
|
const cairo_point_t *points,
|
|
int num_points);
|
|
|
|
#define cairo_path_head(path__) (&(path__)->buf.base)
|
|
#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
|
|
|
|
#define cairo_path_buf_next(pos__) \
|
|
cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
|
|
#define cairo_path_buf_prev(pos__) \
|
|
cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
|
|
|
|
#define cairo_path_foreach_buf_start(pos__, path__) \
|
|
pos__ = cairo_path_head (path__); do
|
|
#define cairo_path_foreach_buf_end(pos__, path__) \
|
|
while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__))
|
|
|
|
void
|
|
_cairo_path_fixed_init (cairo_path_fixed_t *path)
|
|
{
|
|
VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
|
|
|
|
cairo_list_init (&path->buf.base.link);
|
|
|
|
path->buf.base.num_ops = 0;
|
|
path->buf.base.num_points = 0;
|
|
path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
|
|
path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
|
|
path->buf.base.op = path->buf.op;
|
|
path->buf.base.points = path->buf.points;
|
|
|
|
path->current_point.x = 0;
|
|
path->current_point.y = 0;
|
|
path->last_move_point = path->current_point;
|
|
path->has_last_move_point = FALSE;
|
|
path->has_current_point = FALSE;
|
|
path->has_curve_to = FALSE;
|
|
path->is_rectilinear = TRUE;
|
|
path->maybe_fill_region = TRUE;
|
|
path->is_empty_fill = TRUE;
|
|
|
|
path->extents.p1.x = path->extents.p1.y = INT_MAX;
|
|
path->extents.p2.x = path->extents.p2.y = INT_MIN;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
|
|
const cairo_path_fixed_t *other)
|
|
{
|
|
cairo_path_buf_t *buf, *other_buf;
|
|
unsigned int num_points, num_ops;
|
|
|
|
VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t)));
|
|
|
|
cairo_list_init (&path->buf.base.link);
|
|
|
|
path->buf.base.op = path->buf.op;
|
|
path->buf.base.points = path->buf.points;
|
|
path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op);
|
|
path->buf.base.size_points = ARRAY_LENGTH (path->buf.points);
|
|
|
|
path->current_point = other->current_point;
|
|
path->last_move_point = other->last_move_point;
|
|
path->has_last_move_point = other->has_last_move_point;
|
|
path->has_current_point = other->has_current_point;
|
|
path->has_curve_to = other->has_curve_to;
|
|
path->is_rectilinear = other->is_rectilinear;
|
|
path->maybe_fill_region = other->maybe_fill_region;
|
|
path->is_empty_fill = other->is_empty_fill;
|
|
|
|
path->extents = other->extents;
|
|
|
|
path->buf.base.num_ops = other->buf.base.num_ops;
|
|
path->buf.base.num_points = other->buf.base.num_points;
|
|
memcpy (path->buf.op, other->buf.base.op,
|
|
other->buf.base.num_ops * sizeof (other->buf.op[0]));
|
|
memcpy (path->buf.points, other->buf.points,
|
|
other->buf.base.num_points * sizeof (other->buf.points[0]));
|
|
|
|
num_points = num_ops = 0;
|
|
for (other_buf = cairo_path_buf_next (cairo_path_head (other));
|
|
other_buf != cairo_path_head (other);
|
|
other_buf = cairo_path_buf_next (other_buf))
|
|
{
|
|
num_ops += other_buf->num_ops;
|
|
num_points += other_buf->num_points;
|
|
}
|
|
|
|
if (num_ops) {
|
|
buf = _cairo_path_buf_create (num_ops, num_points);
|
|
if (unlikely (buf == NULL)) {
|
|
_cairo_path_fixed_fini (path);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
for (other_buf = cairo_path_buf_next (cairo_path_head (other));
|
|
other_buf != cairo_path_head (other);
|
|
other_buf = cairo_path_buf_next (other_buf))
|
|
{
|
|
memcpy (buf->op + buf->num_ops, other_buf->op,
|
|
other_buf->num_ops * sizeof (buf->op[0]));
|
|
buf->num_ops += other_buf->num_ops;
|
|
|
|
memcpy (buf->points + buf->num_points, other_buf->points,
|
|
other_buf->num_points * sizeof (buf->points[0]));
|
|
buf->num_points += other_buf->num_points;
|
|
}
|
|
|
|
_cairo_path_fixed_add_buf (path, buf);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
unsigned long
|
|
_cairo_path_fixed_hash (const cairo_path_fixed_t *path)
|
|
{
|
|
unsigned long hash = _CAIRO_HASH_INIT_VALUE;
|
|
const cairo_path_buf_t *buf;
|
|
int num_points, num_ops;
|
|
|
|
hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents));
|
|
|
|
num_ops = num_points = 0;
|
|
cairo_path_foreach_buf_start (buf, path) {
|
|
hash = _cairo_hash_bytes (hash, buf->op,
|
|
buf->num_ops * sizeof (buf->op[0]));
|
|
hash = _cairo_hash_bytes (hash, buf->points,
|
|
buf->num_points * sizeof (buf->points[0]));
|
|
|
|
num_ops += buf->num_ops;
|
|
num_points += buf->num_points;
|
|
} cairo_path_foreach_buf_end (buf, path);
|
|
|
|
hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops));
|
|
hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points));
|
|
|
|
return hash;
|
|
}
|
|
|
|
unsigned long
|
|
_cairo_path_fixed_size (const cairo_path_fixed_t *path)
|
|
{
|
|
const cairo_path_buf_t *buf;
|
|
int num_points, num_ops;
|
|
|
|
num_ops = num_points = 0;
|
|
cairo_path_foreach_buf_start (buf, path) {
|
|
num_ops += buf->num_ops;
|
|
num_points += buf->num_points;
|
|
} cairo_path_foreach_buf_end (buf, path);
|
|
|
|
return num_ops * sizeof (buf->op[0]) +
|
|
num_points * sizeof (buf->points[0]);
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
|
|
const cairo_path_fixed_t *b)
|
|
{
|
|
const cairo_path_buf_t *buf_a, *buf_b;
|
|
const cairo_path_op_t *ops_a, *ops_b;
|
|
const cairo_point_t *points_a, *points_b;
|
|
int num_points_a, num_ops_a;
|
|
int num_points_b, num_ops_b;
|
|
|
|
if (a == b)
|
|
return TRUE;
|
|
|
|
/* use the flags to quickly differentiate based on contents */
|
|
if (a->is_empty_fill != b->is_empty_fill ||
|
|
a->has_curve_to != b->has_curve_to ||
|
|
a->maybe_fill_region != b->maybe_fill_region ||
|
|
a->is_rectilinear != b->is_rectilinear)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (a->extents.p1.x != b->extents.p1.x ||
|
|
a->extents.p1.y != b->extents.p1.y ||
|
|
a->extents.p2.x != b->extents.p2.x ||
|
|
a->extents.p2.y != b->extents.p2.y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
num_ops_a = num_points_a = 0;
|
|
cairo_path_foreach_buf_start (buf_a, a) {
|
|
num_ops_a += buf_a->num_ops;
|
|
num_points_a += buf_a->num_points;
|
|
} cairo_path_foreach_buf_end (buf_a, a);
|
|
|
|
num_ops_b = num_points_b = 0;
|
|
cairo_path_foreach_buf_start (buf_b, b) {
|
|
num_ops_b += buf_b->num_ops;
|
|
num_points_b += buf_b->num_points;
|
|
} cairo_path_foreach_buf_end (buf_b, b);
|
|
|
|
if (num_ops_a == 0 && num_ops_b == 0)
|
|
return TRUE;
|
|
|
|
if (num_ops_a != num_ops_b || num_points_a != num_points_b)
|
|
return FALSE;
|
|
|
|
buf_a = cairo_path_head (a);
|
|
num_points_a = buf_a->num_points;
|
|
num_ops_a = buf_a->num_ops;
|
|
ops_a = buf_a->op;
|
|
points_a = buf_a->points;
|
|
|
|
buf_b = cairo_path_head (b);
|
|
num_points_b = buf_b->num_points;
|
|
num_ops_b = buf_b->num_ops;
|
|
ops_b = buf_b->op;
|
|
points_b = buf_b->points;
|
|
|
|
while (TRUE) {
|
|
int num_ops = MIN (num_ops_a, num_ops_b);
|
|
int num_points = MIN (num_points_a, num_points_b);
|
|
|
|
if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t)))
|
|
return FALSE;
|
|
if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t)))
|
|
return FALSE;
|
|
|
|
num_ops_a -= num_ops;
|
|
ops_a += num_ops;
|
|
num_points_a -= num_points;
|
|
points_a += num_points;
|
|
if (num_ops_a == 0 || num_points_a == 0) {
|
|
if (num_ops_a || num_points_a)
|
|
return FALSE;
|
|
|
|
buf_a = cairo_path_buf_next (buf_a);
|
|
if (buf_a == cairo_path_head (a))
|
|
break;
|
|
|
|
num_points_a = buf_a->num_points;
|
|
num_ops_a = buf_a->num_ops;
|
|
ops_a = buf_a->op;
|
|
points_a = buf_a->points;
|
|
}
|
|
|
|
num_ops_b -= num_ops;
|
|
ops_b += num_ops;
|
|
num_points_b -= num_points;
|
|
points_b += num_points;
|
|
if (num_ops_b == 0 || num_points_b == 0) {
|
|
if (num_ops_b || num_points_b)
|
|
return FALSE;
|
|
|
|
buf_b = cairo_path_buf_next (buf_b);
|
|
if (buf_b == cairo_path_head (b))
|
|
break;
|
|
|
|
num_points_b = buf_b->num_points;
|
|
num_ops_b = buf_b->num_ops;
|
|
ops_b = buf_b->op;
|
|
points_b = buf_b->points;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
cairo_path_fixed_t *
|
|
_cairo_path_fixed_create (void)
|
|
{
|
|
cairo_path_fixed_t *path;
|
|
|
|
path = malloc (sizeof (cairo_path_fixed_t));
|
|
if (!path) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
_cairo_path_fixed_init (path);
|
|
return path;
|
|
}
|
|
|
|
void
|
|
_cairo_path_fixed_fini (cairo_path_fixed_t *path)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
|
|
buf = cairo_path_buf_next (cairo_path_head (path));
|
|
while (buf != cairo_path_head (path)) {
|
|
cairo_path_buf_t *this = buf;
|
|
buf = cairo_path_buf_next (buf);
|
|
_cairo_path_buf_destroy (this);
|
|
}
|
|
|
|
VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t)));
|
|
}
|
|
|
|
void
|
|
_cairo_path_fixed_destroy (cairo_path_fixed_t *path)
|
|
{
|
|
_cairo_path_fixed_fini (path);
|
|
free (path);
|
|
}
|
|
|
|
static cairo_path_op_t
|
|
_cairo_path_last_op (cairo_path_fixed_t *path)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
|
|
buf = cairo_path_tail (path);
|
|
if (buf->num_ops == 0)
|
|
return -1;
|
|
|
|
return buf->op[buf->num_ops - 1];
|
|
}
|
|
|
|
static inline void
|
|
_cairo_path_fixed_extents_add (cairo_path_fixed_t *path,
|
|
const cairo_point_t *point)
|
|
{
|
|
if (point->x < path->extents.p1.x)
|
|
path->extents.p1.x = point->x;
|
|
if (point->y < path->extents.p1.y)
|
|
path->extents.p1.y = point->y;
|
|
|
|
if (point->x > path->extents.p2.x)
|
|
path->extents.p2.x = point->x;
|
|
if (point->y > path->extents.p2.y)
|
|
path->extents.p2.y = point->y;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
|
|
cairo_fixed_t x,
|
|
cairo_fixed_t y)
|
|
{
|
|
cairo_status_t status;
|
|
cairo_point_t point;
|
|
|
|
point.x = x;
|
|
point.y = y;
|
|
|
|
/* If the previous op was also a MOVE_TO, then just change its
|
|
* point rather than adding a new op. */
|
|
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
|
|
cairo_path_buf_t *buf;
|
|
|
|
buf = cairo_path_tail (path);
|
|
buf->points[buf->num_points - 1] = point;
|
|
} else {
|
|
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (path->has_current_point && path->is_rectilinear) {
|
|
/* a move-to is first an implicit close */
|
|
path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
|
|
path->current_point.y == path->last_move_point.y;
|
|
path->maybe_fill_region &= path->is_rectilinear;
|
|
}
|
|
if (path->maybe_fill_region) {
|
|
path->maybe_fill_region =
|
|
_cairo_fixed_is_integer (path->last_move_point.x) &&
|
|
_cairo_fixed_is_integer (path->last_move_point.y);
|
|
}
|
|
}
|
|
|
|
path->current_point = point;
|
|
path->last_move_point = point;
|
|
path->has_last_move_point = TRUE;
|
|
path->has_current_point = TRUE;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
void
|
|
_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
|
|
{
|
|
path->has_current_point = FALSE;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
|
|
cairo_fixed_t dx,
|
|
cairo_fixed_t dy)
|
|
{
|
|
if (unlikely (! path->has_current_point))
|
|
return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
|
|
|
|
return _cairo_path_fixed_move_to (path,
|
|
path->current_point.x + dx,
|
|
path->current_point.y + dy);
|
|
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
|
|
cairo_fixed_t x,
|
|
cairo_fixed_t y)
|
|
{
|
|
cairo_status_t status;
|
|
cairo_point_t point;
|
|
|
|
point.x = x;
|
|
point.y = y;
|
|
|
|
/* When there is not yet a current point, the line_to operation
|
|
* becomes a move_to instead. Note: We have to do this by
|
|
* explicitly calling into _cairo_path_fixed_move_to to ensure
|
|
* that the last_move_point state is updated properly.
|
|
*/
|
|
if (! path->has_current_point)
|
|
return _cairo_path_fixed_move_to (path, point.x, point.y);
|
|
|
|
/* If the previous op was but the initial MOVE_TO and this segment
|
|
* is degenerate, then we can simply skip this point. Note that
|
|
* a move-to followed by a degenerate line-to is a valid path for
|
|
* stroking, but at all other times is simply a degenerate segment.
|
|
*/
|
|
if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
|
|
if (x == path->current_point.x && y == path->current_point.y)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* If the previous op was also a LINE_TO with the same gradient,
|
|
* then just change its end-point rather than adding a new op.
|
|
*/
|
|
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
|
|
cairo_path_buf_t *buf;
|
|
const cairo_point_t *p;
|
|
|
|
buf = cairo_path_tail (path);
|
|
if (likely (buf->num_points >= 2)) {
|
|
p = &buf->points[buf->num_points-2];
|
|
} else {
|
|
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
|
|
p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
|
|
}
|
|
|
|
if (p->x == path->current_point.x && p->y == path->current_point.y) {
|
|
/* previous line element was degenerate, replace */
|
|
buf->points[buf->num_points - 1] = point;
|
|
goto FLAGS;
|
|
} else {
|
|
cairo_slope_t prev, self;
|
|
|
|
_cairo_slope_init (&prev, p, &path->current_point);
|
|
_cairo_slope_init (&self, &path->current_point, &point);
|
|
if (_cairo_slope_equal (&prev, &self) &&
|
|
/* cannot trim anti-parallel segments whilst stroking */
|
|
! _cairo_slope_backwards (&prev, &self))
|
|
{
|
|
buf->points[buf->num_points - 1] = point;
|
|
goto FLAGS;
|
|
}
|
|
}
|
|
}
|
|
|
|
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
FLAGS:
|
|
if (path->is_rectilinear) {
|
|
path->is_rectilinear = path->current_point.x == x ||
|
|
path->current_point.y == y;
|
|
path->maybe_fill_region &= path->is_rectilinear;
|
|
}
|
|
if (path->maybe_fill_region) {
|
|
path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
|
|
_cairo_fixed_is_integer (y);
|
|
}
|
|
if (path->is_empty_fill) {
|
|
path->is_empty_fill = path->current_point.x == x &&
|
|
path->current_point.y == y;
|
|
}
|
|
|
|
path->current_point = point;
|
|
if (path->has_last_move_point) {
|
|
_cairo_path_fixed_extents_add (path, &path->last_move_point);
|
|
path->has_last_move_point = FALSE;
|
|
}
|
|
_cairo_path_fixed_extents_add (path, &point);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
|
|
cairo_fixed_t dx,
|
|
cairo_fixed_t dy)
|
|
{
|
|
if (unlikely (! path->has_current_point))
|
|
return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
|
|
|
|
return _cairo_path_fixed_line_to (path,
|
|
path->current_point.x + dx,
|
|
path->current_point.y + dy);
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
|
|
cairo_fixed_t x0, cairo_fixed_t y0,
|
|
cairo_fixed_t x1, cairo_fixed_t y1,
|
|
cairo_fixed_t x2, cairo_fixed_t y2)
|
|
{
|
|
cairo_status_t status;
|
|
cairo_point_t point[3];
|
|
|
|
/* make sure subpaths are started properly */
|
|
if (! path->has_current_point) {
|
|
status = _cairo_path_fixed_move_to (path, x0, y0);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
point[0].x = x0; point[0].y = y0;
|
|
point[1].x = x1; point[1].y = y1;
|
|
point[2].x = x2; point[2].y = y2;
|
|
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
path->current_point = point[2];
|
|
path->has_current_point = TRUE;
|
|
path->is_empty_fill = FALSE;
|
|
path->has_curve_to = TRUE;
|
|
path->is_rectilinear = FALSE;
|
|
path->maybe_fill_region = FALSE;
|
|
|
|
/* coarse bounds */
|
|
if (path->has_last_move_point) {
|
|
_cairo_path_fixed_extents_add (path, &path->last_move_point);
|
|
path->has_last_move_point = FALSE;
|
|
}
|
|
_cairo_path_fixed_extents_add (path, &point[0]);
|
|
_cairo_path_fixed_extents_add (path, &point[1]);
|
|
_cairo_path_fixed_extents_add (path, &point[2]);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
|
|
cairo_fixed_t dx0, cairo_fixed_t dy0,
|
|
cairo_fixed_t dx1, cairo_fixed_t dy1,
|
|
cairo_fixed_t dx2, cairo_fixed_t dy2)
|
|
{
|
|
if (unlikely (! path->has_current_point))
|
|
return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
|
|
|
|
return _cairo_path_fixed_curve_to (path,
|
|
path->current_point.x + dx0,
|
|
path->current_point.y + dy0,
|
|
|
|
path->current_point.x + dx1,
|
|
path->current_point.y + dy1,
|
|
|
|
path->current_point.x + dx2,
|
|
path->current_point.y + dy2);
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_close_path (cairo_path_fixed_t *path)
|
|
{
|
|
cairo_status_t status;
|
|
|
|
if (! path->has_current_point)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
/* If the previous op was also a LINE_TO back to the start, discard it */
|
|
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
|
|
if (path->current_point.x == path->last_move_point.x &&
|
|
path->current_point.y == path->last_move_point.y)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
cairo_point_t *p;
|
|
|
|
buf = cairo_path_tail (path);
|
|
if (likely (buf->num_points >= 2)) {
|
|
p = &buf->points[buf->num_points-2];
|
|
} else {
|
|
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
|
|
p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
|
|
}
|
|
|
|
path->current_point = *p;
|
|
buf->num_ops--;
|
|
buf->num_points--;
|
|
}
|
|
}
|
|
|
|
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
return _cairo_path_fixed_move_to (path,
|
|
path->last_move_point.x,
|
|
path->last_move_point.y);
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
|
|
cairo_fixed_t *x,
|
|
cairo_fixed_t *y)
|
|
{
|
|
if (! path->has_current_point)
|
|
return FALSE;
|
|
|
|
*x = path->current_point.x;
|
|
*y = path->current_point.y;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_path_fixed_add (cairo_path_fixed_t *path,
|
|
cairo_path_op_t op,
|
|
const cairo_point_t *points,
|
|
int num_points)
|
|
{
|
|
cairo_path_buf_t *buf = cairo_path_tail (path);
|
|
|
|
if (buf->num_ops + 1 > buf->size_ops ||
|
|
buf->num_points + num_points > buf->size_points)
|
|
{
|
|
buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2);
|
|
if (unlikely (buf == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
_cairo_path_fixed_add_buf (path, buf);
|
|
}
|
|
|
|
if (WATCH_PATH) {
|
|
const char *op_str[] = {
|
|
"move-to",
|
|
"line-to",
|
|
"curve-to",
|
|
"close-path",
|
|
};
|
|
char buf[1024];
|
|
int len = 0;
|
|
int i;
|
|
|
|
len += snprintf (buf + len, sizeof (buf), "[");
|
|
for (i = 0; i < num_points; i++) {
|
|
if (i != 0)
|
|
len += snprintf (buf + len, sizeof (buf), " ");
|
|
len += snprintf (buf + len, sizeof (buf), "(%f, %f)",
|
|
_cairo_fixed_to_double (points[i].x),
|
|
_cairo_fixed_to_double (points[i].y));
|
|
}
|
|
len += snprintf (buf + len, sizeof (buf), "]");
|
|
|
|
fprintf (stderr,
|
|
"_cairo_path_fixed_add (%s, %s)\n",
|
|
op_str[(int) op], buf);
|
|
}
|
|
|
|
_cairo_path_buf_add_op (buf, op);
|
|
_cairo_path_buf_add_points (buf, points, num_points);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
|
|
cairo_path_buf_t *buf)
|
|
{
|
|
cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link);
|
|
}
|
|
|
|
COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1);
|
|
static cairo_path_buf_t *
|
|
_cairo_path_buf_create (int size_ops, int size_points)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
|
|
/* adjust size_ops to ensure that buf->points is naturally aligned */
|
|
size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double));
|
|
buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t));
|
|
if (buf) {
|
|
buf->num_ops = 0;
|
|
buf->num_points = 0;
|
|
buf->size_ops = size_ops;
|
|
buf->size_points = size_points;
|
|
|
|
buf->op = (cairo_path_op_t *) (buf + 1);
|
|
buf->points = (cairo_point_t *) (buf->op + size_ops);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void
|
|
_cairo_path_buf_destroy (cairo_path_buf_t *buf)
|
|
{
|
|
free (buf);
|
|
}
|
|
|
|
static void
|
|
_cairo_path_buf_add_op (cairo_path_buf_t *buf,
|
|
cairo_path_op_t op)
|
|
{
|
|
buf->op[buf->num_ops++] = op;
|
|
}
|
|
|
|
static void
|
|
_cairo_path_buf_add_points (cairo_path_buf_t *buf,
|
|
const cairo_point_t *points,
|
|
int num_points)
|
|
{
|
|
memcpy (buf->points + buf->num_points,
|
|
points,
|
|
sizeof (points[0]) * num_points);
|
|
buf->num_points += num_points;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
|
|
cairo_direction_t dir,
|
|
cairo_path_fixed_move_to_func_t *move_to,
|
|
cairo_path_fixed_line_to_func_t *line_to,
|
|
cairo_path_fixed_curve_to_func_t *curve_to,
|
|
cairo_path_fixed_close_path_func_t *close_path,
|
|
void *closure)
|
|
{
|
|
const uint8_t num_args[] = {
|
|
1, /* cairo_path_move_to */
|
|
1, /* cairo_path_op_line_to */
|
|
3, /* cairo_path_op_curve_to */
|
|
0, /* cairo_path_op_close_path */
|
|
};
|
|
cairo_status_t status;
|
|
const cairo_path_buf_t *buf, *first;
|
|
cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD);
|
|
int step = forward ? 1 : -1;
|
|
|
|
buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path);
|
|
do {
|
|
cairo_point_t *points;
|
|
int start, stop, i;
|
|
|
|
if (forward) {
|
|
start = 0;
|
|
stop = buf->num_ops;
|
|
points = buf->points;
|
|
} else {
|
|
start = buf->num_ops - 1;
|
|
stop = -1;
|
|
points = buf->points + buf->num_points;
|
|
}
|
|
|
|
for (i = start; i != stop; i += step) {
|
|
cairo_path_op_t op = buf->op[i];
|
|
|
|
if (! forward)
|
|
points -= num_args[(int) op];
|
|
|
|
switch (op) {
|
|
case CAIRO_PATH_OP_MOVE_TO:
|
|
status = (*move_to) (closure, &points[0]);
|
|
break;
|
|
case CAIRO_PATH_OP_LINE_TO:
|
|
status = (*line_to) (closure, &points[0]);
|
|
break;
|
|
case CAIRO_PATH_OP_CURVE_TO:
|
|
status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
case CAIRO_PATH_OP_CLOSE_PATH:
|
|
status = (*close_path) (closure);
|
|
break;
|
|
}
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (forward)
|
|
points += num_args[(int) op];
|
|
}
|
|
} while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
typedef struct _cairo_path_fixed_append_closure {
|
|
cairo_point_t offset;
|
|
cairo_path_fixed_t *path;
|
|
} cairo_path_fixed_append_closure_t;
|
|
|
|
static cairo_status_t
|
|
_append_move_to (void *abstract_closure,
|
|
const cairo_point_t *point)
|
|
{
|
|
cairo_path_fixed_append_closure_t *closure = abstract_closure;
|
|
|
|
return _cairo_path_fixed_move_to (closure->path,
|
|
point->x + closure->offset.x,
|
|
point->y + closure->offset.y);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_append_line_to (void *abstract_closure,
|
|
const cairo_point_t *point)
|
|
{
|
|
cairo_path_fixed_append_closure_t *closure = abstract_closure;
|
|
|
|
return _cairo_path_fixed_line_to (closure->path,
|
|
point->x + closure->offset.x,
|
|
point->y + closure->offset.y);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_append_curve_to (void *abstract_closure,
|
|
const cairo_point_t *p0,
|
|
const cairo_point_t *p1,
|
|
const cairo_point_t *p2)
|
|
{
|
|
cairo_path_fixed_append_closure_t *closure = abstract_closure;
|
|
|
|
return _cairo_path_fixed_curve_to (closure->path,
|
|
p0->x + closure->offset.x,
|
|
p0->y + closure->offset.y,
|
|
p1->x + closure->offset.x,
|
|
p1->y + closure->offset.y,
|
|
p2->x + closure->offset.x,
|
|
p2->y + closure->offset.y);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_append_close_path (void *abstract_closure)
|
|
{
|
|
cairo_path_fixed_append_closure_t *closure = abstract_closure;
|
|
|
|
return _cairo_path_fixed_close_path (closure->path);
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_append (cairo_path_fixed_t *path,
|
|
const cairo_path_fixed_t *other,
|
|
cairo_direction_t dir,
|
|
cairo_fixed_t tx,
|
|
cairo_fixed_t ty)
|
|
{
|
|
cairo_path_fixed_append_closure_t closure;
|
|
|
|
closure.path = path;
|
|
closure.offset.x = tx;
|
|
closure.offset.y = ty;
|
|
|
|
return _cairo_path_fixed_interpret (other, dir,
|
|
_append_move_to,
|
|
_append_line_to,
|
|
_append_curve_to,
|
|
_append_close_path,
|
|
&closure);
|
|
}
|
|
|
|
static void
|
|
_cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
|
|
cairo_fixed_t offx,
|
|
cairo_fixed_t offy,
|
|
cairo_fixed_t scalex,
|
|
cairo_fixed_t scaley)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
unsigned int i;
|
|
|
|
if (path->maybe_fill_region) {
|
|
path->maybe_fill_region = _cairo_fixed_is_integer (offx) &&
|
|
_cairo_fixed_is_integer (offy) &&
|
|
_cairo_fixed_is_integer (scalex) &&
|
|
_cairo_fixed_is_integer (scaley);
|
|
}
|
|
|
|
cairo_path_foreach_buf_start (buf, path) {
|
|
for (i = 0; i < buf->num_points; i++) {
|
|
if (scalex != CAIRO_FIXED_ONE)
|
|
buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex);
|
|
buf->points[i].x += offx;
|
|
|
|
if (scaley != CAIRO_FIXED_ONE)
|
|
buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
|
|
buf->points[i].y += offy;
|
|
}
|
|
} cairo_path_foreach_buf_end (buf, path);
|
|
|
|
path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx;
|
|
path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx;
|
|
|
|
path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy;
|
|
path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy;
|
|
}
|
|
|
|
void
|
|
_cairo_path_fixed_translate (cairo_path_fixed_t *path,
|
|
cairo_fixed_t offx,
|
|
cairo_fixed_t offy)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
unsigned int i;
|
|
|
|
if (offx == 0 && offy == 0)
|
|
return;
|
|
|
|
if (path->maybe_fill_region &&
|
|
! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy)))
|
|
{
|
|
path->maybe_fill_region = FALSE;
|
|
}
|
|
|
|
path->last_move_point.x += offx;
|
|
path->last_move_point.y += offy;
|
|
path->current_point.x += offx;
|
|
path->current_point.y += offy;
|
|
|
|
cairo_path_foreach_buf_start (buf, path) {
|
|
for (i = 0; i < buf->num_points; i++) {
|
|
buf->points[i].x += offx;
|
|
buf->points[i].y += offy;
|
|
}
|
|
} cairo_path_foreach_buf_end (buf, path);
|
|
|
|
path->extents.p1.x += offx;
|
|
path->extents.p1.y += offy;
|
|
path->extents.p2.x += offx;
|
|
path->extents.p2.y += offy;
|
|
}
|
|
|
|
/**
|
|
* _cairo_path_fixed_transform:
|
|
* @path: a #cairo_path_fixed_t to be transformed
|
|
* @matrix: a #cairo_matrix_t
|
|
*
|
|
* Transform the fixed-point path according to the given matrix.
|
|
* There is a fast path for the case where @matrix has no rotation
|
|
* or shear.
|
|
**/
|
|
void
|
|
_cairo_path_fixed_transform (cairo_path_fixed_t *path,
|
|
const cairo_matrix_t *matrix)
|
|
{
|
|
cairo_path_buf_t *buf;
|
|
unsigned int i;
|
|
double dx, dy;
|
|
|
|
/* XXX current_point, last_move_to */
|
|
|
|
if (matrix->yx == 0.0 && matrix->xy == 0.0) {
|
|
/* Fast path for the common case of scale+transform */
|
|
if (matrix->xx == 1. && matrix->yy == 1.) {
|
|
_cairo_path_fixed_translate (path,
|
|
_cairo_fixed_from_double (matrix->x0),
|
|
_cairo_fixed_from_double (matrix->y0));
|
|
} else {
|
|
_cairo_path_fixed_offset_and_scale (path,
|
|
_cairo_fixed_from_double (matrix->x0),
|
|
_cairo_fixed_from_double (matrix->y0),
|
|
_cairo_fixed_from_double (matrix->xx),
|
|
_cairo_fixed_from_double (matrix->yy));
|
|
}
|
|
return;
|
|
}
|
|
|
|
path->extents.p1.x = path->extents.p1.y = INT_MAX;
|
|
path->extents.p2.x = path->extents.p2.y = INT_MIN;
|
|
path->maybe_fill_region = FALSE;
|
|
cairo_path_foreach_buf_start (buf, path) {
|
|
for (i = 0; i < buf->num_points; i++) {
|
|
dx = _cairo_fixed_to_double (buf->points[i].x);
|
|
dy = _cairo_fixed_to_double (buf->points[i].y);
|
|
|
|
cairo_matrix_transform_point (matrix, &dx, &dy);
|
|
|
|
buf->points[i].x = _cairo_fixed_from_double (dx);
|
|
buf->points[i].y = _cairo_fixed_from_double (dy);
|
|
|
|
/* XXX need to eliminate surplus move-to's? */
|
|
_cairo_path_fixed_extents_add (path, &buf->points[i]);
|
|
}
|
|
} cairo_path_foreach_buf_end (buf, path);
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
|
|
const cairo_path_fixed_t *other)
|
|
{
|
|
const cairo_path_buf_t *path_buf, *other_buf;
|
|
|
|
if (path->current_point.x != other->current_point.x ||
|
|
path->current_point.y != other->current_point.y ||
|
|
path->has_current_point != other->has_current_point ||
|
|
path->has_curve_to != other->has_curve_to ||
|
|
path->is_rectilinear != other->is_rectilinear ||
|
|
path->maybe_fill_region != other->maybe_fill_region ||
|
|
path->last_move_point.x != other->last_move_point.x ||
|
|
path->last_move_point.y != other->last_move_point.y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
other_buf = cairo_path_head (other);
|
|
cairo_path_foreach_buf_start (path_buf, path) {
|
|
if (path_buf->num_ops != other_buf->num_ops ||
|
|
path_buf->num_points != other_buf->num_points ||
|
|
memcmp (path_buf->op, other_buf->op,
|
|
sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 ||
|
|
memcmp (path_buf->points, other_buf->points,
|
|
sizeof (cairo_point_t) * path_buf->num_points) != 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
other_buf = cairo_path_buf_next (other_buf);
|
|
} cairo_path_foreach_buf_end (path_buf, path);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Closure for path flattening */
|
|
typedef struct cairo_path_flattener {
|
|
double tolerance;
|
|
cairo_point_t current_point;
|
|
cairo_path_fixed_move_to_func_t *move_to;
|
|
cairo_path_fixed_line_to_func_t *line_to;
|
|
cairo_path_fixed_close_path_func_t *close_path;
|
|
void *closure;
|
|
} cpf_t;
|
|
|
|
static cairo_status_t
|
|
_cpf_move_to (void *closure,
|
|
const cairo_point_t *point)
|
|
{
|
|
cpf_t *cpf = closure;
|
|
|
|
cpf->current_point = *point;
|
|
|
|
return cpf->move_to (cpf->closure, point);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cpf_line_to (void *closure,
|
|
const cairo_point_t *point)
|
|
{
|
|
cpf_t *cpf = closure;
|
|
|
|
cpf->current_point = *point;
|
|
|
|
return cpf->line_to (cpf->closure, point);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cpf_curve_to (void *closure,
|
|
const cairo_point_t *p1,
|
|
const cairo_point_t *p2,
|
|
const cairo_point_t *p3)
|
|
{
|
|
cpf_t *cpf = closure;
|
|
cairo_spline_t spline;
|
|
|
|
cairo_point_t *p0 = &cpf->current_point;
|
|
|
|
if (! _cairo_spline_init (&spline,
|
|
cpf->line_to,
|
|
cpf->closure,
|
|
p0, p1, p2, p3))
|
|
{
|
|
return _cpf_line_to (closure, p3);
|
|
}
|
|
|
|
cpf->current_point = *p3;
|
|
|
|
return _cairo_spline_decompose (&spline, cpf->tolerance);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cpf_close_path (void *closure)
|
|
{
|
|
cpf_t *cpf = closure;
|
|
|
|
return cpf->close_path (cpf->closure);
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
|
|
cairo_direction_t dir,
|
|
cairo_path_fixed_move_to_func_t *move_to,
|
|
cairo_path_fixed_line_to_func_t *line_to,
|
|
cairo_path_fixed_close_path_func_t *close_path,
|
|
void *closure,
|
|
double tolerance)
|
|
{
|
|
cpf_t flattener;
|
|
|
|
if (! path->has_curve_to) {
|
|
return _cairo_path_fixed_interpret (path, dir,
|
|
move_to,
|
|
line_to,
|
|
NULL,
|
|
close_path,
|
|
closure);
|
|
}
|
|
|
|
flattener.tolerance = tolerance;
|
|
flattener.move_to = move_to;
|
|
flattener.line_to = line_to;
|
|
flattener.close_path = close_path;
|
|
flattener.closure = closure;
|
|
return _cairo_path_fixed_interpret (path, dir,
|
|
_cpf_move_to,
|
|
_cpf_line_to,
|
|
_cpf_curve_to,
|
|
_cpf_close_path,
|
|
&flattener);
|
|
}
|
|
|
|
static inline void
|
|
_canonical_box (cairo_box_t *box,
|
|
const cairo_point_t *p1,
|
|
const cairo_point_t *p2)
|
|
{
|
|
if (p1->x <= p2->x) {
|
|
box->p1.x = p1->x;
|
|
box->p2.x = p2->x;
|
|
} else {
|
|
box->p1.x = p2->x;
|
|
box->p2.x = p1->x;
|
|
}
|
|
|
|
if (p1->y <= p2->y) {
|
|
box->p1.y = p1->y;
|
|
box->p2.y = p2->y;
|
|
} else {
|
|
box->p1.y = p2->y;
|
|
box->p2.y = p1->y;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check whether the given path contains a single rectangle.
|
|
*/
|
|
cairo_bool_t
|
|
_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
|
|
cairo_box_t *box)
|
|
{
|
|
const cairo_path_buf_t *buf = cairo_path_head (path);
|
|
|
|
if (! path->is_rectilinear)
|
|
return FALSE;
|
|
|
|
/* Do we have the right number of ops? */
|
|
if (buf->num_ops < 4 || buf->num_ops > 6)
|
|
return FALSE;
|
|
|
|
/* Check whether the ops are those that would be used for a rectangle */
|
|
if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
|
|
buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
|
|
buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
|
|
buf->op[3] != CAIRO_PATH_OP_LINE_TO)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* we accept an implicit close for filled paths */
|
|
if (buf->num_ops > 4) {
|
|
/* Now, there are choices. The rectangle might end with a LINE_TO
|
|
* (to the original point), but this isn't required. If it
|
|
* doesn't, then it must end with a CLOSE_PATH. */
|
|
if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) {
|
|
if (buf->points[4].x != buf->points[0].x ||
|
|
buf->points[4].y != buf->points[0].y)
|
|
return FALSE;
|
|
} else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (buf->num_ops == 6) {
|
|
/* A trailing CLOSE_PATH or MOVE_TO is ok */
|
|
if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO &&
|
|
buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Ok, we may have a box, if the points line up */
|
|
if (buf->points[0].y == buf->points[1].y &&
|
|
buf->points[1].x == buf->points[2].x &&
|
|
buf->points[2].y == buf->points[3].y &&
|
|
buf->points[3].x == buf->points[0].x)
|
|
{
|
|
_canonical_box (box, &buf->points[0], &buf->points[2]);
|
|
return TRUE;
|
|
}
|
|
|
|
if (buf->points[0].x == buf->points[1].x &&
|
|
buf->points[1].y == buf->points[2].y &&
|
|
buf->points[2].x == buf->points[3].x &&
|
|
buf->points[3].y == buf->points[0].y)
|
|
{
|
|
_canonical_box (box, &buf->points[0], &buf->points[2]);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Check whether the given path contains a single rectangle
|
|
* that is logically equivalent to:
|
|
* <informalexample><programlisting>
|
|
* cairo_move_to (cr, x, y);
|
|
* cairo_rel_line_to (cr, width, 0);
|
|
* cairo_rel_line_to (cr, 0, height);
|
|
* cairo_rel_line_to (cr, -width, 0);
|
|
* cairo_close_path (cr);
|
|
* </programlisting></informalexample>
|
|
*/
|
|
cairo_bool_t
|
|
_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path,
|
|
cairo_box_t *box)
|
|
{
|
|
const cairo_path_buf_t *buf;
|
|
|
|
if (! _cairo_path_fixed_is_box (path, box))
|
|
return FALSE;
|
|
|
|
buf = cairo_path_head (path);
|
|
if (buf->points[0].y == buf->points[1].y)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
|
|
const cairo_path_fixed_t *path)
|
|
{
|
|
iter->first = iter->buf = cairo_path_head (path);
|
|
iter->n_op = 0;
|
|
iter->n_point = 0;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter)
|
|
{
|
|
if (++iter->n_op >= iter->buf->num_ops) {
|
|
iter->buf = cairo_path_buf_next (iter->buf);
|
|
if (iter->buf == iter->first) {
|
|
iter->buf = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
iter->n_op = 0;
|
|
iter->n_point = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
|
|
cairo_box_t *box)
|
|
{
|
|
cairo_point_t points[5];
|
|
cairo_path_fixed_iter_t iter;
|
|
|
|
if (_iter->buf == NULL)
|
|
return FALSE;
|
|
|
|
iter = *_iter;
|
|
|
|
if (iter.n_op == iter.buf->num_ops &&
|
|
! _cairo_path_fixed_iter_next_op (&iter))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check whether the ops are those that would be used for a rectangle */
|
|
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
|
|
return FALSE;
|
|
points[0] = iter.buf->points[iter.n_point++];
|
|
if (! _cairo_path_fixed_iter_next_op (&iter))
|
|
return FALSE;
|
|
|
|
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
|
|
return FALSE;
|
|
points[1] = iter.buf->points[iter.n_point++];
|
|
if (! _cairo_path_fixed_iter_next_op (&iter))
|
|
return FALSE;
|
|
|
|
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
|
|
return FALSE;
|
|
points[2] = iter.buf->points[iter.n_point++];
|
|
if (! _cairo_path_fixed_iter_next_op (&iter))
|
|
return FALSE;
|
|
|
|
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
|
|
return FALSE;
|
|
points[3] = iter.buf->points[iter.n_point++];
|
|
if (! _cairo_path_fixed_iter_next_op (&iter))
|
|
return FALSE;
|
|
|
|
/* Now, there are choices. The rectangle might end with a LINE_TO
|
|
* (to the original point), but this isn't required. If it
|
|
* doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
|
|
if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO)
|
|
{
|
|
points[4] = iter.buf->points[iter.n_point++];
|
|
if (points[4].x != points[0].x || points[4].y != points[0].y)
|
|
return FALSE;
|
|
}
|
|
else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH ||
|
|
iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (! _cairo_path_fixed_iter_next_op (&iter))
|
|
return FALSE;
|
|
|
|
/* Ok, we may have a box, if the points line up */
|
|
if (points[0].y == points[1].y &&
|
|
points[1].x == points[2].x &&
|
|
points[2].y == points[3].y &&
|
|
points[3].x == points[0].x)
|
|
{
|
|
box->p1 = points[0];
|
|
box->p2 = points[2];
|
|
*_iter = iter;
|
|
return TRUE;
|
|
}
|
|
|
|
if (points[0].x == points[1].x &&
|
|
points[1].y == points[2].y &&
|
|
points[2].x == points[3].x &&
|
|
points[3].y == points[0].y)
|
|
{
|
|
box->p1 = points[1];
|
|
box->p2 = points[3];
|
|
*_iter = iter;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
|
|
{
|
|
if (iter->buf == NULL)
|
|
return TRUE;
|
|
|
|
if (iter->n_op == iter->buf->num_ops)
|
|
return TRUE;
|
|
|
|
if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO &&
|
|
iter->buf->num_ops == iter->n_op + 1)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|