2002-08-07 19:48:49 +00:00
|
|
|
/*
|
2004-10-21 18:40:50 +00:00
|
|
|
* Copyright © 2002 Keith Packard
|
2002-08-07 19:48:49 +00:00
|
|
|
*
|
2004-10-12 14:17:23 +00:00
|
|
|
* 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.
|
2002-08-07 19:48:49 +00:00
|
|
|
*
|
2004-10-12 14:17:23 +00:00
|
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Keith Packard
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s):
|
|
|
|
|
* Keith R. Packard <keithp@keithp.com>
|
2005-02-22 11:35:03 +00:00
|
|
|
* Carl D. Worth <cworth@cworth.org>
|
2002-08-07 19:48:49 +00:00
|
|
|
*
|
2003-07-18 11:34:19 +00:00
|
|
|
* 2002-07-15: Converted from XRenderCompositeDoublePoly to cairo_trap. Carl D. Worth
|
2002-08-07 19:48:49 +00:00
|
|
|
*/
|
|
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
#include "cairoint.h"
|
2002-08-07 19:48:49 +00:00
|
|
|
|
|
|
|
|
/* private functions */
|
|
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
static cairo_status_t
|
2007-03-08 20:05:13 -05:00
|
|
|
_cairo_traps_grow (cairo_traps_t *traps);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-31 22:41:44 +00:00
|
|
|
static cairo_status_t
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom,
|
|
|
|
|
cairo_line_t *left, cairo_line_t *right);
|
2002-08-14 00:39:43 +00:00
|
|
|
|
2002-08-07 19:48:49 +00:00
|
|
|
static int
|
2003-07-18 11:34:19 +00:00
|
|
|
_compare_point_fixed_by_y (const void *av, const void *bv);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
|
|
|
|
static int
|
2003-07-18 11:34:19 +00:00
|
|
|
_compare_cairo_edge_by_top (const void *av, const void *bv);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
static int
|
|
|
|
|
_compare_cairo_edge_by_slope (const void *av, const void *bv);
|
|
|
|
|
|
2003-07-23 21:20:24 +00:00
|
|
|
static cairo_fixed_16_16_t
|
2003-07-30 08:30:50 +00:00
|
|
|
_compute_x (cairo_line_t *line, cairo_fixed_t y);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2004-01-22 20:47:24 +00:00
|
|
|
static int
|
2003-07-30 08:30:50 +00:00
|
|
|
_line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
|
|
|
|
void
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_init (cairo_traps_t *traps)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2006-11-10 12:37:01 -08:00
|
|
|
traps->status = CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
traps->num_traps = 0;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
traps->traps_size = 0;
|
|
|
|
|
traps->traps = NULL;
|
2007-03-08 19:34:43 -05:00
|
|
|
traps->extents.p1.x = traps->extents.p1.y = INT32_MAX;
|
|
|
|
|
traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_fini (cairo_traps_t *traps)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2007-03-08 20:32:36 -05:00
|
|
|
if (traps->traps != traps->traps_embedded)
|
2003-07-30 08:30:50 +00:00
|
|
|
free (traps->traps);
|
2007-03-08 20:32:36 -05:00
|
|
|
|
|
|
|
|
traps->traps = NULL;
|
|
|
|
|
traps->traps_size = 0;
|
|
|
|
|
traps->num_traps = 0;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2005-04-27 10:16:47 +00:00
|
|
|
/**
|
|
|
|
|
* _cairo_traps_init_box:
|
|
|
|
|
* @traps: a #cairo_traps_t
|
|
|
|
|
* @box: a box that will be converted to a single trapezoid
|
|
|
|
|
* to store in @traps.
|
2006-06-06 15:35:48 -07:00
|
|
|
*
|
2005-04-27 10:16:47 +00:00
|
|
|
* Initializes a cairo_traps_t to contain a single rectangular
|
|
|
|
|
* trapezoid.
|
|
|
|
|
**/
|
|
|
|
|
cairo_status_t
|
|
|
|
|
_cairo_traps_init_box (cairo_traps_t *traps,
|
|
|
|
|
cairo_box_t *box)
|
|
|
|
|
{
|
|
|
|
|
_cairo_traps_init (traps);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2007-03-08 20:05:13 -05:00
|
|
|
traps->status = _cairo_traps_grow (traps);
|
2006-11-10 12:37:01 -08:00
|
|
|
if (traps->status)
|
|
|
|
|
return traps->status;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2005-04-27 10:16:47 +00:00
|
|
|
traps->num_traps = 1;
|
|
|
|
|
|
|
|
|
|
traps->traps[0].top = box->p1.y;
|
|
|
|
|
traps->traps[0].bottom = box->p2.y;
|
|
|
|
|
traps->traps[0].left.p1 = box->p1;
|
|
|
|
|
traps->traps[0].left.p2.x = box->p1.x;
|
|
|
|
|
traps->traps[0].left.p2.y = box->p2.y;
|
|
|
|
|
traps->traps[0].right.p1.x = box->p2.x;
|
|
|
|
|
traps->traps[0].right.p1.y = box->p1.y;
|
|
|
|
|
traps->traps[0].right.p2 = box->p2;
|
|
|
|
|
|
|
|
|
|
traps->extents = *box;
|
|
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
return traps->status;
|
2005-04-27 10:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-31 22:41:44 +00:00
|
|
|
static cairo_status_t
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom,
|
|
|
|
|
cairo_line_t *left, cairo_line_t *right)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_trapezoid_t *trap;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
if (traps->status)
|
|
|
|
|
return traps->status;
|
|
|
|
|
|
2002-08-12 09:38:05 +00:00
|
|
|
if (top == bottom) {
|
2003-07-18 11:34:19 +00:00
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2002-08-12 09:38:05 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
if (traps->num_traps >= traps->traps_size) {
|
2007-03-08 20:05:13 -05:00
|
|
|
traps->status = _cairo_traps_grow (traps);
|
2006-11-10 12:37:01 -08:00
|
|
|
if (traps->status)
|
|
|
|
|
return traps->status;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
trap = &traps->traps[traps->num_traps];
|
2002-08-07 19:48:49 +00:00
|
|
|
trap->top = top;
|
|
|
|
|
trap->bottom = bottom;
|
2003-07-24 01:40:16 +00:00
|
|
|
trap->left = *left;
|
|
|
|
|
trap->right = *right;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2004-10-12 14:17:23 +00:00
|
|
|
if (top < traps->extents.p1.y)
|
|
|
|
|
traps->extents.p1.y = top;
|
|
|
|
|
if (bottom > traps->extents.p2.y)
|
|
|
|
|
traps->extents.p2.y = bottom;
|
|
|
|
|
/*
|
|
|
|
|
* This isn't generally accurate, but it is close enough for
|
|
|
|
|
* this purpose. Assuming that the left and right segments always
|
|
|
|
|
* contain the trapezoid vertical extents, these compares will
|
|
|
|
|
* yield a containing box. Assuming that the points all come from
|
|
|
|
|
* the same figure which will eventually be completely drawn, then
|
|
|
|
|
* the compares will yield the correct overall extents
|
|
|
|
|
*/
|
|
|
|
|
if (left->p1.x < traps->extents.p1.x)
|
|
|
|
|
traps->extents.p1.x = left->p1.x;
|
|
|
|
|
if (left->p2.x < traps->extents.p1.x)
|
|
|
|
|
traps->extents.p1.x = left->p2.x;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-10-12 14:17:23 +00:00
|
|
|
if (right->p1.x > traps->extents.p2.x)
|
|
|
|
|
traps->extents.p2.x = right->p1.x;
|
|
|
|
|
if (right->p2.x > traps->extents.p2.x)
|
|
|
|
|
traps->extents.p2.x = right->p2.x;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
traps->num_traps++;
|
2002-08-15 05:22:59 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
return traps->status;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2006-09-20 10:47:58 -07:00
|
|
|
cairo_status_t
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_add_trap_from_points (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom,
|
|
|
|
|
cairo_point_t left_p1, cairo_point_t left_p2,
|
|
|
|
|
cairo_point_t right_p1, cairo_point_t right_p2)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_line_t left;
|
|
|
|
|
cairo_line_t right;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
if (traps->status)
|
|
|
|
|
return traps->status;
|
|
|
|
|
|
2002-08-07 19:48:49 +00:00
|
|
|
left.p1 = left_p1;
|
|
|
|
|
left.p2 = left_p2;
|
|
|
|
|
|
|
|
|
|
right.p1 = right_p1;
|
|
|
|
|
right.p2 = right_p2;
|
|
|
|
|
|
2003-08-26 07:40:17 +00:00
|
|
|
return _cairo_traps_add_trap (traps, top, bottom, &left, &right);
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2007-03-08 20:32:36 -05:00
|
|
|
/* make room for at least one more trap */
|
2003-07-18 11:34:19 +00:00
|
|
|
static cairo_status_t
|
2007-03-08 20:05:13 -05:00
|
|
|
_cairo_traps_grow (cairo_traps_t *traps)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_trapezoid_t *new_traps;
|
|
|
|
|
int old_size = traps->traps_size;
|
2007-03-08 20:32:36 -05:00
|
|
|
int embedded_size = sizeof (traps->traps_embedded) / sizeof (traps->traps_embedded[0]);
|
|
|
|
|
int new_size = 2 * MAX (old_size, 16);
|
|
|
|
|
|
|
|
|
|
/* we have a local buffer at traps->traps_embedded. try to fulfill the request
|
|
|
|
|
* from there. */
|
|
|
|
|
if (old_size < embedded_size) {
|
|
|
|
|
traps->traps = traps->traps_embedded;
|
|
|
|
|
traps->traps_size = embedded_size;
|
|
|
|
|
return traps->status;
|
|
|
|
|
}
|
2007-03-08 20:05:13 -05:00
|
|
|
|
|
|
|
|
assert (traps->num_traps <= traps->traps_size);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
if (traps->status)
|
|
|
|
|
return traps->status;
|
|
|
|
|
|
2007-03-08 20:32:36 -05:00
|
|
|
if (traps->traps == traps->traps_embedded) {
|
|
|
|
|
new_traps = malloc (new_size * sizeof (cairo_trapezoid_t));
|
|
|
|
|
if (new_traps)
|
|
|
|
|
memcpy (new_traps, traps->traps, old_size * sizeof (cairo_trapezoid_t));
|
|
|
|
|
} else {
|
|
|
|
|
new_traps = realloc (traps->traps, new_size * sizeof (cairo_trapezoid_t));
|
|
|
|
|
}
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
if (new_traps == NULL) {
|
2006-11-10 12:37:01 -08:00
|
|
|
traps->status = CAIRO_STATUS_NO_MEMORY;
|
|
|
|
|
return traps->status;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
2002-08-15 05:22:59 +00:00
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
traps->traps = new_traps;
|
2007-03-08 20:05:13 -05:00
|
|
|
traps->traps_size = new_size;
|
2002-08-15 05:22:59 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
return traps->status;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2003-07-18 11:34:19 +00:00
|
|
|
_compare_point_fixed_by_y (const void *av, const void *bv)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-30 08:30:50 +00:00
|
|
|
const cairo_point_t *a = av, *b = bv;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2002-09-17 13:38:55 +00:00
|
|
|
int ret = a->y - b->y;
|
2002-08-12 09:38:05 +00:00
|
|
|
if (ret == 0) {
|
2002-09-17 13:38:55 +00:00
|
|
|
ret = a->x - b->x;
|
2002-08-12 09:38:05 +00:00
|
|
|
}
|
|
|
|
|
return ret;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2005-08-04 22:45:59 +00:00
|
|
|
void
|
|
|
|
|
_cairo_traps_translate (cairo_traps_t *traps, int x, int y)
|
|
|
|
|
{
|
|
|
|
|
cairo_fixed_t xoff, yoff;
|
|
|
|
|
cairo_trapezoid_t *t;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Ugh. The cairo_composite/(Render) interface doesn't allow
|
|
|
|
|
an offset for the trapezoids. Need to manually shift all
|
|
|
|
|
the coordinates to align with the offset origin of the
|
|
|
|
|
intermediate surface. */
|
|
|
|
|
|
|
|
|
|
xoff = _cairo_fixed_from_int (x);
|
|
|
|
|
yoff = _cairo_fixed_from_int (y);
|
|
|
|
|
|
|
|
|
|
for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) {
|
|
|
|
|
t->top += yoff;
|
|
|
|
|
t->bottom += yoff;
|
|
|
|
|
t->left.p1.x += xoff;
|
|
|
|
|
t->left.p1.y += yoff;
|
|
|
|
|
t->left.p2.x += xoff;
|
|
|
|
|
t->left.p2.y += yoff;
|
|
|
|
|
t->right.p1.x += xoff;
|
|
|
|
|
t->right.p1.y += yoff;
|
|
|
|
|
t->right.p2.x += xoff;
|
|
|
|
|
t->right.p2.y += yoff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-05-04 01:45:41 -07:00
|
|
|
void
|
|
|
|
|
_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
|
|
|
|
|
cairo_trapezoid_t *src_traps,
|
|
|
|
|
int num_traps,
|
|
|
|
|
double tx, double ty,
|
|
|
|
|
double sx, double sy)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
cairo_fixed_t xoff = _cairo_fixed_from_double (tx);
|
|
|
|
|
cairo_fixed_t yoff = _cairo_fixed_from_double (ty);
|
|
|
|
|
|
|
|
|
|
if (sx == 1.0 && sy == 1.0) {
|
|
|
|
|
for (i = 0; i < num_traps; i++) {
|
|
|
|
|
offset_traps[i].top = src_traps[i].top + yoff;
|
|
|
|
|
offset_traps[i].bottom = src_traps[i].bottom + yoff;
|
|
|
|
|
offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff;
|
|
|
|
|
offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff;
|
|
|
|
|
offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff;
|
|
|
|
|
offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff;
|
|
|
|
|
offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff;
|
|
|
|
|
offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff;
|
|
|
|
|
offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff;
|
|
|
|
|
offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cairo_fixed_t xsc = _cairo_fixed_from_double (sx);
|
|
|
|
|
cairo_fixed_t ysc = _cairo_fixed_from_double (sy);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_traps; i++) {
|
|
|
|
|
#define FIXED_MUL(_a, _b) \
|
|
|
|
|
(_cairo_int64_to_int32(_cairo_int64_rsl(_cairo_int32x32_64_mul((_a), (_b)), 16)))
|
|
|
|
|
|
|
|
|
|
offset_traps[i].top = FIXED_MUL(src_traps[i].top + yoff, ysc);
|
|
|
|
|
offset_traps[i].bottom = FIXED_MUL(src_traps[i].bottom + yoff, ysc);
|
|
|
|
|
offset_traps[i].left.p1.x = FIXED_MUL(src_traps[i].left.p1.x + xoff, xsc);
|
|
|
|
|
offset_traps[i].left.p1.y = FIXED_MUL(src_traps[i].left.p1.y + yoff, ysc);
|
|
|
|
|
offset_traps[i].left.p2.x = FIXED_MUL(src_traps[i].left.p2.x + xoff, xsc);
|
|
|
|
|
offset_traps[i].left.p2.y = FIXED_MUL(src_traps[i].left.p2.y + yoff, ysc);
|
|
|
|
|
offset_traps[i].right.p1.x = FIXED_MUL(src_traps[i].right.p1.x + xoff, xsc);
|
|
|
|
|
offset_traps[i].right.p1.y = FIXED_MUL(src_traps[i].right.p1.y + yoff, ysc);
|
|
|
|
|
offset_traps[i].right.p2.x = FIXED_MUL(src_traps[i].right.p2.x + xoff, xsc);
|
|
|
|
|
offset_traps[i].right.p2.y = FIXED_MUL(src_traps[i].right.p2.y + yoff, ysc);
|
|
|
|
|
|
|
|
|
|
#undef FIXED_MUL
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
cairo_status_t
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_tessellate_triangle (cairo_traps_t *traps, cairo_point_t t[3])
|
2003-01-28 07:23:54 +00:00
|
|
|
{
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_line_t line;
|
2003-07-23 21:20:24 +00:00
|
|
|
cairo_fixed_16_16_t intersect;
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_point_t tsort[3];
|
2003-01-28 07:23:54 +00:00
|
|
|
|
2003-07-30 08:30:50 +00:00
|
|
|
memcpy (tsort, t, 3 * sizeof (cairo_point_t));
|
|
|
|
|
qsort (tsort, 3, sizeof (cairo_point_t), _compare_point_fixed_by_y);
|
2003-01-28 07:23:54 +00:00
|
|
|
|
|
|
|
|
/* horizontal top edge requires special handling */
|
2003-01-28 13:49:57 +00:00
|
|
|
if (tsort[0].y == tsort[1].y) {
|
|
|
|
|
if (tsort[0].x < tsort[1].x)
|
2006-11-10 12:37:01 -08:00
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
tsort[1].y, tsort[2].y,
|
|
|
|
|
tsort[0], tsort[2],
|
|
|
|
|
tsort[1], tsort[2]);
|
2003-01-28 07:23:54 +00:00
|
|
|
else
|
2006-11-10 12:37:01 -08:00
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
tsort[1].y, tsort[2].y,
|
|
|
|
|
tsort[1], tsort[2],
|
|
|
|
|
tsort[0], tsort[2]);
|
|
|
|
|
return traps->status;
|
2003-01-28 07:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-28 13:49:57 +00:00
|
|
|
line.p1 = tsort[0];
|
|
|
|
|
line.p2 = tsort[1];
|
2003-01-28 07:23:54 +00:00
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
intersect = _compute_x (&line, tsort[2].y);
|
2003-01-28 07:23:54 +00:00
|
|
|
|
2003-01-28 13:49:57 +00:00
|
|
|
if (intersect < tsort[2].x) {
|
2006-11-10 12:37:01 -08:00
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
tsort[0].y, tsort[1].y,
|
|
|
|
|
tsort[0], tsort[1],
|
|
|
|
|
tsort[0], tsort[2]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
tsort[1].y, tsort[2].y,
|
|
|
|
|
tsort[1], tsort[2],
|
|
|
|
|
tsort[0], tsort[2]);
|
2003-01-28 07:23:54 +00:00
|
|
|
} else {
|
2006-11-10 12:37:01 -08:00
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
tsort[0].y, tsort[1].y,
|
|
|
|
|
tsort[0], tsort[2],
|
|
|
|
|
tsort[0], tsort[1]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
tsort[1].y, tsort[2].y,
|
|
|
|
|
tsort[0], tsort[2],
|
|
|
|
|
tsort[1], tsort[2]);
|
2003-01-28 07:23:54 +00:00
|
|
|
}
|
2003-01-28 13:49:57 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
return traps->status;
|
2003-01-28 07:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
cairo_status_t
|
2006-11-10 12:53:33 -08:00
|
|
|
_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, cairo_point_t q[4])
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2006-11-10 12:53:33 -08:00
|
|
|
int a, b, c, d;
|
|
|
|
|
int i;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2006-11-10 12:53:33 -08:00
|
|
|
/* Choose a as a point with minimal y */
|
|
|
|
|
a = 0;
|
|
|
|
|
for (i = 1; i < 4; i++)
|
|
|
|
|
if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0)
|
|
|
|
|
a = i;
|
|
|
|
|
|
2006-11-14 17:58:09 -05:00
|
|
|
/* b and d are adjacent to a, while c is opposite */
|
2006-11-10 12:53:33 -08:00
|
|
|
b = (a + 1) % 4;
|
|
|
|
|
c = (a + 2) % 4;
|
|
|
|
|
d = (a + 3) % 4;
|
|
|
|
|
|
|
|
|
|
/* Choose between b and d so that b.y is less than d.y */
|
|
|
|
|
if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) {
|
|
|
|
|
b = (a + 3) % 4;
|
|
|
|
|
d = (a + 1) % 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Without freedom left to choose anything else, we have four
|
|
|
|
|
* cases to tessellate which we can distinguish by comparing c.y
|
|
|
|
|
* to d.y and then by comparing b.x to d.x. And then for any of
|
|
|
|
|
* these cases there is a trivial way to emit three
|
|
|
|
|
* trapezoids. The 4 cases and their trapezoids are described and
|
|
|
|
|
* implemented below:
|
|
|
|
|
*/
|
|
|
|
|
if (q[c].y < q[d].y) {
|
|
|
|
|
if (q[b].x < q[d].x) {
|
|
|
|
|
/* c.y < d.y && b.x < d.x
|
|
|
|
|
*
|
|
|
|
|
* top bot left right
|
|
|
|
|
* a
|
|
|
|
|
* / | a.y b.y ab ad
|
|
|
|
|
* b |
|
|
|
|
|
* | | b.y c.y bc ad
|
|
|
|
|
* c |
|
|
|
|
|
* \ | c.y d.y cd ad
|
|
|
|
|
* d
|
|
|
|
|
*/
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[a].y, q[b].y,
|
|
|
|
|
q[a], q[b], q[a], q[d]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[b].y, q[c].y,
|
|
|
|
|
q[b], q[c], q[a], q[d]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[c].y, q[d].y,
|
|
|
|
|
q[c], q[d], q[a], q[d]);
|
|
|
|
|
} else {
|
|
|
|
|
/* c.y < d.y && b.x >= d.x
|
|
|
|
|
*
|
|
|
|
|
* a
|
|
|
|
|
* | \ a.y b.y ad ab
|
|
|
|
|
* | b
|
|
|
|
|
* | | b.y c.y ad bc
|
|
|
|
|
* | c
|
|
|
|
|
* | / c.y d.y ad cd
|
|
|
|
|
* d
|
|
|
|
|
*/
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[a].y, q[b].y,
|
|
|
|
|
q[a], q[d], q[a], q[b]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[b].y, q[c].y,
|
|
|
|
|
q[a], q[d], q[b], q[c]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[c].y, q[d].y,
|
|
|
|
|
q[a], q[d], q[c], q[d]);
|
|
|
|
|
}
|
2002-08-07 19:48:49 +00:00
|
|
|
} else {
|
2006-11-10 12:53:33 -08:00
|
|
|
if (q[b].x < q[d].x) {
|
|
|
|
|
/* c.y >= d.y && b.x < d.x
|
|
|
|
|
*
|
|
|
|
|
* a
|
|
|
|
|
* / \ a.y b.y ab ad
|
|
|
|
|
* b \
|
|
|
|
|
* \ \ b.y d.y bc ad
|
|
|
|
|
* \ d
|
|
|
|
|
* \ / d.y c.y bc dc
|
|
|
|
|
* c
|
|
|
|
|
*/
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[a].y, q[b].y,
|
|
|
|
|
q[a], q[b], q[a], q[d]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[b].y, q[d].y,
|
|
|
|
|
q[b], q[c], q[a], q[d]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[d].y, q[c].y,
|
|
|
|
|
q[b], q[c], q[d], q[c]);
|
|
|
|
|
} else {
|
|
|
|
|
/* c.y >= d.y && b.x >= d.x
|
|
|
|
|
*
|
|
|
|
|
* a
|
|
|
|
|
* / \ a.y b.y ad ab
|
|
|
|
|
* / b
|
|
|
|
|
* / / b.y d.y ad bc
|
|
|
|
|
* d /
|
|
|
|
|
* \ / d.y c.y dc bc
|
|
|
|
|
* c
|
|
|
|
|
*/
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[a].y, q[b].y,
|
|
|
|
|
q[a], q[d], q[a], q[b]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[b].y, q[d].y,
|
|
|
|
|
q[a], q[d], q[b], q[c]);
|
|
|
|
|
_cairo_traps_add_trap_from_points (traps,
|
|
|
|
|
q[d].y, q[c].y,
|
|
|
|
|
q[d], q[c], q[b], q[c]);
|
|
|
|
|
}
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
2002-08-15 05:22:59 +00:00
|
|
|
|
2006-11-10 12:37:01 -08:00
|
|
|
return traps->status;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2003-07-18 11:34:19 +00:00
|
|
|
_compare_cairo_edge_by_top (const void *av, const void *bv)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-18 11:34:19 +00:00
|
|
|
const cairo_edge_t *a = av, *b = bv;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
return a->edge.p1.y - b->edge.p1.y;
|
2002-09-17 13:38:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return value is:
|
|
|
|
|
> 0 if a is "clockwise" from b, (in a mathematical, not a graphical sense)
|
2003-07-18 11:34:19 +00:00
|
|
|
== 0 if slope (a) == slope (b)
|
2002-09-17 13:38:55 +00:00
|
|
|
< 0 if a is "counter-clockwise" from b
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2003-07-18 11:34:19 +00:00
|
|
|
_compare_cairo_edge_by_slope (const void *av, const void *bv)
|
2002-09-17 13:38:55 +00:00
|
|
|
{
|
2003-07-18 11:34:19 +00:00
|
|
|
const cairo_edge_t *a = av, *b = bv;
|
2003-07-23 21:20:24 +00:00
|
|
|
cairo_fixed_32_32_t d;
|
|
|
|
|
|
|
|
|
|
cairo_fixed_48_16_t a_dx = a->edge.p2.x - a->edge.p1.x;
|
|
|
|
|
cairo_fixed_48_16_t a_dy = a->edge.p2.y - a->edge.p1.y;
|
|
|
|
|
cairo_fixed_48_16_t b_dx = b->edge.p2.x - b->edge.p1.x;
|
|
|
|
|
cairo_fixed_48_16_t b_dy = b->edge.p2.y - b->edge.p1.y;
|
2002-09-17 13:38:55 +00:00
|
|
|
|
2003-07-23 21:20:24 +00:00
|
|
|
d = b_dy * a_dx - a_dy * b_dx;
|
2002-09-17 13:38:55 +00:00
|
|
|
|
2003-07-23 21:20:24 +00:00
|
|
|
if (d > 0)
|
|
|
|
|
return 1;
|
|
|
|
|
else if (d == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return -1;
|
2002-09-17 13:38:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2003-07-24 01:40:16 +00:00
|
|
|
_compare_cairo_edge_by_current_x_slope (const void *av, const void *bv)
|
2002-09-17 13:38:55 +00:00
|
|
|
{
|
2003-07-18 11:34:19 +00:00
|
|
|
const cairo_edge_t *a = av, *b = bv;
|
2002-09-17 13:38:55 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = a->current_x - b->current_x;
|
|
|
|
|
if (ret == 0)
|
2003-07-18 11:34:19 +00:00
|
|
|
ret = _compare_cairo_edge_by_slope (a, b);
|
2002-08-14 00:39:43 +00:00
|
|
|
return ret;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
/* XXX: Both _compute_x and _compute_inverse_slope will divide by zero
|
2002-09-20 09:43:42 +00:00
|
|
|
for horizontal lines. Now, we "know" that when we are tessellating
|
|
|
|
|
polygons that the polygon data structure discards all horizontal
|
|
|
|
|
edges, but there's nothing here to guarantee that. I suggest the
|
|
|
|
|
following:
|
|
|
|
|
|
|
|
|
|
A) Move all of the polygon tessellation code out of xrtraps.c and
|
|
|
|
|
into xrpoly.c, (in order to be in the same module as the code
|
|
|
|
|
discarding horizontal lines).
|
|
|
|
|
|
|
|
|
|
OR
|
|
|
|
|
|
|
|
|
|
B) Re-implement the line intersection in a way that avoids all
|
|
|
|
|
division by zero. Here's one approach. The only disadvantage
|
|
|
|
|
might be that that there are not meaningful names for all of the
|
|
|
|
|
sub-computations -- just a bunch of determinants. I haven't
|
|
|
|
|
looked at complexity, (both are probably similar and it probably
|
|
|
|
|
doesn't matter much anyway).
|
2004-05-28 12:37:15 +00:00
|
|
|
*/
|
2002-09-20 09:43:42 +00:00
|
|
|
|
2005-03-07 14:23:07 +00:00
|
|
|
/* XXX: Keith's new intersection code is much cleaner, and uses
|
|
|
|
|
* sufficient precision for correctly sorting intersections according
|
|
|
|
|
* to the analysis in Hobby's paper.
|
|
|
|
|
*
|
|
|
|
|
* But, when we enable this code, some things are failing, (eg. the
|
|
|
|
|
* stars in test/fill_rule get filled wrong). This could indicate a
|
|
|
|
|
* bug in one of tree places:
|
|
|
|
|
*
|
|
|
|
|
* 1) The new intersection code in this file
|
|
|
|
|
*
|
|
|
|
|
* 2) cairo_wideint.c (which is only exercised here)
|
|
|
|
|
*
|
|
|
|
|
* 3) In the current tessellator, (where the old intersection
|
|
|
|
|
* code, with its mystic increments could be masking the bug).
|
|
|
|
|
*
|
|
|
|
|
* It will likely be easier to revisit this when the new tessellation
|
|
|
|
|
* code is in place. So, for now, we'll simply disable the new
|
|
|
|
|
* intersection code.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE 0
|
|
|
|
|
|
|
|
|
|
#if CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE
|
2004-05-28 12:37:15 +00:00
|
|
|
static const cairo_fixed_32_32_t
|
|
|
|
|
_det16_32 (cairo_fixed_16_16_t a,
|
|
|
|
|
cairo_fixed_16_16_t b,
|
|
|
|
|
cairo_fixed_16_16_t c,
|
|
|
|
|
cairo_fixed_16_16_t d)
|
2002-09-20 09:43:42 +00:00
|
|
|
{
|
2004-05-28 12:37:15 +00:00
|
|
|
return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
|
|
|
|
|
_cairo_int32x32_64_mul (b, c));
|
2002-09-20 09:43:42 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
static const cairo_fixed_64_64_t
|
|
|
|
|
_det32_64 (cairo_fixed_32_32_t a,
|
|
|
|
|
cairo_fixed_32_32_t b,
|
|
|
|
|
cairo_fixed_32_32_t c,
|
|
|
|
|
cairo_fixed_32_32_t d)
|
2002-09-20 09:43:42 +00:00
|
|
|
{
|
2004-05-28 12:37:15 +00:00
|
|
|
return _cairo_int128_sub (_cairo_int64x64_128_mul (a, d),
|
|
|
|
|
_cairo_int64x64_128_mul (b, c));
|
|
|
|
|
}
|
2002-09-20 09:43:42 +00:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
static const cairo_fixed_32_32_t
|
|
|
|
|
_fixed_16_16_to_fixed_32_32 (cairo_fixed_16_16_t a)
|
|
|
|
|
{
|
|
|
|
|
return _cairo_int64_lsl (_cairo_int32_to_int64 (a), 16);
|
|
|
|
|
}
|
2002-09-20 09:43:42 +00:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
static int
|
|
|
|
|
_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_intersection)
|
|
|
|
|
{
|
|
|
|
|
cairo_fixed_16_16_t dx1, dx2, dy1, dy2;
|
|
|
|
|
cairo_fixed_32_32_t den_det;
|
|
|
|
|
cairo_fixed_32_32_t l1_det, l2_det;
|
|
|
|
|
cairo_fixed_64_64_t num_det;
|
|
|
|
|
cairo_fixed_32_32_t intersect_32_32;
|
|
|
|
|
cairo_fixed_48_16_t intersect_48_16;
|
|
|
|
|
cairo_fixed_16_16_t intersect_16_16;
|
|
|
|
|
cairo_quorem128_t qr;
|
|
|
|
|
|
|
|
|
|
dx1 = l1->p1.x - l1->p2.x;
|
|
|
|
|
dy1 = l1->p1.y - l1->p2.y;
|
|
|
|
|
dx2 = l2->p1.x - l2->p2.x;
|
|
|
|
|
dy2 = l2->p1.y - l2->p2.y;
|
|
|
|
|
den_det = _det16_32 (dx1, dy1,
|
|
|
|
|
dx2, dy2);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
if (_cairo_int64_eq (den_det, _cairo_int32_to_int64(0)))
|
2002-09-20 09:43:42 +00:00
|
|
|
return 0;
|
|
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
l1_det = _det16_32 (l1->p1.x, l1->p1.y,
|
|
|
|
|
l1->p2.x, l1->p2.y);
|
|
|
|
|
l2_det = _det16_32 (l2->p1.x, l2->p1.y,
|
|
|
|
|
l2->p2.x, l2->p2.y);
|
|
|
|
|
|
|
|
|
|
num_det = _det32_64 (l1_det, _fixed_16_16_to_fixed_32_32 (dy1),
|
|
|
|
|
l2_det, _fixed_16_16_to_fixed_32_32 (dy2));
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
/*
|
|
|
|
|
* Ok, this one is a bit tricky in fixed point, the denominator
|
|
|
|
|
* needs to be left with 32-bits of fraction so that the
|
|
|
|
|
* result of the divide ends up with 32-bits of fraction (64 - 32 = 32)
|
|
|
|
|
*/
|
|
|
|
|
qr = _cairo_int128_divrem (num_det, _cairo_int64_to_int128 (den_det));
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
intersect_32_32 = _cairo_int128_to_int64 (qr.quo);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
/*
|
|
|
|
|
* Find the ceiling of the quotient -- divrem returns
|
|
|
|
|
* the quotient truncated towards zero, so if the
|
|
|
|
|
* quotient should be positive (num_den and den_det have same sign)
|
|
|
|
|
* bump the quotient up by one.
|
|
|
|
|
*/
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
if (_cairo_int128_ne (qr.rem, _cairo_int32_to_int128 (0)) &&
|
|
|
|
|
(_cairo_int128_ge (num_det, _cairo_int32_to_int128 (0)) ==
|
|
|
|
|
_cairo_int64_ge (den_det, _cairo_int32_to_int64 (0))))
|
|
|
|
|
{
|
|
|
|
|
intersect_32_32 = _cairo_int64_add (intersect_32_32,
|
|
|
|
|
_cairo_int32_to_int64 (1));
|
|
|
|
|
}
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2006-06-06 15:35:48 -07:00
|
|
|
/*
|
2004-05-28 12:37:15 +00:00
|
|
|
* Now convert from 32.32 to 48.16 and take the ceiling;
|
|
|
|
|
* this requires adding in 15 1 bits and shifting the result
|
|
|
|
|
*/
|
2002-09-20 09:43:42 +00:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
intersect_32_32 = _cairo_int64_add (intersect_32_32,
|
|
|
|
|
_cairo_int32_to_int64 ((1 << 16) - 1));
|
|
|
|
|
intersect_48_16 = _cairo_int64_rsa (intersect_32_32, 16);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
/*
|
|
|
|
|
* And drop the top bits
|
|
|
|
|
*/
|
|
|
|
|
intersect_16_16 = _cairo_int64_to_int32 (intersect_48_16);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2004-05-28 12:37:15 +00:00
|
|
|
*y_intersection = intersect_16_16;
|
2002-09-20 09:43:42 +00:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2005-03-07 14:23:07 +00:00
|
|
|
#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */
|
2004-05-28 12:37:15 +00:00
|
|
|
|
2003-07-23 21:20:24 +00:00
|
|
|
static cairo_fixed_16_16_t
|
2003-07-30 08:30:50 +00:00
|
|
|
_compute_x (cairo_line_t *line, cairo_fixed_t y)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-23 21:20:24 +00:00
|
|
|
cairo_fixed_16_16_t dx = line->p2.x - line->p1.x;
|
|
|
|
|
cairo_fixed_32_32_t ex = (cairo_fixed_48_16_t) (y - line->p1.y) * (cairo_fixed_48_16_t) dx;
|
|
|
|
|
cairo_fixed_16_16_t dy = line->p2.y - line->p1.y;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2002-09-20 09:43:42 +00:00
|
|
|
return line->p1.x + (ex / dy);
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2005-03-07 14:23:07 +00:00
|
|
|
#if ! CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE
|
2002-08-07 19:48:49 +00:00
|
|
|
static double
|
2003-07-30 08:30:50 +00:00
|
|
|
_compute_inverse_slope (cairo_line_t *l)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2006-06-06 15:41:31 -07:00
|
|
|
return (_cairo_fixed_to_double (l->p2.x - l->p1.x) /
|
2003-09-05 15:29:49 +00:00
|
|
|
_cairo_fixed_to_double (l->p2.y - l->p1.y));
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double
|
2003-07-30 08:30:50 +00:00
|
|
|
_compute_x_intercept (cairo_line_t *l, double inverse_slope)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-09-05 15:29:49 +00:00
|
|
|
return _cairo_fixed_to_double (l->p1.x) - inverse_slope * _cairo_fixed_to_double (l->p1.y);
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2002-09-17 14:44:52 +00:00
|
|
|
static int
|
2003-07-30 08:30:50 +00:00
|
|
|
_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ret)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* x = m1y + b1
|
|
|
|
|
* x = m2y + b2
|
|
|
|
|
* m1y + b1 = m2y + b2
|
|
|
|
|
* y * (m1 - m2) = b2 - b1
|
|
|
|
|
* y = (b2 - b1) / (m1 - m2)
|
|
|
|
|
*/
|
2003-07-24 01:40:16 +00:00
|
|
|
cairo_fixed_16_16_t y_intersect;
|
|
|
|
|
double m1 = _compute_inverse_slope (l1);
|
|
|
|
|
double b1 = _compute_x_intercept (l1, m1);
|
|
|
|
|
double m2 = _compute_inverse_slope (l2);
|
|
|
|
|
double b2 = _compute_x_intercept (l2, m2);
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2002-09-17 14:44:52 +00:00
|
|
|
if (m1 == m2)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2003-11-06 18:33:28 +00:00
|
|
|
y_intersect = _cairo_fixed_from_double ((b2 - b1) / (m1 - m2));
|
2003-07-23 21:20:24 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
if (m1 < m2) {
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_line_t *t;
|
2003-07-24 01:40:16 +00:00
|
|
|
t = l1;
|
|
|
|
|
l1 = l2;
|
|
|
|
|
l2 = t;
|
|
|
|
|
}
|
2003-07-23 21:20:24 +00:00
|
|
|
|
2003-07-25 13:29:07 +00:00
|
|
|
/* Assuming 56 bits of floating point precision, the intersection
|
|
|
|
|
is accurate within one sub-pixel coordinate. We must ensure
|
|
|
|
|
that we return a value that is at or after the intersection. At
|
|
|
|
|
most, we must increment once. */
|
|
|
|
|
if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect))
|
2003-07-24 01:40:16 +00:00
|
|
|
y_intersect++;
|
2003-07-30 08:30:50 +00:00
|
|
|
/* XXX: Hmm... Keith's error calculations said we'd at most be off
|
|
|
|
|
by one sub-pixel. But, I found that the paint-fill-BE-01.svg
|
|
|
|
|
test from the W3C SVG conformance suite definitely requires two
|
|
|
|
|
increments.
|
|
|
|
|
|
|
|
|
|
It could be that we need one to overcome the error, and another
|
|
|
|
|
to round up.
|
|
|
|
|
|
|
|
|
|
It would be nice to be sure this code is correct, (but we can't
|
|
|
|
|
do the while loop as it will work for way to long on
|
|
|
|
|
exceedingly distant intersections with large errors that we
|
|
|
|
|
really don't care about anyway as they will be ignored by the
|
|
|
|
|
calling function.
|
|
|
|
|
*/
|
|
|
|
|
if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect))
|
|
|
|
|
y_intersect++;
|
2003-08-29 17:48:48 +00:00
|
|
|
/* XXX: hmm... now I found "intersection_killer" inside xrspline.c
|
|
|
|
|
that requires 3 increments. Clearly, we haven't characterized
|
|
|
|
|
this completely yet. */
|
|
|
|
|
if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect))
|
|
|
|
|
y_intersect++;
|
|
|
|
|
/* I think I've found the answer to our problems. The insight is
|
|
|
|
|
that everytime we round we are changing the slopes of the
|
|
|
|
|
relevant lines, so we may be introducing new intersections that
|
|
|
|
|
we miss, so everything breaks apart. John Hobby wrote a paper
|
|
|
|
|
on how to fix this:
|
|
|
|
|
|
|
|
|
|
[Hobby93c] John D. Hobby, Practical Segment Intersection with
|
|
|
|
|
Finite Precision Output, Computation Geometry Theory and
|
|
|
|
|
Applications, 13(4), 1999.
|
|
|
|
|
|
|
|
|
|
Available online (2003-08017):
|
|
|
|
|
|
|
|
|
|
http://cm.bell-labs.com/cm/cs/doc/93/2-27.ps.gz
|
|
|
|
|
|
|
|
|
|
Now we just need to go off and implement that.
|
|
|
|
|
*/
|
2003-07-23 21:20:24 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
*y_ret = y_intersect;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
return 1;
|
2002-09-13 19:01:01 +00:00
|
|
|
}
|
2005-03-07 14:23:07 +00:00
|
|
|
#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */
|
2002-09-13 19:01:01 +00:00
|
|
|
|
2002-09-17 14:21:36 +00:00
|
|
|
/* The algorithm here is pretty simple:
|
|
|
|
|
|
|
|
|
|
inactive = [edges]
|
2003-07-18 11:34:19 +00:00
|
|
|
y = min_p1_y (inactive)
|
2002-09-17 14:21:36 +00:00
|
|
|
|
|
|
|
|
while (num_active || num_inactive) {
|
|
|
|
|
active = all edges containing y
|
|
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
next_y = min ( min_p2_y (active), min_p1_y (inactive), min_intersection (active) )
|
2002-09-17 14:21:36 +00:00
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
fill_traps (active, y, next_y, fill_rule)
|
2002-09-17 14:21:36 +00:00
|
|
|
|
|
|
|
|
y = next_y
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
The invariants that hold during fill_traps are:
|
|
|
|
|
|
|
|
|
|
All edges in active contain both y and next_y
|
|
|
|
|
No edges in active intersect within y and next_y
|
|
|
|
|
|
|
|
|
|
These invariants mean that fill_traps is as simple as sorting the
|
|
|
|
|
active edges, forming a trapezoid between each adjacent pair. Then,
|
|
|
|
|
either the even-odd or winding rule is used to determine whether to
|
|
|
|
|
emit each of these trapezoids.
|
2003-01-28 13:49:57 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
Warning: This function obliterates the edges of the polygon provided.
|
2002-09-17 14:21:36 +00:00
|
|
|
*/
|
2003-07-18 11:34:19 +00:00
|
|
|
cairo_status_t
|
2003-08-26 07:40:17 +00:00
|
|
|
_cairo_traps_tessellate_polygon (cairo_traps_t *traps,
|
2004-02-12 19:02:33 +00:00
|
|
|
cairo_polygon_t *poly,
|
2003-08-26 07:40:17 +00:00
|
|
|
cairo_fill_rule_t fill_rule)
|
2002-08-07 19:48:49 +00:00
|
|
|
{
|
2003-07-24 01:40:16 +00:00
|
|
|
int i, active, inactive;
|
2003-07-30 08:30:50 +00:00
|
|
|
cairo_fixed_t y, y_next, intersect;
|
2003-07-24 01:40:16 +00:00
|
|
|
int in_out, num_edges = poly->num_edges;
|
2003-07-18 11:34:19 +00:00
|
|
|
cairo_edge_t *edges = poly->edges;
|
2002-08-14 00:44:28 +00:00
|
|
|
|
|
|
|
|
if (num_edges == 0)
|
2003-07-18 11:34:19 +00:00
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2002-08-14 00:44:28 +00:00
|
|
|
|
2003-07-18 11:34:19 +00:00
|
|
|
qsort (edges, num_edges, sizeof (cairo_edge_t), _compare_cairo_edge_by_top);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2002-08-07 19:48:49 +00:00
|
|
|
y = edges[0].edge.p1.y;
|
|
|
|
|
active = 0;
|
|
|
|
|
inactive = 0;
|
2003-07-24 01:40:16 +00:00
|
|
|
while (active < num_edges) {
|
|
|
|
|
while (inactive < num_edges && edges[inactive].edge.p1.y <= y)
|
2002-08-07 19:48:49 +00:00
|
|
|
inactive++;
|
|
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
for (i = active; i < inactive; i++)
|
|
|
|
|
edges[i].current_x = _compute_x (&edges[i].edge, y);
|
|
|
|
|
|
|
|
|
|
qsort (&edges[active], inactive - active,
|
|
|
|
|
sizeof (cairo_edge_t), _compare_cairo_edge_by_current_x_slope);
|
2002-09-17 13:38:55 +00:00
|
|
|
|
2002-08-07 19:48:49 +00:00
|
|
|
/* find next inflection point */
|
2003-07-24 01:40:16 +00:00
|
|
|
y_next = edges[active].edge.p2.y;
|
2002-09-17 13:38:55 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
for (i = active; i < inactive; i++) {
|
|
|
|
|
if (edges[i].edge.p2.y < y_next)
|
|
|
|
|
y_next = edges[i].edge.p2.y;
|
2002-08-07 19:48:49 +00:00
|
|
|
/* check intersect */
|
2003-07-24 01:40:16 +00:00
|
|
|
if (i != inactive - 1 && edges[i].current_x != edges[i+1].current_x)
|
|
|
|
|
if (_line_segs_intersect_ceil (&edges[i].edge, &edges[i+1].edge,
|
|
|
|
|
&intersect))
|
|
|
|
|
if (intersect > y && intersect < y_next)
|
|
|
|
|
y_next = intersect;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
/* check next inactive point */
|
2003-07-24 01:40:16 +00:00
|
|
|
if (inactive < num_edges && edges[inactive].edge.p1.y < y_next)
|
|
|
|
|
y_next = edges[inactive].edge.p1.y;
|
2002-08-07 19:48:49 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
/* walk the active edges generating trapezoids */
|
2002-08-07 19:48:49 +00:00
|
|
|
in_out = 0;
|
2003-07-24 01:40:16 +00:00
|
|
|
for (i = active; i < inactive - 1; i++) {
|
2003-07-18 11:34:19 +00:00
|
|
|
if (fill_rule == CAIRO_FILL_RULE_WINDING) {
|
2003-07-24 01:40:16 +00:00
|
|
|
if (edges[i].clockWise)
|
2002-08-07 19:48:49 +00:00
|
|
|
in_out++;
|
2003-07-24 01:40:16 +00:00
|
|
|
else
|
2002-08-07 19:48:49 +00:00
|
|
|
in_out--;
|
2003-07-24 01:40:16 +00:00
|
|
|
if (in_out == 0)
|
2002-08-07 19:48:49 +00:00
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
in_out++;
|
2003-07-24 01:40:16 +00:00
|
|
|
if ((in_out & 1) == 0)
|
2002-08-07 19:48:49 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2006-11-10 12:37:01 -08:00
|
|
|
_cairo_traps_add_trap (traps, y, y_next, &edges[i].edge, &edges[i+1].edge);
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
/* delete inactive edges */
|
|
|
|
|
for (i = active; i < inactive; i++) {
|
|
|
|
|
if (edges[i].edge.p2.y <= y_next) {
|
|
|
|
|
memmove (&edges[active+1], &edges[active], (i - active) * sizeof (cairo_edge_t));
|
|
|
|
|
active++;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
2002-09-17 13:38:55 +00:00
|
|
|
|
2003-07-24 01:40:16 +00:00
|
|
|
y = y_next;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
2006-11-10 12:37:01 -08:00
|
|
|
return traps->status;
|
2002-08-07 19:48:49 +00:00
|
|
|
}
|
2003-11-06 18:33:28 +00:00
|
|
|
|
2005-02-01 15:06:33 +00:00
|
|
|
static cairo_bool_t
|
2003-11-06 18:33:28 +00:00
|
|
|
_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt)
|
|
|
|
|
{
|
|
|
|
|
cairo_slope_t slope_left, slope_pt, slope_right;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2003-11-06 18:33:28 +00:00
|
|
|
if (t->top > pt->y)
|
2005-02-01 15:06:33 +00:00
|
|
|
return FALSE;
|
2003-11-06 18:33:28 +00:00
|
|
|
if (t->bottom < pt->y)
|
2005-02-01 15:06:33 +00:00
|
|
|
return FALSE;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2003-11-06 18:33:28 +00:00
|
|
|
_cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2);
|
|
|
|
|
_cairo_slope_init (&slope_pt, &t->left.p1, pt);
|
|
|
|
|
|
|
|
|
|
if (_cairo_slope_compare (&slope_left, &slope_pt) < 0)
|
2005-02-01 15:06:33 +00:00
|
|
|
return FALSE;
|
2003-11-06 18:33:28 +00:00
|
|
|
|
|
|
|
|
_cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2);
|
|
|
|
|
_cairo_slope_init (&slope_pt, &t->right.p1, pt);
|
|
|
|
|
|
|
|
|
|
if (_cairo_slope_compare (&slope_pt, &slope_right) < 0)
|
2005-02-01 15:06:33 +00:00
|
|
|
return FALSE;
|
2003-11-06 18:33:28 +00:00
|
|
|
|
2005-02-01 15:06:33 +00:00
|
|
|
return TRUE;
|
2003-11-06 18:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
2005-02-01 15:06:33 +00:00
|
|
|
cairo_bool_t
|
2003-11-06 18:33:28 +00:00
|
|
|
_cairo_traps_contain (cairo_traps_t *traps, double x, double y)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
cairo_point_t point;
|
|
|
|
|
|
|
|
|
|
point.x = _cairo_fixed_from_double (x);
|
|
|
|
|
point.y = _cairo_fixed_from_double (y);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
|
|
|
if (_cairo_trap_contains (&traps->traps[i], &point))
|
2005-02-01 15:06:33 +00:00
|
|
|
return TRUE;
|
2003-11-06 18:33:28 +00:00
|
|
|
}
|
|
|
|
|
|
2005-02-01 15:06:33 +00:00
|
|
|
return FALSE;
|
2003-11-06 18:33:28 +00:00
|
|
|
}
|
2004-04-06 09:36:12 +00:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents)
|
|
|
|
|
{
|
2004-10-12 14:17:23 +00:00
|
|
|
*extents = traps->extents;
|
2004-04-06 09:36:12 +00:00
|
|
|
}
|
2005-04-13 11:23:43 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* _cairo_traps_extract_region:
|
|
|
|
|
* @traps: a #cairo_traps_t
|
|
|
|
|
* @region: on return, %NULL is stored here if the trapezoids aren't
|
|
|
|
|
* exactly representable as a pixman region, otherwise a
|
|
|
|
|
* a pointer to such a region, newly allocated.
|
|
|
|
|
* (free with pixman region destroy)
|
2006-06-06 15:35:48 -07:00
|
|
|
*
|
2005-04-13 11:23:43 +00:00
|
|
|
* Determines if a set of trapezoids are exactly representable as a
|
|
|
|
|
* pixman region, and if so creates such a region.
|
2006-06-06 15:35:48 -07:00
|
|
|
*
|
2005-04-13 11:23:43 +00:00
|
|
|
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
|
|
|
|
**/
|
|
|
|
|
cairo_status_t
|
|
|
|
|
_cairo_traps_extract_region (cairo_traps_t *traps,
|
|
|
|
|
pixman_region16_t **region)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < traps->num_traps; i++)
|
|
|
|
|
if (!(traps->traps[i].left.p1.x == traps->traps[i].left.p2.x
|
|
|
|
|
&& traps->traps[i].right.p1.x == traps->traps[i].right.p2.x
|
2005-08-27 18:54:03 +00:00
|
|
|
&& _cairo_fixed_is_integer(traps->traps[i].top)
|
|
|
|
|
&& _cairo_fixed_is_integer(traps->traps[i].bottom)
|
2005-04-13 11:23:43 +00:00
|
|
|
&& _cairo_fixed_is_integer(traps->traps[i].left.p1.x)
|
2005-08-27 18:54:03 +00:00
|
|
|
&& _cairo_fixed_is_integer(traps->traps[i].right.p1.x))) {
|
2005-04-13 11:23:43 +00:00
|
|
|
*region = NULL;
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
}
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2005-04-13 11:23:43 +00:00
|
|
|
*region = pixman_region_create ();
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
|
|
|
int x = _cairo_fixed_integer_part(traps->traps[i].left.p1.x);
|
2005-08-27 18:54:03 +00:00
|
|
|
int y = _cairo_fixed_integer_part(traps->traps[i].top);
|
2005-04-13 11:23:43 +00:00
|
|
|
int width = _cairo_fixed_integer_part(traps->traps[i].right.p1.x) - x;
|
2005-08-27 18:54:03 +00:00
|
|
|
int height = _cairo_fixed_integer_part(traps->traps[i].bottom) - y;
|
2005-04-13 14:01:50 +00:00
|
|
|
|
2005-06-01 00:29:24 +00:00
|
|
|
/* XXX: Sometimes we get degenerate trapezoids from the tesellator,
|
2005-04-13 14:01:50 +00:00
|
|
|
* if we call pixman_region_union_rect(), it bizarrly fails on such
|
|
|
|
|
* an empty rectangle, so skip them.
|
|
|
|
|
*/
|
|
|
|
|
if (width == 0 || height == 0)
|
|
|
|
|
continue;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
2005-04-13 11:23:43 +00:00
|
|
|
if (pixman_region_union_rect (*region, *region,
|
|
|
|
|
x, y, width, height) != PIXMAN_REGION_STATUS_SUCCESS) {
|
|
|
|
|
pixman_region_destroy (*region);
|
|
|
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
}
|