cairo/src/cairo-traps.c

651 lines
20 KiB
C
Raw Normal View History

2002-08-07 19:48:49 +00:00
/*
* Copyright © 2002 Keith Packard
* Copyright © 2007 Red Hat, Inc.
2002-08-07 19:48:49 +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
*
* 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>
* 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
_cairo_traps_grow (cairo_traps_t *traps);
2002-08-07 19:48:49 +00:00
static cairo_status_t
_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
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 cairo_fixed_16_16_t
_compute_x (cairo_line_t *line, cairo_fixed_t y);
2002-08-07 19:48:49 +00:00
void
_cairo_traps_init (cairo_traps_t *traps)
2002-08-07 19:48:49 +00:00
{
traps->status = CAIRO_STATUS_SUCCESS;
traps->num_traps = 0;
2002-08-07 19:48:49 +00:00
traps->traps_size = 0;
traps->traps = NULL;
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
_cairo_traps_fini (cairo_traps_t *traps)
2002-08-07 19:48:49 +00:00
{
if (traps->traps && traps->traps != traps->traps_embedded)
free (traps->traps);
traps->traps = NULL;
traps->traps_size = 0;
traps->num_traps = 0;
2002-08-07 19:48:49 +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.
*
* 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);
traps->status = _cairo_traps_grow (traps);
if (traps->status)
return traps->status;
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;
return traps->status;
}
static cairo_status_t
_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
{
cairo_trapezoid_t *trap;
2002-08-07 19:48:49 +00:00
if (traps->status)
return traps->status;
if (top == bottom) {
2003-07-18 11:34:19 +00:00
return CAIRO_STATUS_SUCCESS;
}
if (traps->num_traps >= traps->traps_size) {
traps->status = _cairo_traps_grow (traps);
if (traps->status)
return traps->status;
2002-08-07 19:48:49 +00:00
}
trap = &traps->traps[traps->num_traps];
2002-08-07 19:48:49 +00:00
trap->top = top;
trap->bottom = bottom;
trap->left = *left;
trap->right = *right;
2002-08-07 19:48:49 +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;
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;
traps->num_traps++;
return traps->status;
2002-08-07 19:48:49 +00:00
}
cairo_status_t
_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
{
cairo_line_t left;
cairo_line_t right;
2002-08-07 19:48:49 +00: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;
return _cairo_traps_add_trap (traps, top, bottom, &left, &right);
2002-08-07 19:48:49 +00:00
}
/* make room for at least one more trap */
2003-07-18 11:34:19 +00:00
static cairo_status_t
_cairo_traps_grow (cairo_traps_t *traps)
2002-08-07 19:48:49 +00:00
{
cairo_trapezoid_t *new_traps;
int old_size = traps->traps_size;
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;
}
assert (traps->num_traps <= traps->traps_size);
2002-08-07 19:48:49 +00:00
if (traps->status)
return traps->status;
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
if (new_traps == NULL) {
traps->status = CAIRO_STATUS_NO_MEMORY;
return traps->status;
2002-08-07 19:48:49 +00:00
}
traps->traps = new_traps;
traps->traps_size = new_size;
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
{
const cairo_point_t *a = av, *b = bv;
2002-08-07 19:48:49 +00:00
int ret = a->y - b->y;
if (ret == 0) {
ret = a->x - b->x;
}
return ret;
2002-08-07 19:48:49 +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;
}
}
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
_cairo_traps_tessellate_triangle (cairo_traps_t *traps, cairo_point_t t[3])
{
cairo_line_t line;
cairo_fixed_16_16_t intersect;
cairo_point_t tsort[3];
memcpy (tsort, t, 3 * sizeof (cairo_point_t));
qsort (tsort, 3, sizeof (cairo_point_t), _compare_point_fixed_by_y);
/* horizontal top edge requires special handling */
if (tsort[0].y == tsort[1].y) {
if (tsort[0].x < tsort[1].x)
_cairo_traps_add_trap_from_points (traps,
tsort[1].y, tsort[2].y,
tsort[0], tsort[2],
tsort[1], tsort[2]);
else
_cairo_traps_add_trap_from_points (traps,
tsort[1].y, tsort[2].y,
tsort[1], tsort[2],
tsort[0], tsort[2]);
return traps->status;
}
line.p1 = tsort[0];
line.p2 = tsort[1];
2003-07-18 11:34:19 +00:00
intersect = _compute_x (&line, tsort[2].y);
if (intersect < tsort[2].x) {
_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]);
} else {
_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]);
}
return traps->status;
}
2003-07-18 11:34:19 +00:00
cairo_status_t
_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, cairo_point_t q[4])
2002-08-07 19:48:49 +00:00
{
int a, b, c, d;
int i;
cairo_slope_t ab, ad;
cairo_bool_t b_left_of_d;
2002-08-07 19:48:49 +00: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 */
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.
*
* First, we have to determine the Y-axis sort of the four
* vertices, (either abcd or abdc). After that we need to detemine
* which edges will be "left" and which will be "right" in the
* resulting trapezoids. This can be determined by computing a
* slope comparison of ab and ad to determine if b is left of d or
* not.
*
* Note that "left of" here is in the sense of which edges should
* be the left vs. right edges of the trapezoid. In particular, b
* left of d does *not* mean that b.x is less than d.x.
*
* This should hopefully be made clear in the lame ASCII art
* below. Since the same slope comparison is used in all cases, we
* compute it before testing for the Y-value sort. */
/* Note: If a == b then the ab slope doesn't give us any
* information. In that case, we can replace it with the ac (or
* equivalenly the bc) slope which gives us exactly the same
* information we need. At worst the names of the identifiers ab
* and b_left_of_d are inaccurate in this case, (would be ac, and
* c_left_of_d). */
if (q[a].x == q[b].x && q[a].y == q[b].y)
_cairo_slope_init (&ab, &q[a], &q[c]);
else
_cairo_slope_init (&ab, &q[a], &q[b]);
_cairo_slope_init (&ad, &q[a], &q[d]);
b_left_of_d = (_cairo_slope_compare (&ab, &ad) > 0);
if (q[c].y <= q[d].y) {
if (b_left_of_d) {
/* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad))
*
* top bot left right
* _a a a
* / / /| |\ a.y b.y ab ad
* b / b | b \
* / / | | \ \ b.y c.y bc ad
* c / c | c \
* | / \| \ \ c.y d.y cd ad
* d d 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 {
/* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad))
*
* a a a_
* /| |\ \ \ a.y b.y ad ab
* / b | b \ b
* / / | | \ \ b.y c.y ad bc
* / c | c \ c
* / / |/ \ | c.y d.y ad cd
* d d 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 {
if (b_left_of_d) {
/* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad))
*
* a a a
* // / \ |\ a.y b.y ab ad
* /b/ b \ b \
* / / \ \ \ \ b.y d.y bc ad
* /d/ \ d \ d
* // \ / \| d.y c.y bc dc
* c c 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 {
/* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad))
*
* a a a
* /| / \ \\ a.y b.y ad ab
* / b / b \b\
* / / / / \ \ b.y d.y ad bc
* d / d / \d\
* |/ \ / \\ d.y c.y dc bc
* c c 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
}
return traps->status;
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
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).
*/
static cairo_fixed_16_16_t
_compute_x (cairo_line_t *line, cairo_fixed_t y)
2002-08-07 19:48:49 +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
return line->p1.x + (ex / dy);
2002-08-07 19:48:49 +00:00
}
static cairo_bool_t
_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt)
{
cairo_slope_t slope_left, slope_pt, slope_right;
if (t->top > pt->y)
return FALSE;
if (t->bottom < pt->y)
return FALSE;
_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)
return FALSE;
_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)
return FALSE;
return TRUE;
}
cairo_bool_t
_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))
return TRUE;
}
return FALSE;
}
2004-04-06 09:36:12 +00:00
void
_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents)
{
*extents = traps->extents;
2004-04-06 09:36:12 +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)
*
* Determines if a set of trapezoids are exactly representable as a
* pixman region, and if so creates such a region.
*
* 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
&& _cairo_fixed_is_integer(traps->traps[i].top)
&& _cairo_fixed_is_integer(traps->traps[i].bottom)
&& _cairo_fixed_is_integer(traps->traps[i].left.p1.x)
&& _cairo_fixed_is_integer(traps->traps[i].right.p1.x))) {
*region = NULL;
return CAIRO_STATUS_SUCCESS;
}
*region = pixman_region_create ();
for (i = 0; i < traps->num_traps; i++) {
int x = _cairo_fixed_integer_part(traps->traps[i].left.p1.x);
int y = _cairo_fixed_integer_part(traps->traps[i].top);
int width = _cairo_fixed_integer_part(traps->traps[i].right.p1.x) - x;
int height = _cairo_fixed_integer_part(traps->traps[i].bottom) - y;
/* XXX: Sometimes we get degenerate trapezoids from the tesellator,
* if we call pixman_region_union_rect(), it bizarrly fails on such
* an empty rectangle, so skip them.
*/
if (width == 0 || height == 0)
continue;
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;
}