Eliminate self-intersecting strokes.

We refactor the surface fallbacks to convert full strokes and fills to the
intermediate polygon representation (as opposed to before where we
returned the trapezoidal representation). This allow greater flexibility
to choose how then to rasterize the polygon. Where possible we use the
local spans rasteriser for its increased performance, but still have the
option to use the tessellator instead (for example, with the current
Render protocol which does not yet have a polygon image).

In order to accommodate this, the spans interface is tweaked to accept
whole polygons instead of a path and the tessellator is tweaked for speed.

Performance Impact
==================

...
Still measuring, expecting some severe regressions.
...
This commit is contained in:
Chris Wilson 2008-12-17 09:32:16 +00:00
parent 7c499db8af
commit f8bb3617c3
177 changed files with 6252 additions and 2139 deletions

18
NEWS
View file

@ -69,6 +69,24 @@ New experimental backends:
more offloading onto the GPU.
The initial work on the backend was performed by Eric Anholt.
Long standing bugs fixed:
Self-intersecting strokes.
A long standing bug where the coverage from overlapping semi-opaque
strokes (including neighbouring edges) was simply summed in lieu of
a costly global calculation has been fixed (by performing the costly
global calculation!) In order to mitigate the extra cost, the
tessellator has been overhauled and tune, which handles the fallback
for when we are unable to use the new span rasteriser on the stroke
(e.g. when using the current RENDER protocol). The large number of
pixel artefacts that implementing self-intersection elimination
removes is ample justification for the potential performance
regression. If you unfortunately do suffer a substantial performance
regression in your application, please consider obtaining a
cairo-trace and submitting it to us for analysis and inclusion into
our performance suite.
Snapshot 1.9.2 (2009-06-12)
===========================

View file

@ -99,5 +99,7 @@ COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS)
# cairo has been compiled with symbol hiding.
.c.i: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h
$(CPP) $(PREPROCESS_ARGS) $< -o $@
.c.s: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h
$(CC) $(COMPILE_ARGS) $< -S -o $@
include $(srcdir)/Makefile.am.analysis

File diff suppressed because it is too large Load diff

View file

@ -588,17 +588,19 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
_cairo_box_from_rectangle (&box, &clip_path->extents);
_cairo_traps_limit (&traps, &box);
status = _cairo_path_fixed_fill_to_traps (&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
&traps);
if (unlikely (status))
status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
clip_path->fill_rule,
&traps);
if (status) {
_cairo_traps_fini (&traps);
clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
return status;
}
status = _cairo_traps_extract_region (&traps, &clip_path->region);
_cairo_traps_fini (&traps);
if (status) {
if (unlikely (status)) {
clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
return status;
}

View file

@ -226,15 +226,61 @@ _cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b)
return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS));
}
/* computes a * b / c */
/* computes round (a * b / c) */
static inline cairo_fixed_t
_cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c)
{
cairo_int64_t ab = _cairo_int32x32_64_mul (a, b);
cairo_int64_t c64 = _cairo_int32_to_int64 (c);
cairo_int64_t quo = _cairo_int64_divrem (ab, c64).quo;
return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo);
}
return _cairo_int64_to_int32(quo);
/* computes floor (a * b / c) */
static inline cairo_fixed_t
_cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c)
{
return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c);
}
static inline cairo_fixed_t
_cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1,
const cairo_point_t *p2,
cairo_fixed_t x)
{
cairo_fixed_t y, dx;
if (x == p1->x)
return p1->y;
if (x == p2->x)
return p2->y;
y = p1->y;
dx = p2->x - p1->x;
if (dx != 0)
y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx);
return y;
}
static inline cairo_fixed_t
_cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1,
const cairo_point_t *p2,
cairo_fixed_t y)
{
cairo_fixed_t x, dy;
if (y == p1->y)
return p1->x;
if (y == p2->y)
return p2->x;
x = p1->x;
dy = p2->y - p1->y;
if (dy != 0)
x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy);
return x;
}
#else

View file

@ -67,4 +67,9 @@ typedef int32_t cairo_fixed_t;
/* An unsigned type of the same size as #cairo_fixed_t */
typedef uint32_t cairo_fixed_unsigned_t;
typedef struct _cairo_point {
cairo_fixed_t x;
cairo_fixed_t y;
} cairo_point_t;
#endif /* CAIRO_FIXED_TYPE_PRIVATE_H */

View file

@ -1184,7 +1184,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
&gstate->ctm_inverse,
gstate->tolerance,
&traps);
if (status == CAIRO_STATUS_SUCCESS) {
if (likely (status == CAIRO_STATUS_SUCCESS)) {
_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
x1, y1, x2, y2);
}
@ -1209,7 +1209,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate,
gstate->fill_rule,
gstate->tolerance,
&traps);
if (status == CAIRO_STATUS_SUCCESS) {
if (likely (status == CAIRO_STATUS_SUCCESS)) {
_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
x1, y1, x2, y2);
}

View file

@ -191,9 +191,6 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal)
void
_cairo_hash_table_destroy (cairo_hash_table_t *hash_table)
{
if (hash_table == NULL)
return;
/* The hash table must be empty. Otherwise, halt. */
assert (hash_table->live_entries == 0);
/* No iterators can be running. Otherwise, halt. */
@ -525,9 +522,6 @@ _cairo_hash_table_foreach (cairo_hash_table_t *hash_table,
unsigned long i;
cairo_hash_entry_t *entry;
if (hash_table == NULL)
return;
/* Mark the table for iteration */
++hash_table->iterating;
for (i = 0; i < hash_table->arrangement->size; i++) {

View file

@ -688,16 +688,9 @@ _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
return FALSE;
}
/* By pixel exact here, we mean a matrix that is composed only of
* 90 degree rotations, flips, and integer translations and produces a 1:1
* mapping between source and destination pixels. If we transform an image
* with a pixel-exact matrix, filtering is not useful.
*/
cairo_private cairo_bool_t
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
cairo_bool_t
_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix)
{
cairo_fixed_t x0_fixed, y0_fixed;
if (matrix->xy == 0.0 && matrix->yx == 0.0) {
if (! (matrix->xx == 1.0 || matrix->xx == -1.0))
return FALSE;
@ -711,6 +704,22 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
} else
return FALSE;
return TRUE;
}
/* By pixel exact here, we mean a matrix that is composed only of
* 90 degree rotations, flips, and integer translations and produces a 1:1
* mapping between source and destination pixels. If we transform an image
* with a pixel-exact matrix, filtering is not useful.
*/
cairo_bool_t
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
{
cairo_fixed_t x0_fixed, y0_fixed;
if (! _cairo_matrix_has_unity_scale (matrix))
return FALSE;
x0_fixed = _cairo_fixed_from_double (matrix->x0);
y0_fixed = _cairo_fixed_from_double (matrix->y0);

View file

@ -39,29 +39,25 @@
typedef struct cairo_filler {
double tolerance;
cairo_traps_t *traps;
cairo_point_t current_point;
cairo_polygon_t polygon;
cairo_polygon_t *polygon;
} cairo_filler_t;
static void
_cairo_filler_init (cairo_filler_t *filler, double tolerance, cairo_traps_t *traps)
_cairo_filler_init (cairo_filler_t *filler,
double tolerance,
cairo_polygon_t *polygon)
{
filler->tolerance = tolerance;
filler->traps = traps;
filler->polygon = polygon;
filler->current_point.x = 0;
filler->current_point.y = 0;
_cairo_polygon_init (&filler->polygon);
}
static void
_cairo_filler_fini (cairo_filler_t *filler)
{
_cairo_polygon_fini (&filler->polygon);
}
static cairo_status_t
@ -69,14 +65,14 @@ _cairo_filler_move_to (void *closure,
const cairo_point_t *point)
{
cairo_filler_t *filler = closure;
cairo_polygon_t *polygon = &filler->polygon;
cairo_polygon_t *polygon = filler->polygon;
_cairo_polygon_close (polygon);
_cairo_polygon_move_to (polygon, point);
filler->current_point = *point;
return _cairo_polygon_status (&filler->polygon);
return _cairo_polygon_status (filler->polygon);
}
static cairo_status_t
@ -84,13 +80,13 @@ _cairo_filler_line_to (void *closure,
const cairo_point_t *point)
{
cairo_filler_t *filler = closure;
cairo_polygon_t *polygon = &filler->polygon;
cairo_polygon_t *polygon = filler->polygon;
_cairo_polygon_line_to (polygon, point);
filler->current_point = *point;
return _cairo_polygon_status (&filler->polygon);
return _cairo_polygon_status (filler->polygon);
}
static cairo_status_t
@ -103,11 +99,10 @@ _cairo_filler_curve_to (void *closure,
cairo_spline_t spline;
if (! _cairo_spline_init (&spline,
_cairo_filler_line_to,
filler,
_cairo_filler_line_to, filler,
&filler->current_point, b, c, d))
{
return CAIRO_STATUS_SUCCESS;
return _cairo_filler_line_to (closure, d);
}
return _cairo_spline_decompose (&spline, filler->tolerance);
@ -117,36 +112,22 @@ static cairo_status_t
_cairo_filler_close_path (void *closure)
{
cairo_filler_t *filler = closure;
cairo_polygon_t *polygon = &filler->polygon;
cairo_polygon_t *polygon = filler->polygon;
_cairo_polygon_close (polygon);
return _cairo_polygon_status (polygon);
}
static cairo_int_status_t
_cairo_path_fixed_fill_rectangle (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps);
cairo_status_t
_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_traps_t *traps)
_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
double tolerance,
cairo_polygon_t *polygon)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_filler_t filler;
cairo_status_t status;
traps->maybe_region = path->maybe_fill_region;
/* Before we do anything else, we use a special-case filler for
* a device-axis aligned rectangle if possible. */
status = _cairo_path_fixed_fill_rectangle (path, fill_rule, traps);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
_cairo_filler_init (&filler, tolerance, traps);
_cairo_filler_init (&filler, tolerance, polygon);
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
@ -156,25 +137,48 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
_cairo_filler_close_path,
&filler);
if (unlikely (status))
goto BAIL;
return status;
_cairo_polygon_close (&filler.polygon);
status = _cairo_polygon_status (&filler.polygon);
if (unlikely (status))
goto BAIL;
status = _cairo_bentley_ottmann_tessellate_polygon (filler.traps,
&filler.polygon,
fill_rule);
if (unlikely (status))
goto BAIL;
BAIL:
_cairo_polygon_close (polygon);
status = _cairo_polygon_status (polygon);
_cairo_filler_fini (&filler);
return status;
}
cairo_status_t
_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_traps_t *traps)
{
cairo_polygon_t polygon;
cairo_status_t status;
if (path->is_rectilinear) {
status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
fill_rule,
traps);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
_cairo_polygon_init (&polygon);
status = _cairo_path_fixed_fill_to_polygon (path,
tolerance,
&polygon);
if (unlikely (status))
goto CLEANUP;
status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
fill_rule);
CLEANUP:
_cairo_polygon_fini (&polygon);
return status;
}
/* This special-case filler supports only a path that describes a
* device-axis aligned rectangle. It exists to avoid the overhead of
* the general tessellator when drawing very common rectangles.
@ -182,15 +186,14 @@ BAIL:
* If the path described anything but a device-axis aligned rectangle,
* this function will return %CAIRO_INT_STATUS_UNSUPPORTED.
*/
static cairo_int_status_t
_cairo_path_fixed_fill_rectangle (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps)
cairo_int_status_t
_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps)
{
cairo_box_t box;
if (! path->is_rectilinear)
return CAIRO_INT_STATUS_UNSUPPORTED;
assert (path->is_rectilinear);
if (_cairo_path_fixed_is_box (path, &box)) {
if (box.p1.x > box.p2.x) {

File diff suppressed because it is too large Load diff

View file

@ -393,194 +393,3 @@ _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
return i;
}
static int
_cairo_pen_stroke_spline_add_convolved_point (cairo_pen_stroke_spline_t *stroker,
const cairo_point_t *last_point,
const cairo_slope_t *slope,
cairo_point_t *last_hull_point,
int active,
int step)
{
do {
cairo_point_t hull_point;
hull_point.x = last_point->x + stroker->pen.vertices[active].point.x;
hull_point.y = last_point->y + stroker->pen.vertices[active].point.y;
_cairo_polygon_add_edge (&stroker->polygon,
last_hull_point, &hull_point,
step);
*last_hull_point = hull_point;
/* The strict inequalities here ensure that if a spline slope
* compares identically with either of the slopes of the
* active vertex, then it remains the active vertex. This is
* very important since otherwise we can trigger an infinite
* loop in the case of a degenerate pen, (a line), where
* neither vertex considers itself active for the slope---one
* will consider it as equal and reject, and the other will
* consider it unequal and reject. This is due to the inherent
* ambiguity when comparing slopes that differ by exactly
* pi. */
if (_cairo_slope_compare (slope,
&stroker->pen.vertices[active].slope_ccw) > 0)
{
if (++active == stroker->pen.num_vertices)
active = 0;
}
else if (_cairo_slope_compare (slope,
&stroker->pen.vertices[active].slope_cw) < 0)
{
if (--active == -1)
active = stroker->pen.num_vertices - 1;
}
else
{
return active;
}
} while (TRUE);
}
/* Compute outline of a given spline using the pen.
* The trapezoids needed to fill that outline will be added to traps
*/
cairo_status_t
_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t *stroker,
double tolerance,
cairo_traps_t *traps)
{
cairo_status_t status;
cairo_slope_t slope;
/* If the line width is so small that the pen is reduced to a
single point, then we have nothing to do. */
if (stroker->pen.num_vertices <= 1)
return CAIRO_STATUS_SUCCESS;
/* open the polygon */
slope = stroker->spline.initial_slope;
stroker->forward_vertex =
_cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope);
stroker->forward_hull_point.x = stroker->last_point.x +
stroker->pen.vertices[stroker->forward_vertex].point.x;
stroker->forward_hull_point.y = stroker->last_point.y +
stroker->pen.vertices[stroker->forward_vertex].point.y;
slope.dx = -slope.dx;
slope.dy = -slope.dy;
stroker->backward_vertex =
_cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope);
stroker->backward_hull_point.x = stroker->last_point.x +
stroker->pen.vertices[stroker->backward_vertex].point.x;
stroker->backward_hull_point.y = stroker->last_point.y +
stroker->pen.vertices[stroker->backward_vertex].point.y;
_cairo_polygon_add_edge (&stroker->polygon,
&stroker->backward_hull_point,
&stroker->forward_hull_point,
1);
status = _cairo_spline_decompose (&stroker->spline, tolerance);
if (unlikely (status))
return status;
/* close the polygon */
slope = stroker->spline.final_slope;
_cairo_pen_stroke_spline_add_convolved_point (stroker,
&stroker->last_point,
&slope,
&stroker->forward_hull_point,
stroker->forward_vertex,
1);
slope.dx = -slope.dx;
slope.dy = -slope.dy;
_cairo_pen_stroke_spline_add_convolved_point (stroker,
&stroker->last_point,
&slope,
&stroker->backward_hull_point,
stroker->backward_vertex,
-1);
_cairo_polygon_add_edge (&stroker->polygon,
&stroker->forward_hull_point,
&stroker->backward_hull_point,
1);
status = _cairo_polygon_status (&stroker->polygon);
if (unlikely (status))
return status;
status = _cairo_bentley_ottmann_tessellate_polygon (traps,
&stroker->polygon,
CAIRO_FILL_RULE_WINDING);
return status;
}
static cairo_status_t
_cairo_pen_stroke_spline_add_point (void *closure,
const cairo_point_t *point)
{
cairo_pen_stroke_spline_t *stroker = closure;
cairo_slope_t slope;
_cairo_slope_init (&slope, &stroker->last_point, point);
stroker->forward_vertex =
_cairo_pen_stroke_spline_add_convolved_point (stroker,
&stroker->last_point,
&slope,
&stroker->forward_hull_point,
stroker->forward_vertex,
1);
slope.dx = -slope.dx;
slope.dy = -slope.dy;
stroker->backward_vertex =
_cairo_pen_stroke_spline_add_convolved_point (stroker,
&stroker->last_point,
&slope,
&stroker->backward_hull_point,
stroker->backward_vertex,
-1);
stroker->last_point = *point;
return CAIRO_STATUS_SUCCESS;
}
cairo_int_status_t
_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker,
const cairo_pen_t *pen,
const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
cairo_int_status_t status;
if (! _cairo_spline_init (&stroker->spline,
_cairo_pen_stroke_spline_add_point,
stroker,
a, b, c, d))
{
return CAIRO_INT_STATUS_DEGENERATE;
}
status = _cairo_pen_init_copy (&stroker->pen, pen);
if (unlikely (status))
return status;
_cairo_polygon_init (&stroker->polygon);
stroker->last_point = *a;
return CAIRO_STATUS_SUCCESS;
}
void
_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker)
{
_cairo_polygon_fini (&stroker->polygon);
_cairo_pen_fini (&stroker->pen);
}

View file

@ -49,6 +49,18 @@ _cairo_polygon_init (cairo_polygon_t *polygon)
polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
polygon->has_current_point = FALSE;
polygon->has_limits = FALSE;
polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
}
void
_cairo_polygon_limit (cairo_polygon_t *polygon,
const cairo_box_t *limits)
{
polygon->has_limits = TRUE;
polygon->limits = *limits;
}
void
@ -93,17 +105,16 @@ _cairo_polygon_grow (cairo_polygon_t *polygon)
return TRUE;
}
void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2,
int dir)
static void
_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2,
int top, int bottom,
int dir)
{
cairo_edge_t *edge;
/* drop horizontal edges */
if (p1->y == p2->y)
return;
assert (top < bottom);
if (polygon->num_edges == polygon->edges_size) {
if (! _cairo_polygon_grow (polygon))
@ -111,26 +122,247 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon,
}
edge = &polygon->edges[polygon->num_edges++];
if (p1->y < p2->y) {
edge->edge.p1 = *p1;
edge->edge.p2 = *p2;
edge->dir = dir;
} else {
edge->edge.p1 = *p2;
edge->edge.p2 = *p1;
edge->dir = -dir;
edge->line.p1 = *p1;
edge->line.p2 = *p2;
edge->top = top;
edge->bottom = bottom;
edge->dir = dir;
if (top < polygon->extents.p1.y)
polygon->extents.p1.y = top;
if (bottom > polygon->extents.p2.y)
polygon->extents.p2.y = bottom;
if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) {
cairo_fixed_t x = p1->x;
if (top != p1->y)
x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top);
if (x < polygon->extents.p1.x)
polygon->extents.p1.x = x;
if (x > polygon->extents.p2.x)
polygon->extents.p2.x = x;
}
if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) {
cairo_fixed_t x = p2->x;
if (bottom != p2->y)
x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom);
if (x < polygon->extents.p1.x)
polygon->extents.p1.x = x;
if (x > polygon->extents.p2.x)
polygon->extents.p2.x = x;
}
}
static void
_add_clipped_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2,
int dir)
{
cairo_point_t p[2];
int top_y, bot_y;
if (p1->x <= polygon->limits.p1.x && p2->x <= polygon->limits.p1.x)
{
p[0].x = polygon->limits.p1.x;
p[0].y = polygon->limits.p1.y;
top_y = p1->y;
if (top_y < p[0].y)
top_y = p[0].y;
p[1].x = polygon->limits.p1.x;
p[1].y = polygon->limits.p2.y;
bot_y = p2->y;
if (bot_y > p[1].y)
bot_y = p[1].y;
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
}
else if (p1->x >= polygon->limits.p2.x && p2->x >= polygon->limits.p2.x)
{
p[0].x = polygon->limits.p2.x;
p[0].y = polygon->limits.p1.y;
top_y = p1->y;
if (top_y < p[0].y)
top_y = p[0].y;
p[1].x = polygon->limits.p2.x;
p[1].y = polygon->limits.p2.y;
bot_y = p2->y;
if (bot_y > p[1].y)
bot_y = p[1].y;
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
}
else if (p1->x >= polygon->limits.p1.x && p2->x >= polygon->limits.p1.x &&
p1->x <= polygon->limits.p2.x && p2->x <= polygon->limits.p2.x)
{
top_y = p1->y;
if (top_y < polygon->limits.p1.y)
top_y = polygon->limits.p1.y;
bot_y = p2->y;
if (bot_y > polygon->limits.p2.y)
bot_y = polygon->limits.p2.y;
_add_edge (polygon, p1, p2, top_y, bot_y, dir);
}
else
{
int left_y, right_y;
int p1_y, p2_y;
left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
polygon->limits.p1.x);
right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
polygon->limits.p2.x);
if (left_y == right_y) /* horizontal within bounds */
return;
p1_y = p1->y;
p2_y = p2->y;
if (left_y < right_y) {
if (p1->x < polygon->limits.p1.x && left_y > polygon->limits.p1.y)
{
p[0].x = polygon->limits.p1.x;
p[0].y = polygon->limits.p1.y;
top_y = p1_y;
if (top_y < p[0].y)
top_y = p[0].y;
p[1].x = polygon->limits.p1.x;
p[1].y = polygon->limits.p2.y;
bot_y = left_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p1_y = bot_y;
}
if (p2->x > polygon->limits.p2.x && right_y < polygon->limits.p2.y)
{
p[0].x = polygon->limits.p2.x;
p[0].y = polygon->limits.p1.y;
top_y = right_y;
if (top_y < p[0].y)
top_y = p[0].y;
p[1].x = polygon->limits.p2.x;
p[1].y = polygon->limits.p2.y;
bot_y = p2_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p2_y = top_y;
}
} else {
if (p1->x > polygon->limits.p2.x && right_y > polygon->limits.p1.y)
{
p[0].x = polygon->limits.p2.x;
p[0].y = polygon->limits.p1.y;
top_y = p1_y;
if (top_y < p[0].y)
top_y = p[0].y;
p[1].x = polygon->limits.p2.x;
p[1].y = polygon->limits.p2.y;
bot_y = right_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p1_y = bot_y;
}
if (p2->x < polygon->limits.p1.x && left_y < polygon->limits.p2.y)
{
p[0].x = polygon->limits.p1.x;
p[0].y = polygon->limits.p1.y;
top_y = left_y;
if (top_y < p[0].y)
top_y = p[0].y;
p[1].x = polygon->limits.p1.x;
p[1].y = polygon->limits.p2.y;
bot_y = p2_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p2_y = top_y;
}
}
if (p1_y < polygon->limits.p1.y)
p1_y = polygon->limits.p1.y;
if (p2_y > polygon->limits.p2.y)
p2_y = polygon->limits.p2.y;
if (p2_y > p1_y)
_add_edge (polygon, p1, p2, p1_y, p2_y, dir);
}
}
static void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
int dir;
/* drop horizontal edges */
if (p1->y == p2->y)
return;
if (p1->y < p2->y) {
dir = 1;
} else {
const cairo_point_t *t;
t = p1, p1 = p2, p2 = t;
dir = -1;
}
if (polygon->has_limits) {
if (p2->y <= polygon->limits.p1.y)
return;
if (p1->y >= polygon->limits.p2.y)
return;
_add_clipped_edge (polygon, p1, p2, dir);
} else
_add_edge (polygon, p1, p2, p1->y, p2->y, dir);
}
cairo_status_t
_cairo_polygon_add_external_edge (void *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
_cairo_polygon_add_edge (polygon, p1, p2);
return _cairo_polygon_status (polygon);
}
/* flattened path operations */
void
_cairo_polygon_move_to (cairo_polygon_t *polygon,
const cairo_point_t *point)
{
if (! polygon->has_current_point)
if (! polygon->has_current_point) {
polygon->first_point = *point;
polygon->has_current_point = TRUE;
}
polygon->current_point = *point;
polygon->has_current_point = TRUE;
}
void
@ -138,7 +370,7 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon,
const cairo_point_t *point)
{
if (polygon->has_current_point)
_cairo_polygon_add_edge (polygon, &polygon->current_point, point, 1);
_cairo_polygon_add_edge (polygon, &polygon->current_point, point);
_cairo_polygon_move_to (polygon, point);
}
@ -149,8 +381,7 @@ _cairo_polygon_close (cairo_polygon_t *polygon)
if (polygon->has_current_point) {
_cairo_polygon_add_edge (polygon,
&polygon->current_point,
&polygon->first_point,
1);
&polygon->first_point);
polygon->has_current_point = FALSE;
}

View file

@ -62,7 +62,7 @@ typedef struct _skip_elt {
#define SKIP_LIST_ELT_TO_DATA(type, elt) ((type *) ((char *) (elt) - (sizeof (type) - sizeof (skip_elt_t))))
typedef int
(*cairo_skip_list_compare_t) (void *list, void *a, void *b);
(*cairo_skip_list_compare_t) (void *list, const void *a, const void *b);
typedef struct _skip_list {
cairo_skip_list_compare_t compare;
@ -101,7 +101,7 @@ _cairo_skip_list_fini (cairo_skip_list_t *list);
* Otherwise data will be copied (elt_size bytes from <data> via
* memcpy) and the new element is returned. */
cairo_private void *
_cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique);
_cairo_skip_list_insert (cairo_skip_list_t *list, void *data);
/* Find an element which compare considers equal to <data> */
cairo_private void *

View file

@ -189,7 +189,7 @@ free_elt (cairo_skip_list_t *list, skip_elt_t *elt)
* Insert 'data' into the list
*/
void *
_cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique)
_cairo_skip_list_insert (cairo_skip_list_t *list, void *data)
{
skip_elt_t **update[MAX_LEVEL];
skip_elt_t *prev[MAX_LEVEL];
@ -209,7 +209,7 @@ _cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique)
for (; (elt = next[i]); next = elt->next)
{
int cmp = list->compare (list, ELT_DATA(elt), data);
if (unique && 0 == cmp)
if (0 == cmp)
return ELT_DATA(elt);
if (cmp > 0)
break;
@ -369,7 +369,7 @@ typedef struct {
} test_elt_t;
static int
test_cmp (void *list, void *A, void *B)
test_cmp (void *list, const void *A, const void *B)
{
const test_elt_t *a = A, *b = B;
return a->n - b->n;
@ -386,7 +386,7 @@ main (void)
for (n = 0; n < 10000000; n++) {
void *elt_and_data;
elt.n = n;
elt_and_data = _cairo_skip_list_insert (&list, &elt, TRUE);
elt_and_data = _cairo_skip_list_insert (&list, &elt);
assert (elt_and_data != NULL);
}
_cairo_skip_list_fini (&list);

View file

@ -94,9 +94,7 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
* of b by an infinitesimally small amount, (that is, 'a' will
* always be considered less than 'b').
*/
if (((a->dx > 0) != (b->dx > 0)) ||
((a->dy > 0) != (b->dy > 0)))
{
if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) {
if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
return +1;
else

View file

@ -75,22 +75,15 @@ struct _cairo_scan_converter {
/* Destroy this scan converter. */
cairo_destroy_func_t destroy;
/* Add an edge to the converter. */
cairo_status_t
(*add_edge)(
void *abstract_converter,
cairo_fixed_t x1,
cairo_fixed_t y1,
cairo_fixed_t x2,
cairo_fixed_t y2);
/* Add a polygon (set of edges) to the converter. */
cairo_status_t (*add_polygon) (void *abstract_converter,
const cairo_polygon_t *polygon);
/* Generates coverage spans for rows for the added edges and calls
* the renderer function for each row. After generating spans the
* only valid thing to do with the converter is to destroy it. */
cairo_status_t
(*generate)(
void *abstract_converter,
cairo_span_renderer_t *renderer);
cairo_status_t (*generate) (void *abstract_converter,
cairo_span_renderer_t *renderer);
/* Private status. Read with _cairo_scan_converter_status(). */
cairo_status_t status;
@ -99,12 +92,11 @@ struct _cairo_scan_converter {
/* Scan converter constructors. */
cairo_private cairo_scan_converter_t *
_cairo_tor_scan_converter_create(
int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule);
_cairo_tor_scan_converter_create (int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule);
/* cairo-spans.c: */
@ -132,14 +124,13 @@ _cairo_span_renderer_set_error (void *abstract_renderer,
cairo_status_t error);
cairo_private cairo_status_t
_cairo_path_fixed_fill_using_spans (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_path_fixed_t *path,
cairo_surface_t *dst,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_region_t *clip_region);
_cairo_surface_composite_polygon (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_polygon_t *polygon,
cairo_region_t *clip_region);
#endif /* CAIRO_SPANS_PRIVATE_H */

View file

@ -26,107 +26,7 @@
*/
#include "cairoint.h"
typedef struct {
cairo_scan_converter_t *converter;
cairo_point_t current_point;
cairo_point_t first_point;
cairo_bool_t has_first_point;
} scan_converter_filler_t;
static void
scan_converter_filler_init (scan_converter_filler_t *filler,
cairo_scan_converter_t *converter)
{
filler->converter = converter;
filler->current_point.x = 0;
filler->current_point.y = 0;
filler->first_point = filler->current_point;
filler->has_first_point = FALSE;
}
static cairo_status_t
scan_converter_filler_close_path (void *closure)
{
scan_converter_filler_t *filler = closure;
cairo_status_t status;
filler->has_first_point = FALSE;
if (filler->first_point.x == filler->current_point.x &&
filler->first_point.y == filler->current_point.y)
{
return CAIRO_STATUS_SUCCESS;
}
status = filler->converter->add_edge (
filler->converter,
filler->current_point.x, filler->current_point.y,
filler->first_point.x, filler->first_point.y);
filler->current_point = filler->first_point;
return status;
}
static cairo_status_t
scan_converter_filler_move_to (void *closure,
const cairo_point_t *p)
{
scan_converter_filler_t *filler = closure;
if (filler->has_first_point) {
cairo_status_t status = scan_converter_filler_close_path (closure);
if (status)
return status;
}
filler->current_point.x = p->x;
filler->current_point.y = p->y;
filler->first_point = filler->current_point;
filler->has_first_point = TRUE;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
scan_converter_filler_line_to (void *closure,
const cairo_point_t *p)
{
scan_converter_filler_t *filler = closure;
cairo_status_t status;
cairo_point_t to;
to.x = p->x;
to.y = p->y;
status = filler->converter->add_edge (
filler->converter,
filler->current_point.x, filler->current_point.y,
to.x, to.y);
filler->current_point = to;
return status;
}
static cairo_status_t
_cairo_path_fixed_fill_to_scan_converter (
cairo_path_fixed_t *path,
double tolerance,
cairo_scan_converter_t *converter)
{
scan_converter_filler_t filler;
cairo_status_t status;
scan_converter_filler_init (&filler, converter);
status = _cairo_path_fixed_interpret_flat (
path, CAIRO_DIRECTION_FORWARD,
scan_converter_filler_move_to,
scan_converter_filler_line_to,
scan_converter_filler_close_path,
&filler, tolerance);
if (status)
return status;
return scan_converter_filler_close_path (&filler);
}
#include "cairo-fixed-private.h"
static cairo_scan_converter_t *
_create_scan_converter (cairo_fill_rule_t fill_rule,
@ -135,51 +35,50 @@ _create_scan_converter (cairo_fill_rule_t fill_rule,
{
if (antialias == CAIRO_ANTIALIAS_NONE) {
ASSERT_NOT_REACHED;
return _cairo_scan_converter_create_in_error (
CAIRO_INT_STATUS_UNSUPPORTED);
}
else {
return _cairo_tor_scan_converter_create (
rects->mask.x,
rects->mask.y,
rects->mask.x + rects->width,
rects->mask.y + rects->height,
fill_rule);
return NULL;
}
return _cairo_tor_scan_converter_create (rects->mask.x,
rects->mask.y,
rects->mask.x + rects->width,
rects->mask.y + rects->height,
fill_rule);
}
/* XXX Add me to the compositor interface. Ok, first create the compositor
* interface, and then add this with associated fallback!
*/
cairo_status_t
_cairo_path_fixed_fill_using_spans (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_path_fixed_t *path,
cairo_surface_t *dst,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_region_t *clip_region)
_cairo_surface_composite_polygon (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_polygon_t *polygon,
cairo_region_t *clip_region)
{
cairo_span_renderer_t *renderer;
cairo_scan_converter_t *converter;
cairo_status_t status;
cairo_span_renderer_t *renderer = _cairo_surface_create_span_renderer (
op, pattern, dst, antialias, rects, clip_region);
cairo_scan_converter_t *converter = _create_scan_converter (
fill_rule, antialias, rects);
status = _cairo_path_fixed_fill_to_scan_converter (
path, tolerance, converter);
if (status)
goto BAIL;
converter = _create_scan_converter (fill_rule, antialias, rects);
status = converter->add_polygon (converter, polygon);
if (unlikely (status))
goto CLEANUP_CONVERTER;
renderer = _cairo_surface_create_span_renderer (op, pattern, surface,
antialias, rects,
clip_region);
status = converter->generate (converter, renderer);
if (status)
goto BAIL;
if (unlikely (status))
goto CLEANUP_RENDERER;
status = renderer->finish (renderer);
if (status)
goto BAIL;
BAIL:
CLEANUP_RENDERER:
renderer->destroy (renderer);
CLEANUP_CONVERTER:
converter->destroy (converter);
return status;
}
@ -191,17 +90,11 @@ _cairo_nil_destroy (void *abstract)
}
static cairo_status_t
_cairo_nil_scan_converter_add_edge (void *abstract_converter,
cairo_fixed_t x1,
cairo_fixed_t y1,
cairo_fixed_t x2,
cairo_fixed_t y2)
_cairo_nil_scan_converter_add_polygon (void *abstract_converter,
const cairo_polygon_t *polygon)
{
(void) abstract_converter;
(void) x1;
(void) y1;
(void) x2;
(void) y2;
(void) polygon;
return _cairo_scan_converter_status (abstract_converter);
}
@ -229,7 +122,7 @@ _cairo_scan_converter_set_error (void *abstract_converter,
if (error == CAIRO_STATUS_SUCCESS)
ASSERT_NOT_REACHED;
if (converter->status == CAIRO_STATUS_SUCCESS) {
converter->add_edge = _cairo_nil_scan_converter_add_edge;
converter->add_polygon = _cairo_nil_scan_converter_add_polygon;
converter->generate = _cairo_nil_scan_converter_generate;
converter->status = error;
}

View file

@ -34,6 +34,8 @@
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
#include "cairoint.h"
@ -41,6 +43,7 @@
#include "cairo-surface-fallback-private.h"
#include "cairo-clip-private.h"
#include "cairo-region-private.h"
#include "cairo-spans-private.h"
typedef struct {
cairo_surface_t *dst;
@ -739,8 +742,15 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
}
}
/* Otherwise we need to render the trapezoids to a mask and composite
* in the usual fashion.
/* No fast path, exclude self-intersections and clip trapezoids. */
if (traps->has_intersections) {
status = _cairo_bentley_ottmann_tessellate_traps (traps);
if (unlikely (status))
return status;
}
/* Otherwise render the trapezoids to a mask and composite in the usual
* fashion.
*/
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t trap_extents;
@ -754,20 +764,20 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
traps_info.traps = traps;
traps_info.antialias = antialias;
return _clip_and_composite (clip, op, src,
_composite_traps_draw_func,
&traps_info, dst, extents);
}
typedef struct {
cairo_path_fixed_t *path;
cairo_polygon_t *polygon;
cairo_fill_rule_t fill_rule;
double tolerance;
cairo_antialias_t antialias;
} cairo_composite_spans_fill_info_t;
} cairo_composite_spans_info_t;
static cairo_status_t
_composite_spans_fill_func (void *closure,
_composite_spans_draw_func (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
@ -777,7 +787,7 @@ _composite_spans_fill_func (void *closure,
cairo_region_t *clip_region)
{
cairo_composite_rectangles_t rects;
cairo_composite_spans_fill_info_t *info = closure;
cairo_composite_spans_info_t *info = closure;
_cairo_composite_rectangles_init (
&rects, extents->x, extents->y,
@ -789,12 +799,12 @@ _composite_spans_fill_func (void *closure,
rects.dst.x -= dst_x;
rects.dst.y -= dst_y;
return _cairo_path_fixed_fill_using_spans (op, src, info->path, dst,
info->fill_rule,
info->tolerance,
info->antialias,
&rects,
clip_region);
return _cairo_surface_composite_polygon (dst, op, src,
info->fill_rule,
info->antialias,
&rects,
info->polygon,
clip_region);
}
static cairo_bool_t
@ -930,11 +940,12 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_box_t box;
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_status_t status;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
@ -952,22 +963,70 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
_cairo_box_from_rectangle (&box, &extents);
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, &box);
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, &box);
status = _cairo_path_fixed_stroke_to_traps (path,
stroke_style,
ctm, ctm_inverse,
tolerance,
&traps);
if (unlikely (status))
goto FAIL;
if (path->is_rectilinear) {
status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
stroke_style,
ctm,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
status = _clip_and_composite_trapezoids (source, op,
surface, &traps, antialias,
if (_cairo_status_is_error (status))
goto CLEANUP;
}
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (unlikely (status))
goto CLEANUP;
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t polygon_extents;
_cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents);
if (! _cairo_rectangle_intersect (&extents, &polygon_extents))
goto CLEANUP;
}
if (antialias != CAIRO_ANTIALIAS_NONE &&
_cairo_surface_check_span_renderer (op, source, surface,
antialias, NULL))
{
cairo_composite_spans_info_t info;
info.polygon = &polygon;
info.fill_rule = CAIRO_FILL_RULE_WINDING;
info.antialias = antialias;
status = _clip_and_composite (clip, op, source,
_composite_spans_draw_func,
&info, surface, &extents);
goto CLEANUP;
}
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
goto CLEANUP;
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
clip, &extents);
FAIL:
CLEANUP:
_cairo_traps_fini (&traps);
_cairo_polygon_fini (&polygon);
return status;
}
@ -982,11 +1041,12 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_box_t box;
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_status_t status;
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded || clip);
@ -1002,71 +1062,69 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface,
if (! _rectangle_intersect_clip (&extents, clip))
return CAIRO_STATUS_SUCCESS;
/* XXX future direction:
status = _cairo_path_fixed_fill_to_region (path, fill_rule, &region);
if (status != CAIRO_STATUS_INT_UNSUPPORTED) {
if (unlikely (status))
return status;
status = _clip_and_composite_region ();
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
*/
/* Ask if the surface would like to render this combination of
* op/source/dst/antialias with spans or not, but don't actually
* make a renderer yet. We'll try to hit the region optimisations
* in _clip_and_composite_trapezoids() if it looks like the path
* is a region. */
/* TODO: Until we have a mono scan converter we won't even try
* to use spans for CAIRO_ANTIALIAS_NONE. */
/* TODO: The region filling code should be lifted from
* _clip_and_composite_trapezoids() and given first priority
* explicitly before deciding between spans and trapezoids. */
if (antialias != CAIRO_ANTIALIAS_NONE && ! path->is_rectilinear &&
_cairo_surface_check_span_renderer (
op, source, surface, antialias, NULL))
{
cairo_composite_spans_fill_info_t info;
info.path = path;
info.fill_rule = fill_rule;
info.tolerance = tolerance;
info.antialias = antialias;
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t path_extents;
_cairo_path_fixed_approximate_clip_extents (path,
&path_extents);
if (! _cairo_rectangle_intersect (&extents, &path_extents))
return CAIRO_STATUS_SUCCESS;
}
return _clip_and_composite (clip, op, source,
_composite_spans_fill_func,
&info,
surface,
&extents);
}
/* Fall back to trapezoid fills. */
_cairo_box_from_rectangle (&box, &extents);
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, &box);
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, &box);
status = _cairo_path_fixed_fill_to_traps (path,
fill_rule,
tolerance,
&traps);
if (unlikely (status))
goto FAIL;
if (path->is_rectilinear) {
status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
fill_rule,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
if (_cairo_status_is_error (status))
goto CLEANUP;
}
status = _cairo_path_fixed_fill_to_polygon (path,
tolerance,
&polygon);
if (unlikely (status))
goto CLEANUP;
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t polygon_extents;
_cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents);
if (! _cairo_rectangle_intersect (&extents, &polygon_extents))
goto CLEANUP;
}
if (antialias != CAIRO_ANTIALIAS_NONE &&
_cairo_surface_check_span_renderer (op, source, surface,
antialias, NULL))
{
cairo_composite_spans_info_t info;
info.polygon = &polygon;
info.fill_rule = fill_rule;
info.antialias = antialias;
status = _clip_and_composite (clip, op, source,
_composite_spans_draw_func,
&info, surface, &extents);
goto CLEANUP;
}
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
fill_rule);
if (unlikely (status))
goto CLEANUP;
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
clip, &extents);
FAIL:
CLEANUP:
_cairo_traps_fini (&traps);
_cairo_polygon_fini (&polygon);
return status;
}

View file

@ -199,11 +199,8 @@ glitter_scan_converter_reset(
* converter should be reset or destroyed. Dir must be +1 or -1,
* with the latter reversing the orientation of the edge. */
I glitter_status_t
glitter_scan_converter_add_edge(
glitter_scan_converter_t *converter,
glitter_input_scaled_t x1, glitter_input_scaled_t y1,
glitter_input_scaled_t x2, glitter_input_scaled_t y2,
int dir);
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
const cairo_edge_t *edge);
/* Render the polygon in the scan converter to the given A8 format
* image raster. Only the pixels accessible as pixels[y*stride+x] for
@ -623,10 +620,8 @@ _pool_alloc_from_new_chunk(
}
if (NULL == chunk) {
chunk = _pool_chunk_create(
pool->current,
capacity);
if (NULL == chunk)
chunk = _pool_chunk_create (pool->current, capacity);
if (unlikely (NULL == chunk))
return NULL;
}
pool->current = chunk;
@ -643,9 +638,7 @@ _pool_alloc_from_new_chunk(
* allocation failures. The pool retains ownership of the returned
* memory. */
inline static void *
pool_alloc(
struct pool *pool,
size_t size)
pool_alloc (struct pool *pool, size_t size)
{
struct _pool_chunk *chunk = pool->current;
@ -653,15 +646,14 @@ pool_alloc(
void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
chunk->size += size;
return obj;
}
else {
} else {
return _pool_alloc_from_new_chunk(pool, size);
}
}
/* Relinquish all pool_alloced memory back to the pool. */
static void
pool_reset(struct pool *pool)
pool_reset (struct pool *pool)
{
/* Transfer all used chunks to the chunk free list. */
struct _pool_chunk *chunk = pool->current;
@ -680,19 +672,18 @@ pool_reset(struct pool *pool)
/* Rewinds the cell list's cursor to the beginning. After rewinding
* we're good to cell_list_find() the cell any x coordinate. */
inline static void
cell_list_rewind(struct cell_list *cells)
cell_list_rewind (struct cell_list *cells)
{
cells->cursor = &cells->head;
}
/* Rewind the cell list if its cursor has been advanced past x. */
inline static void
cell_list_maybe_rewind(struct cell_list *cells, int x)
cell_list_maybe_rewind (struct cell_list *cells, int x)
{
struct cell *tail = *cells->cursor;
if (tail->x > x) {
cell_list_rewind(cells);
}
if (tail->x > x)
cell_list_rewind (cells);
}
static void
@ -704,24 +695,24 @@ cell_list_init(struct cell_list *cells)
cells->tail.next = NULL;
cells->tail.x = INT_MAX;
cells->head = &cells->tail;
cell_list_rewind(cells);
cell_list_rewind (cells);
}
static void
cell_list_fini(struct cell_list *cells)
{
pool_fini(cells->cell_pool.base);
cell_list_init(cells);
pool_fini (cells->cell_pool.base);
cell_list_init (cells);
}
/* Empty the cell list. This is called at the start of every pixel
* row. */
inline static void
cell_list_reset(struct cell_list *cells)
cell_list_reset (struct cell_list *cells)
{
cell_list_rewind(cells);
cell_list_rewind (cells);
cells->head = &cells->tail;
pool_reset(cells->cell_pool.base);
pool_reset (cells->cell_pool.base);
}
/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
@ -730,7 +721,7 @@ cell_list_reset(struct cell_list *cells)
* cell_list_rewind(). Ownership of the returned cell is retained by
* the cell list. */
inline static struct cell *
cell_list_find(struct cell_list *cells, int x)
cell_list_find (struct cell_list *cells, int x)
{
struct cell **cursor = cells->cursor;
struct cell *tail;
@ -749,11 +740,12 @@ cell_list_find(struct cell_list *cells, int x)
if (tail->x == x) {
return tail;
} else {
struct cell *cell = pool_alloc(
cells->cell_pool.base,
sizeof(struct cell));
if (NULL == cell)
struct cell *cell;
cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
if (unlikely (NULL == cell))
return NULL;
*cursor = cell;
cell->next = tail;
cell->x = x;
@ -785,17 +777,18 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2)
cell1 = *cursor;
if (cell1->x > x1)
break;
if (cell1->x == x1)
goto found_first;
cursor = &cell1->next;
});
}
/* New first cell at x1. */
newcell = pool_alloc(
cells->cell_pool.base,
sizeof(struct cell));
if (NULL != newcell) {
newcell = pool_alloc (cells->cell_pool.base,
sizeof (struct cell));
if (likely (NULL != newcell)) {
*cursor = newcell;
newcell->next = cell1;
newcell->x = x1;
@ -818,10 +811,9 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2)
}
/* New second cell at x2. */
newcell = pool_alloc(
cells->cell_pool.base,
sizeof(struct cell));
if (NULL != newcell) {
newcell = pool_alloc (cells->cell_pool.base,
sizeof (struct cell));
if (likely (NULL != newcell)) {
*cursor = newcell;
newcell->next = cell2;
newcell->x = x2;
@ -849,12 +841,13 @@ cell_list_add_unbounded_subspan(
GRID_X_TO_INT_FRAC(x, ix, fx);
cell = cell_list_find(cells, ix);
if (cell) {
cell = cell_list_find (cells, ix);
if (likely (cell != NULL)) {
cell->uncovered_area += 2*fx;
cell->covered_height++;
return GLITTER_STATUS_SUCCESS;
}
return GLITTER_STATUS_NO_MEMORY;
}
@ -874,17 +867,16 @@ cell_list_add_subspan(
if (ix1 != ix2) {
struct cell_pair p;
p = cell_list_find_pair(cells, ix1, ix2);
if (p.cell1 && p.cell2) {
if (likely (p.cell1 != NULL && p.cell2 != NULL)) {
p.cell1->uncovered_area += 2*fx1;
++p.cell1->covered_height;
p.cell2->uncovered_area -= 2*fx2;
--p.cell2->covered_height;
return GLITTER_STATUS_SUCCESS;
}
}
else {
} else {
struct cell *cell = cell_list_find(cells, ix1);
if (cell) {
if (likely (cell != NULL)) {
cell->uncovered_area += 2*(fx1-fx2);
return GLITTER_STATUS_SUCCESS;
}
@ -938,8 +930,9 @@ cell_list_render_edge(
/* We always know that ix1 is >= the cell list cursor in this
* case due to the no-intersections precondition. */
struct cell *cell = cell_list_find(cells, ix1);
if (NULL == cell)
if (unlikely (NULL == cell))
return GLITTER_STATUS_NO_MEMORY;
cell->covered_height += sign*GRID_Y;
cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
return GLITTER_STATUS_SUCCESS;
@ -988,7 +981,7 @@ cell_list_render_edge(
cell_list_maybe_rewind(cells, ix1);
pair = cell_list_find_pair(cells, ix1, ix1+1);
if (!pair.cell1 || !pair.cell2)
if (unlikely (!pair.cell1 || !pair.cell2))
return GLITTER_STATUS_NO_MEMORY;
pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
@ -1016,7 +1009,7 @@ cell_list_render_edge(
++ix1;
cell = cell_list_find(cells, ix1);
if (NULL == cell)
if (unlikely (NULL == cell))
return GLITTER_STATUS_NO_MEMORY;
} while (ix1 != ix2);
@ -1030,22 +1023,22 @@ cell_list_render_edge(
}
static void
polygon_init(struct polygon *polygon)
polygon_init (struct polygon *polygon)
{
polygon->ymin = polygon->ymax = 0;
polygon->y_buckets = polygon->y_buckets_embedded;
pool_init(polygon->edge_pool.base,
8192 - sizeof(struct _pool_chunk),
sizeof(polygon->edge_pool.embedded));
pool_init (polygon->edge_pool.base,
8192 - sizeof (struct _pool_chunk),
sizeof (polygon->edge_pool.embedded));
}
static void
polygon_fini(struct polygon *polygon)
polygon_fini (struct polygon *polygon)
{
if (polygon->y_buckets != polygon->y_buckets_embedded)
free(polygon->y_buckets);
pool_fini(polygon->edge_pool.base);
polygon_init(polygon);
free (polygon->y_buckets);
pool_fini (polygon->edge_pool.base);
}
/* Empties the polygon of all edges. The polygon is then prepared to
@ -1063,11 +1056,12 @@ polygon_reset(
pool_reset(polygon->edge_pool.base);
if (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)
if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
goto bail_no_mem; /* even if you could, you wouldn't want to. */
if (polygon->y_buckets != polygon->y_buckets_embedded)
free (polygon->y_buckets);
polygon->y_buckets = polygon->y_buckets_embedded;
if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
polygon->y_buckets = _cairo_malloc_ab (num_buckets,
@ -1099,11 +1093,8 @@ _polygon_insert_edge_into_its_y_bucket(
}
inline static glitter_status_t
polygon_add_edge(
struct polygon *polygon,
int x0, int y0,
int x1, int y1,
int dir)
polygon_add_edge (struct polygon *polygon,
const cairo_edge_t *edge)
{
struct edge *e;
grid_scaled_x_t dx;
@ -1112,54 +1103,45 @@ polygon_add_edge(
grid_scaled_y_t ymin = polygon->ymin;
grid_scaled_y_t ymax = polygon->ymax;
if (y0 == y1)
assert (edge->bottom > edge->top);
if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
return GLITTER_STATUS_SUCCESS;
if (y0 > y1) {
int tmp;
tmp = x0; x0 = x1; x1 = tmp;
tmp = y0; y0 = y1; y1 = tmp;
dir = -dir;
}
if (y0 >= ymax || y1 <= ymin)
return GLITTER_STATUS_SUCCESS;
e = pool_alloc(polygon->edge_pool.base,
sizeof(struct edge));
if (NULL == e)
e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
if (unlikely (NULL == e))
return GLITTER_STATUS_NO_MEMORY;
dx = x1 - x0;
dy = y1 - y0;
dx = edge->line.p2.x - edge->line.p1.x;
dy = edge->line.p2.y - edge->line.p1.y;
e->dy = dy;
e->dxdy = floored_divrem(dx, dy);
e->dxdy = floored_divrem (dx, dy);
if (ymin <= y0) {
ytop = y0;
e->x.quo = x0;
e->x.rem = 0;
}
else {
if (ymin <= edge->top)
ytop = edge->top;
else
ytop = ymin;
e->x = floored_muldivrem(ymin - y0, dx, dy);
e->x.quo += x0;
if (ytop == edge->line.p1.y) {
e->x.quo = edge->line.p1.x;
e->x.rem = 0;
} else {
e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
e->x.quo += edge->line.p1.x;
}
e->dir = dir;
e->dir = edge->dir;
e->ytop = ytop;
ybot = y1 < ymax ? y1 : ymax;
ybot = edge->bottom < ymax ? edge->bottom : ymax;
e->height_left = ybot - ytop;
if (e->height_left >= GRID_Y) {
e->dxdy_full = floored_muldivrem(GRID_Y, dx, dy);
}
else {
e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
} else {
e->dxdy_full.quo = 0;
e->dxdy_full.rem = 0;
}
_polygon_insert_edge_into_its_y_bucket(polygon, e);
_polygon_insert_edge_into_its_y_bucket (polygon, e);
e->x.rem -= dy; /* Bias the remainder for faster
* edge advancement. */
@ -1167,8 +1149,7 @@ polygon_add_edge(
}
static void
active_list_reset(
struct active_list *active)
active_list_reset (struct active_list *active)
{
active->head = NULL;
active->min_height = 0;
@ -1292,8 +1273,7 @@ active_list_merge_edges_from_polygon(
subrow_edges = tail;
if (tail->height_left < min_height)
min_height = tail->height_left;
}
else {
} else {
ptail = &tail->next;
}
}
@ -1347,9 +1327,8 @@ active_list_substep_edges(
}
inline static glitter_status_t
apply_nonzero_fill_rule_for_subrow(
struct active_list *active,
struct cell_list *coverages)
apply_nonzero_fill_rule_for_subrow (struct active_list *active,
struct cell_list *coverages)
{
struct edge *edge = active->head;
int winding = 0;
@ -1357,25 +1336,26 @@ apply_nonzero_fill_rule_for_subrow(
int xend;
int status;
cell_list_rewind(coverages);
cell_list_rewind (coverages);
while (NULL != edge) {
xstart = edge->x.quo;
winding = edge->dir;
while (1) {
edge = edge->next;
if (NULL == edge) {
return cell_list_add_unbounded_subspan(
coverages, xstart);
}
if (NULL == edge)
return cell_list_add_unbounded_subspan (coverages, xstart);
winding += edge->dir;
if (0 == winding)
break;
if (0 == winding) {
if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
break;
}
}
xend = edge->x.quo;
status = cell_list_add_subspan(coverages, xstart, xend);
if (status)
status = cell_list_add_subspan (coverages, xstart, xend);
if (unlikely (status))
return status;
edge = edge->next;
@ -1385,29 +1365,33 @@ apply_nonzero_fill_rule_for_subrow(
}
static glitter_status_t
apply_evenodd_fill_rule_for_subrow(
struct active_list *active,
struct cell_list *coverages)
apply_evenodd_fill_rule_for_subrow (struct active_list *active,
struct cell_list *coverages)
{
struct edge *edge = active->head;
int xstart;
int xend;
int status;
cell_list_rewind(coverages);
cell_list_rewind (coverages);
while (NULL != edge) {
xstart = edge->x.quo;
edge = edge->next;
if (NULL == edge) {
return cell_list_add_unbounded_subspan(
coverages, xstart);
while (1) {
edge = edge->next;
if (NULL == edge)
return cell_list_add_unbounded_subspan (coverages, xstart);
if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
break;
edge = edge->next;
}
xend = edge->x.quo;
status = cell_list_add_subspan(coverages, xstart, xend);
if (status)
status = cell_list_add_subspan (coverages, xstart, xend);
if (unlikely (status))
return status;
edge = edge->next;
@ -1417,9 +1401,8 @@ apply_evenodd_fill_rule_for_subrow(
}
static glitter_status_t
apply_nonzero_fill_rule_and_step_edges(
struct active_list *active,
struct cell_list *coverages)
apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
struct cell_list *coverages)
{
struct edge **cursor = &active->head;
struct edge *left_edge;
@ -1431,32 +1414,30 @@ apply_nonzero_fill_rule_and_step_edges(
int winding = left_edge->dir;
left_edge->height_left -= GRID_Y;
if (left_edge->height_left) {
if (left_edge->height_left)
cursor = &left_edge->next;
}
else {
else
*cursor = left_edge->next;
}
while (1) {
right_edge = *cursor;
if (NULL == right_edge) {
return cell_list_render_edge(
coverages, left_edge, +1);
}
if (NULL == right_edge)
return cell_list_render_edge (coverages, left_edge, +1);
right_edge->height_left -= GRID_Y;
if (right_edge->height_left) {
if (right_edge->height_left)
cursor = &right_edge->next;
}
else {
else
*cursor = right_edge->next;
}
winding += right_edge->dir;
if (0 == winding)
break;
if (0 == winding) {
if (right_edge->next == NULL ||
right_edge->next->x.quo != right_edge->x.quo)
{
break;
}
}
right_edge->x.quo += right_edge->dxdy_full.quo;
right_edge->x.rem += right_edge->dxdy_full.rem;
@ -1466,13 +1447,12 @@ apply_nonzero_fill_rule_and_step_edges(
}
}
status = cell_list_render_edge(
coverages, left_edge, +1);
if (status)
status = cell_list_render_edge (coverages, left_edge, +1);
if (unlikely (status))
return status;
status = cell_list_render_edge(
coverages, right_edge, -1);
if (status)
status = cell_list_render_edge (coverages, right_edge, -1);
if (unlikely (status))
return status;
left_edge = *cursor;
@ -1482,9 +1462,8 @@ apply_nonzero_fill_rule_and_step_edges(
}
static glitter_status_t
apply_evenodd_fill_rule_and_step_edges(
struct active_list *active,
struct cell_list *coverages)
apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
struct cell_list *coverages)
{
struct edge **cursor = &active->head;
struct edge *left_edge;
@ -1495,35 +1474,42 @@ apply_evenodd_fill_rule_and_step_edges(
struct edge *right_edge;
left_edge->height_left -= GRID_Y;
if (left_edge->height_left) {
if (left_edge->height_left)
cursor = &left_edge->next;
}
else {
else
*cursor = left_edge->next;
while (1) {
right_edge = *cursor;
if (NULL == right_edge)
return cell_list_render_edge (coverages, left_edge, +1);
right_edge->height_left -= GRID_Y;
if (right_edge->height_left)
cursor = &right_edge->next;
else
*cursor = right_edge->next;
if (right_edge->next == NULL ||
right_edge->next->x.quo != right_edge->x.quo)
{
break;
}
right_edge->x.quo += right_edge->dxdy_full.quo;
right_edge->x.rem += right_edge->dxdy_full.rem;
if (right_edge->x.rem >= 0) {
++right_edge->x.quo;
right_edge->x.rem -= right_edge->dy;
}
}
right_edge = *cursor;
if (NULL == right_edge) {
return cell_list_render_edge(
coverages, left_edge, +1);
}
right_edge->height_left -= GRID_Y;
if (right_edge->height_left) {
cursor = &right_edge->next;
}
else {
*cursor = right_edge->next;
}
status = cell_list_render_edge(
coverages, left_edge, +1);
if (status)
status = cell_list_render_edge (coverages, left_edge, +1);
if (unlikely (status))
return status;
status = cell_list_render_edge(
coverages, right_edge, -1);
if (status)
status = cell_list_render_edge (coverages, right_edge, -1);
if (unlikely (status))
return status;
left_edge = *cursor;
@ -1692,26 +1678,28 @@ glitter_scan_converter_reset(
} while (0)
I glitter_status_t
glitter_scan_converter_add_edge(
glitter_scan_converter_t *converter,
glitter_input_scaled_t x1, glitter_input_scaled_t y1,
glitter_input_scaled_t x2, glitter_input_scaled_t y2,
int dir)
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
const cairo_edge_t *edge)
{
/* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
grid_scaled_y_t sx1, sy1;
grid_scaled_y_t sx2, sy2;
cairo_edge_t e;
INPUT_TO_GRID_Y(y1, sy1);
INPUT_TO_GRID_Y(y2, sy2);
if (sy1 == sy2)
INPUT_TO_GRID_Y (edge->top, e.top);
INPUT_TO_GRID_Y (edge->bottom, e.bottom);
if (e.top == e.bottom)
return GLITTER_STATUS_SUCCESS;
INPUT_TO_GRID_X(x1, sx1);
INPUT_TO_GRID_X(x2, sx2);
/* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
if (e.line.p1.y == e.line.p2.y)
return GLITTER_STATUS_SUCCESS;
return polygon_add_edge(
converter->polygon, sx1, sy1, sx2, sy2, dir);
INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
e.dir = edge->dir;
return polygon_add_edge (converter->polygon, &e);
}
#ifndef GLITTER_BLIT_COVERAGES_BEGIN
@ -1756,58 +1744,54 @@ glitter_scan_converter_render(
/* Determine if we can ignore this row or use the full pixel
* stepper. */
if (GRID_Y == EDGE_Y_BUCKET_HEIGHT
&& !polygon->y_buckets[i])
{
if (!active->head) {
GLITTER_BLIT_COVERAGES_EMPTY(i+ymin_i, xmin_i, xmax_i);
if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
if (! active->head) {
GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, xmin_i, xmax_i);
continue;
}
do_full_step = active_list_can_step_full_row(active);
do_full_step = active_list_can_step_full_row (active);
}
cell_list_reset(coverages);
cell_list_reset (coverages);
if (do_full_step) {
/* Step by a full pixel row's worth. */
if (nonzero_fill) {
status = apply_nonzero_fill_rule_and_step_edges(
active, coverages);
status = apply_nonzero_fill_rule_and_step_edges (active,
coverages);
} else {
status = apply_evenodd_fill_rule_and_step_edges (active,
coverages);
}
else {
status = apply_evenodd_fill_rule_and_step_edges(
active, coverages);
}
}
else {
} else {
/* Subsample this row. */
grid_scaled_y_t suby;
for (suby = 0; suby < GRID_Y; suby++) {
grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
active_list_merge_edges_from_polygon(
active, y, polygon);
active_list_merge_edges_from_polygon (active, y, polygon);
if (nonzero_fill)
status |= apply_nonzero_fill_rule_for_subrow(
active, coverages);
else
status |= apply_evenodd_fill_rule_for_subrow(
active, coverages);
if (nonzero_fill) {
status |= apply_nonzero_fill_rule_for_subrow (active,
coverages);
} else {
status |= apply_evenodd_fill_rule_for_subrow (active,
coverages);
}
active_list_substep_edges(active);
}
}
if (status)
if (unlikely (status))
return status;
GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i);
if (!active->head) {
if (! active->head) {
active->min_height = INT_MAX;
}
else {
} else {
active->min_height -= GRID_Y;
}
}
@ -1860,7 +1844,7 @@ blit_with_span_renderer(
/* Allocate enough spans for the row. */
pool_reset (span_pool);
spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
if (spans == NULL)
if (unlikely (spans == NULL))
return GLITTER_STATUS_NO_MEMORY;
num_spans = 0;
@ -1906,6 +1890,7 @@ blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y)
struct _cairo_tor_scan_converter {
cairo_scan_converter_t base;
glitter_scan_converter_t converter[1];
cairo_fill_rule_t fill_rule;
@ -1918,9 +1903,9 @@ struct _cairo_tor_scan_converter {
typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t;
static void
_cairo_tor_scan_converter_destroy(void *abstract_converter)
_cairo_tor_scan_converter_destroy (void *converter)
{
cairo_tor_scan_converter_t *self = abstract_converter;
cairo_tor_scan_converter_t *self = converter;
if (self == NULL) {
return;
}
@ -1930,69 +1915,70 @@ _cairo_tor_scan_converter_destroy(void *abstract_converter)
}
static cairo_status_t
_cairo_tor_scan_converter_add_edge(
void *abstract_converter,
cairo_fixed_t x1,
cairo_fixed_t y1,
cairo_fixed_t x2,
cairo_fixed_t y2)
_cairo_tor_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon)
{
cairo_tor_scan_converter_t *self = abstract_converter;
cairo_tor_scan_converter_t *self = converter;
cairo_status_t status;
status = glitter_scan_converter_add_edge (
self->converter,
x1, y1, x2, y2, +1);
if (status) {
return _cairo_scan_converter_set_error (self,
_cairo_error (status));
int i;
for (i = 0; i < polygon->num_edges; i++) {
status = glitter_scan_converter_add_edge (self->converter,
&polygon->edges[i]);
if (unlikely (status)) {
return _cairo_scan_converter_set_error (self,
_cairo_error (status));
}
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_tor_scan_converter_generate(
void *abstract_converter,
cairo_span_renderer_t *renderer)
_cairo_tor_scan_converter_generate (void *converter,
cairo_span_renderer_t *renderer)
{
cairo_tor_scan_converter_t *self = abstract_converter;
cairo_status_t status = glitter_scan_converter_render (
self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING,
renderer,
self->span_pool.base);
if (status) {
return _cairo_scan_converter_set_error (self,
_cairo_error (status));
}
cairo_tor_scan_converter_t *self = converter;
cairo_status_t status;
status = glitter_scan_converter_render (self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING,
renderer,
self->span_pool.base);
if (unlikely (status))
return _cairo_scan_converter_set_error (self, _cairo_error (status));
return CAIRO_STATUS_SUCCESS;
}
cairo_scan_converter_t *
_cairo_tor_scan_converter_create(
int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule)
_cairo_tor_scan_converter_create (int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule)
{
cairo_tor_scan_converter_t *self;
cairo_status_t status;
cairo_tor_scan_converter_t *self =
calloc (1, sizeof(struct _cairo_tor_scan_converter));
if (self == NULL)
goto bail_nomem;
self->base.destroy = &_cairo_tor_scan_converter_destroy;
self->base.add_edge = &_cairo_tor_scan_converter_add_edge;
self->base.generate = &_cairo_tor_scan_converter_generate;
self = calloc (1, sizeof(struct _cairo_tor_scan_converter));
if (unlikely (self == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto bail_nomem;
}
self->base.destroy = _cairo_tor_scan_converter_destroy;
self->base.add_polygon = _cairo_tor_scan_converter_add_polygon;
self->base.generate = _cairo_tor_scan_converter_generate;
pool_init (self->span_pool.base,
250 * sizeof(self->span_pool.embedded[0]),
sizeof(self->span_pool.embedded));
_glitter_scan_converter_init (self->converter);
status = glitter_scan_converter_reset (
self->converter, xmin, ymin, xmax, ymax);
if (status != CAIRO_STATUS_SUCCESS)
status = glitter_scan_converter_reset (self->converter,
xmin, ymin, xmax, ymax);
if (unlikely (status))
goto bail;
self->fill_rule = fill_rule;
@ -2002,5 +1988,5 @@ _cairo_tor_scan_converter_create(
bail:
self->base.destroy(&self->base);
bail_nomem:
return _cairo_scan_converter_create_in_error (CAIRO_STATUS_NO_MEMORY);
return _cairo_scan_converter_create_in_error (status);
}

View file

@ -520,5 +520,6 @@ _cairo_toy_font_face_reset_static_data (void)
cairo_toy_font_face_hash_table = NULL;
CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
_cairo_hash_table_destroy (hash_table);
if (hash_table != NULL)
_cairo_hash_table_destroy (hash_table);
}

View file

@ -63,6 +63,7 @@ _cairo_traps_init (cairo_traps_t *traps)
traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
traps->has_limits = FALSE;
traps->has_intersections = FALSE;
}
void
@ -74,14 +75,6 @@ _cairo_traps_limit (cairo_traps_t *traps,
traps->limits = *limits;
}
cairo_bool_t
_cairo_traps_get_limit (cairo_traps_t *traps,
cairo_box_t *limits)
{
*limits = traps->limits;
return traps->has_limits;
}
void
_cairo_traps_clear (cairo_traps_t *traps)
{
@ -90,6 +83,7 @@ _cairo_traps_clear (cairo_traps_t *traps)
traps->maybe_region = 1;
traps->num_traps = 0;
traps->has_intersections = FALSE;
traps->extents.p1.x = traps->extents.p1.y = INT32_MAX;
traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
}
@ -165,6 +159,12 @@ _cairo_traps_grow (cairo_traps_t *traps)
return TRUE;
}
static cairo_fixed_t
_line_compute_intersection_x_for_y (const cairo_line_t *line,
cairo_fixed_t y)
{
return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y);
}
void
_cairo_traps_add_trap (cairo_traps_t *traps,
cairo_fixed_t top, cairo_fixed_t bottom,
@ -257,23 +257,44 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
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;
if (left->p1.x < traps->extents.p1.x) {
cairo_fixed_t x = left->p1.x;
if (top != left->p1.y) {
x = _line_compute_intersection_x_for_y (left, top);
if (x < traps->extents.p1.x)
traps->extents.p1.x = x;
} else
traps->extents.p1.x = x;
}
if (left->p2.x < traps->extents.p1.x) {
cairo_fixed_t x = left->p2.x;
if (bottom != left->p2.y) {
x = _line_compute_intersection_x_for_y (left, bottom);
if (x < traps->extents.p1.x)
traps->extents.p1.x = x;
} else
traps->extents.p1.x = x;
}
if (right->p1.x > traps->extents.p2.x) {
cairo_fixed_t x = right->p1.x;
if (top != right->p1.y) {
x = _line_compute_intersection_x_for_y (right, top);
if (x > traps->extents.p2.x)
traps->extents.p2.x = x;
} else
traps->extents.p2.x = x;
}
if (right->p2.x > traps->extents.p2.x) {
cairo_fixed_t x = right->p2.x;
if (bottom != right->p2.y) {
x = _line_compute_intersection_x_for_y (right, bottom);
if (x > traps->extents.p2.x)
traps->extents.p2.x = x;
} else
traps->extents.p2.x = x;
}
traps->num_traps++;
}
@ -284,9 +305,8 @@ _compare_point_fixed_by_y (const void *av, const void *bv)
const cairo_point_t *a = av, *b = bv;
int ret = a->y - b->y;
if (ret == 0) {
if (ret == 0)
ret = a->x - b->x;
}
return ret;
}

View file

@ -177,13 +177,7 @@ typedef enum _cairo_internal_surface_type {
#define CAIRO_HAS_TEST_NULL_SURFACE 1
#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1
typedef struct _cairo_point {
cairo_fixed_t x;
cairo_fixed_t y;
} cairo_point_t;
typedef struct _cairo_slope
{
typedef struct _cairo_slope {
cairo_fixed_t dx;
cairo_fixed_t dy;
} cairo_slope_t, cairo_distance_t;
@ -244,7 +238,8 @@ typedef enum _cairo_direction {
} cairo_direction_t;
typedef struct _cairo_edge {
cairo_line_t edge;
cairo_line_t line;
int top, bottom;
int dir;
} cairo_edge_t;
@ -255,6 +250,10 @@ typedef struct _cairo_polygon {
cairo_point_t current_point;
cairo_bool_t has_current_point;
cairo_box_t extents;
cairo_box_t limits;
cairo_bool_t has_limits;
int num_edges;
int edges_size;
cairo_edge_t *edges;

View file

@ -51,6 +51,9 @@
#if !HAVE_UINT64_T
cairo_uquorem64_t I
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den);
cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i);
#define _cairo_uint64_to_uint32(a) ((a).lo)
cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b);
@ -90,6 +93,16 @@ int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b);
#else
static inline cairo_uquorem64_t
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
{
cairo_uquorem64_t qr;
qr.quo = num / den;
qr.rem = num % den;
return qr;
}
#define _cairo_uint32_to_uint64(i) ((uint64_t) (i))
#define _cairo_uint64_to_uint32(i) ((uint32_t) (i))
#define _cairo_uint64_add(a,b) ((a) + (b))
@ -147,11 +160,35 @@ int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b);
* a function which returns both for the 'native' type as well
*/
cairo_uquorem64_t I
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den);
static inline cairo_quorem64_t
_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den)
{
int num_neg = _cairo_int64_negative (num);
int den_neg = _cairo_int64_negative (den);
cairo_uquorem64_t uqr;
cairo_quorem64_t qr;
cairo_quorem64_t I
_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den);
if (num_neg)
num = _cairo_int64_negate (num);
if (den_neg)
den = _cairo_int64_negate (den);
uqr = _cairo_uint64_divrem (num, den);
if (num_neg)
qr.rem = _cairo_int64_negate (uqr.rem);
else
qr.rem = uqr.rem;
if (num_neg != den_neg)
qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo);
else
qr.quo = (cairo_int64_t) uqr.quo;
return qr;
}
static inline int32_t
_cairo_int64_32_div (cairo_int64_t num, int32_t den)
{
return num / den;
}
/*
* 128-bit datatypes. Again, provide two implementations in

View file

@ -39,16 +39,6 @@
#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l))
cairo_uquorem64_t
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
{
cairo_uquorem64_t qr;
qr.quo = num / den;
qr.rem = num % den;
return qr;
}
#else
cairo_uint64_t
@ -317,30 +307,6 @@ _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
#endif /* !HAVE_UINT64_T */
cairo_quorem64_t
_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den)
{
int num_neg = _cairo_int64_negative (num);
int den_neg = _cairo_int64_negative (den);
cairo_uquorem64_t uqr;
cairo_quorem64_t qr;
if (num_neg)
num = _cairo_int64_negate (num);
if (den_neg)
den = _cairo_int64_negate (den);
uqr = _cairo_uint64_divrem (num, den);
if (num_neg)
qr.rem = _cairo_int64_negate (uqr.rem);
else
qr.rem = uqr.rem;
if (num_neg != den_neg)
qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo);
else
qr.quo = (cairo_int64_t) uqr.quo;
return qr;
}
#if HAVE_UINT128_T
cairo_uquorem128_t

View file

@ -952,6 +952,7 @@ typedef struct _cairo_traps {
cairo_status_t status;
cairo_box_t extents;
cairo_box_t limits;
unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
@ -962,7 +963,7 @@ typedef struct _cairo_traps {
cairo_trapezoid_t traps_embedded[4];
cairo_bool_t has_limits;
cairo_box_t limits;
cairo_bool_t has_intersections;
} cairo_traps_t;
#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL
@ -1609,13 +1610,36 @@ _cairo_path_fixed_in_fill (const cairo_path_fixed_t *path,
/* cairo-path-fill.c */
cairo_private cairo_status_t
_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_traps_t *traps);
_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
double tolerance,
cairo_polygon_t *polygon);
cairo_private cairo_int_status_t
_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps);
cairo_private cairo_status_t
_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_traps_t *traps);
/* cairo-path-stroke.c */
cairo_private cairo_status_t
_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_polygon_t *polygon);
cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_traps_t *traps);
cairo_private cairo_status_t
_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
@ -1623,6 +1647,22 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
double tolerance,
cairo_traps_t *traps);
cairo_private cairo_status_t
_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
cairo_matrix_t *ctm,
cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_status_t (*add_triangle) (void *closure,
const cairo_point_t triangle[3]),
cairo_status_t (*add_triangle_fan) (void *closure,
const cairo_point_t *midpt,
const cairo_point_t *points,
int npoints),
cairo_status_t (*add_quad) (void *closure,
const cairo_point_t quad[4]),
void *closure);
/* cairo-scaled-font.c */
cairo_private void
@ -2201,45 +2241,21 @@ cairo_private int
_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
const cairo_slope_t *slope);
typedef struct _cairo_pen_stroke_spline {
cairo_pen_t pen;
cairo_spline_t spline;
cairo_polygon_t polygon;
cairo_point_t last_point;
cairo_point_t forward_hull_point;
cairo_point_t backward_hull_point;
int forward_vertex;
int backward_vertex;
} cairo_pen_stroke_spline_t;
cairo_private cairo_int_status_t
_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker,
const cairo_pen_t *pen,
const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d);
cairo_private cairo_status_t
_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t *pen,
double tolerance,
cairo_traps_t *traps);
cairo_private void
_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker);
/* cairo-polygon.c */
cairo_private void
_cairo_polygon_init (cairo_polygon_t *polygon);
cairo_private void
_cairo_polygon_fini (cairo_polygon_t *polygon);
_cairo_polygon_limit (cairo_polygon_t *polygon,
const cairo_box_t *limits);
cairo_private void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2,
int dir);
_cairo_polygon_fini (cairo_polygon_t *polygon);
cairo_private cairo_status_t
_cairo_polygon_add_external_edge (void *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2);
cairo_private void
_cairo_polygon_move_to (cairo_polygon_t *polygon,
@ -2252,7 +2268,7 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon,
cairo_private void
_cairo_polygon_close (cairo_polygon_t *polygon);
#define _cairo_polygon_status(P) (P)->status
#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status
/* cairo-spline.c */
cairo_private cairo_bool_t
@ -2309,6 +2325,9 @@ cairo_private cairo_bool_t
_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix,
int *itx, int *ity);
cairo_private cairo_bool_t
_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
cairo_private cairo_bool_t
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
@ -2330,10 +2349,6 @@ cairo_private void
_cairo_traps_limit (cairo_traps_t *traps,
cairo_box_t *limits);
cairo_private cairo_bool_t
_cairo_traps_get_limit (cairo_traps_t *traps,
cairo_box_t *limits);
cairo_private void
_cairo_traps_init_box (cairo_traps_t *traps,
const cairo_box_t *box);
@ -2372,6 +2387,9 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
const cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule);
cairo_private cairo_status_t
_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps);
cairo_private int
_cairo_traps_contain (const cairo_traps_t *traps,
double x, double y);

View file

@ -4,8 +4,6 @@ include $(top_srcdir)/test/Makefile.sources
SUBDIRS=pdiff .
CLEANFILES += have-similar.*
# Then we have a collection of tests that are only run if certain
# features are compiled into cairo
if HAVE_PTHREAD
@ -147,8 +145,10 @@ REFERENCE_IMAGES = \
bitmap-font.rgb24.ref.png \
caps-joins-alpha.quartz.ref.png \
caps-joins-alpha.ref.png \
caps-joins-alpha.xlib.ref.png \
caps-joins-curve.ps.ref.png \
caps-joins-curve.ref.png \
caps-joins-curve.xlib.ref.png \
caps-joins.ps.ref.png \
caps-joins.ref.png \
caps-sub-paths.ref.png \
@ -156,6 +156,8 @@ REFERENCE_IMAGES = \
caps.ref.png \
clear.argb32.ref.png \
clear.rgb24.ref.png \
clear.pdf.argb32.ref.png \
clear.ps.argb32.ref.png \
clear.svg12.argb32.xfail.png \
clear.svg12.rgb24.xfail.png \
clip-all.ref.png \
@ -237,6 +239,7 @@ REFERENCE_IMAGES = \
clipped-group.ps3.ref.png \
clipped-group.ref.png \
clipped-surface.ref.png \
clipped-trapezoids.ref.png \
close-path-current-point.ps.ref.png \
close-path-current-point.ref.png \
close-path.ps2.ref.png \
@ -275,6 +278,7 @@ REFERENCE_IMAGES = \
dash-curve.ps3.ref.png \
dash-curve.quartz.ref.png \
dash-curve.ref.png \
dash-curve.xlib.ref.png \
dash-infinite-loop.ref.png \
dash-no-dash.ref.png \
dash-offset-negative.ref.png \
@ -302,16 +306,18 @@ REFERENCE_IMAGES = \
degenerate-curve-to.ps.xfail.png \
degenerate-dash.ps.xfail.png \
degenerate-dash.ref.png \
degenerate-dash.xlib.ref.png \
degenerate-path.ps.argb32.xfail.png \
degenerate-path.ps.rgb24.xfail.png \
degenerate-path.quartz.ref.png \
degenerate-path.quartz.rgb24.ref.png \
degenerate-path.ref.png \
degenerate-path.argb32.ref.png \
degenerate-path.rgb24.ref.png \
degenerate-pen.ps2.ref.png \
degenerate-pen.ps3.ref.png \
degenerate-pen.quartz.ref.png \
degenerate-pen.ref.png \
degenerate-pen.xlib.ref.png \
degenerate-rel-curve-to.ref.png \
degenerate-rel-curve-to.ps.xfail.png \
device-offset-fractional.gl.xfail.png \
@ -389,8 +395,10 @@ REFERENCE_IMAGES = \
fill-and-stroke.ps3.rgb24.ref.png \
fill-and-stroke.quartz.ref.png \
fill-and-stroke.quartz.rgb24.ref.png \
fill-and-stroke.ref.png \
fill-and-stroke.argb32.ref.png \
fill-and-stroke.rgb24.ref.png \
fill-and-stroke.xlib.argb32.ref.png \
fill-and-stroke.xlib.rgb24.ref.png \
fill-degenerate-sort-order.ps.argb32.xfail.png \
fill-degenerate-sort-order.ps.rgb24.xfail.png \
fill-degenerate-sort-order.quartz.ref.png \
@ -469,14 +477,12 @@ REFERENCE_IMAGES = \
ft-text-antialias-none.ps3.argb32.ref.png \
ft-text-antialias-none.ref.png \
ft-text-vertical-layout-type1.pdf.ref.png \
ft-text-vertical-layout-type1.ps2.ref.png \
ft-text-vertical-layout-type1.ps3.ref.png \
ft-text-vertical-layout-type1.ps.ref.png \
ft-text-vertical-layout-type1.ref.png \
ft-text-vertical-layout-type1.svg.ref.png \
ft-text-vertical-layout-type1.xlib.ref.png \
ft-text-vertical-layout-type3.pdf.ref.png \
ft-text-vertical-layout-type3.ps2.ref.png \
ft-text-vertical-layout-type3.ps3.ref.png \
ft-text-vertical-layout-type3.ps.ref.png \
ft-text-vertical-layout-type3.ref.png \
ft-text-vertical-layout-type3.svg.ref.png \
ft-text-vertical-layout-type3.xlib.ref.png \
@ -538,9 +544,11 @@ REFERENCE_IMAGES = \
leaky-dashed-rectangle.pdf.ref.png \
leaky-dashed-rectangle.ps.ref.png \
leaky-dashed-rectangle.ref.png \
leaky-dashed-rectangle.xlib.ref.png \
leaky-dashed-stroke.ps2.ref.png \
leaky-dashed-stroke.ps3.ref.png \
leaky-dashed-stroke.ref.png \
leaky-dashed-stroke.xlib.ref.png \
leaky-polygon.ps2.ref.png \
leaky-polygon.ps3.ref.png \
leaky-polygon.ref.png \
@ -613,13 +621,14 @@ REFERENCE_IMAGES = \
miter-precision.ps3.ref.png \
miter-precision.ref.png \
move-to-show-surface.ref.png \
new-sub-path.pdf.argb32.ref.png \
new-sub-path.ps2.argb32.ref.png \
new-sub-path.ps2.rgb24.ref.png \
new-sub-path.ps3.argb32.ref.png \
new-sub-path.ps3.rgb24.ref.png \
new-sub-path.quartz.ref.png \
new-sub-path.quartz.rgb24.ref.png \
new-sub-path.ref.png \
new-sub-path.argb32.ref.png \
new-sub-path.rgb24.ref.png \
nil-surface.ref.png \
nil-surface.rgb24.ref.png \
@ -726,11 +735,13 @@ REFERENCE_IMAGES = \
radial-gradient.pdf.ref.png \
radial-gradient.quartz.ref.png \
radial-gradient.ref.png \
random-intersections.ps2.ref.png \
random-intersections.ps3.ref.png \
random-intersections.quartz.ref.png \
random-intersections.ref.png \
random-intersections.xlib.ref.png \
random-intersections-eo.ps.ref.png \
random-intersections-eo.quartz.ref.png \
random-intersections-eo.ref.png \
random-intersections-eo.xlib.ref.png \
random-intersections-nonzero.ref.png \
random-intersections-nonzero.ps.ref.png \
random-intersections-nonzero.xlib.ref.png \
rectangle-rounding-error.ref.png \
rectilinear-dash.ref.png \
rectilinear-fill.ref.png \
@ -783,16 +794,9 @@ REFERENCE_IMAGES = \
self-copy.ps2.ref.png \
self-copy.ps3.ref.png \
self-copy.ref.png \
self-intersecting.argb32.xfail.png \
self-intersecting.pdf.argb32.xfail.png \
self-intersecting.pdf.rgb24.xfail.png \
self-intersecting.ps.argb32.xfail.png \
self-intersecting.ps.rgb24.xfail.png \
self-intersecting.ps.ref.png \
self-intersecting.ref.png \
self-intersecting.rgb24.ref.png \
self-intersecting.rgb24.xfail.png \
self-intersecting.xlib.argb32.xfail.png \
self-intersecting.xlib.rgb24.xfail.png \
self-intersecting.xlib.ref.png \
set-source.ref.png \
set-source.rgb24.ref.png \
show-glyphs-many.ref.png \
@ -815,6 +819,7 @@ REFERENCE_IMAGES = \
smask-paint.svg.ref.png \
smask-stroke.pdf.xfail.png \
smask-stroke.ref.png \
smask-stroke.xlib.ref.png \
smask-text.pdf.ref.png \
smask-text.ps2.ref.png \
smask-text.ps3.ref.png \
@ -822,8 +827,7 @@ REFERENCE_IMAGES = \
smask-text.svg.ref.png \
smask-text.xlib.ref.png \
smask.pdf.xfail.png \
smask.ps2.ref.png \
smask.ps3.ref.png \
smask.ps.ref.png \
smask.ref.png \
smask.svg.ref.png \
smask.xlib.ref.png \
@ -844,15 +848,16 @@ REFERENCE_IMAGES = \
spline-decomposition.ps.ref.png \
spline-decomposition.ref.png \
spline-decomposition.svg.ref.png \
spline-decomposition.xlib.ref.png \
stroke-ctm-caps.ps2.ref.png \
stroke-ctm-caps.ps3.ref.png \
stroke-ctm-caps.quartz.ref.png \
stroke-ctm-caps.ref.png \
stroke-image.pdf.ref.png \
stroke-image.ps2.ref.png \
stroke-image.ps3.ref.png \
stroke-image.ps.ref.png \
stroke-image.quartz.ref.png \
stroke-image.ref.png \
stroke-image.xlib.ref.png \
surface-pattern-big-scale-down.ref.png \
surface-pattern-big-scale-down.ps.xfail.png \
surface-pattern-scale-down.pdf.ref.png \
@ -900,6 +905,7 @@ REFERENCE_IMAGES = \
text-rotate.quartz.ref.png \
text-rotate.ref.png \
text-rotate.svg.ref.png \
text-rotate.xlib.ref.png \
text-transform.pdf.ref.png \
text-transform.ps2.ref.png \
text-transform.ps3.ref.png \
@ -923,6 +929,7 @@ REFERENCE_IMAGES = \
twin.ps.ref.png \
twin.ref.png \
twin.svg.ref.png \
twin.xlib.ref.png \
unantialiased-shapes.quartz.ref.png \
unantialiased-shapes.ref.png \
unbounded-operator.gl.argb32.xfail.png \
@ -934,7 +941,7 @@ REFERENCE_IMAGES = \
unbounded-operator.quartz.rgb24.ref.png \
unbounded-operator.ref.png \
unbounded-operator.rgb24.ref.png \
unbounded-operator.svg12.argb32.xfail.png \
unbounded-operator.svg12.argb32.ref.png \
unbounded-operator.svg12.rgb24.xfail.png \
unbounded-operator.xlib.rgb24.ref.png \
user-font-mask.pdf.ref.png \
@ -953,8 +960,7 @@ REFERENCE_IMAGES = \
user-font-rescale.ps3.ref.png \
user-font-rescale.ref.png \
user-font-rescale.svg.ref.png \
user-font.ps2.ref.png \
user-font.ps3.ref.png \
user-font.ps.ref.png \
user-font.ref.png \
user-font.svg.ref.png \
user-font.xlib.ref.png \
@ -1132,14 +1138,22 @@ imagediff_LDADD = \
$(top_builddir)/src/libcairo.la
png_flatten_SOURCES = png-flatten.c
png_flatten_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD)
png_flatten_LDADD = $(top_builddir)/src/libcairo.la \
$(CAIRO_LDADD)
if BUILD_ANY2PPM
check_PROGRAMS += any2ppm
any2ppm_CFLAGS = $(AM_CFLAGS) $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS) $(LIBSPECTRE_CFLAGS)
# add LDADD, so poppler/librsvg uses "our" cairo
any2ppm_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
any2ppm_LDADD = $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(CAIROBOILERPLATE_LIBS) $(POPPLER_LIBS) $(LIBRSVG_LIBS) $(LIBSPECTRE_LIBS)
any2ppm_LDADD = \
$(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
$(top_builddir)/src/libcairo.la \
$(CAIRO_LDADD) \
$(CAIROBOILERPLATE_LIBS) \
$(POPPLER_LIBS) \
$(LIBRSVG_LIBS) \
$(LIBSPECTRE_LIBS)
endif
if CAIRO_CAN_TEST_PDF_SURFACE
@ -1147,7 +1161,9 @@ check_PROGRAMS += pdf2png
pdf2png_CFLAGS = $(AM_CFLAGS) $(POPPLER_CFLAGS)
# add LDADD, so poppler uses "our" cairo
pdf2png_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
pdf2png_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(POPPLER_LIBS)
pdf2png_LDADD = $(top_builddir)/src/libcairo.la \
$(CAIRO_LDADD) \
$(POPPLER_LIBS)
endif
if CAIRO_CAN_TEST_SVG_SURFACE
@ -1155,7 +1171,9 @@ check_PROGRAMS += svg2png
svg2png_CFLAGS = $(AM_CFLAGS) $(LIBRSVG_CFLAGS)
# add LDADD, so librsvg uses "our" cairo
svg2png_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
svg2png_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(LIBRSVG_LIBS)
svg2png_LDADD = $(top_builddir)/src/libcairo.la \
$(CAIRO_LDADD) \
$(LIBRSVG_LIBS)
endif
if CAIRO_HAS_SPECTRE
@ -1163,7 +1181,9 @@ check_PROGRAMS += ps2png
ps2png_CFLAGS = $(AM_CFLAGS) $(LIBSPECTRE_CFLAGS)
# add LDADD, so ps2png uses "our" cairo
ps2png_LDFLAGS = $(AM_LDFLAGS) $(CAIRO_TEST_UNDEFINED_LDFLAGS)
ps2png_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(LIBSPECTRE_LIBS)
ps2png_LDADD = $(top_builddir)/src/libcairo.la \
$(CAIRO_LDADD) \
$(LIBSPECTRE_LIBS)
endif
EXTRA_PROGRAMS += $(TESTS)

View file

@ -150,7 +150,8 @@ test_sources = \
png.c \
push-group.c \
radial-gradient.c \
random-intersections.c \
random-intersections-eo.c \
random-intersections-nonzero.c \
rectangle-rounding-error.c \
rectilinear-fill.c \
rectilinear-miter-limit.c \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 B

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

95
test/clipped-trapezoids.c Normal file
View file

@ -0,0 +1,95 @@
/*
* Copyright 2008 Chris Wilson
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* Chris Wilson not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. Chris Wilson makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Chris Wilson <chris@chris-wilson.co.uk>
*/
#include "cairo-test.h"
static cairo_test_status_t
draw (cairo_t *cr, int width, int height)
{
double dash[2] = { 8, 4 };
double radius;
radius = width;
if (height > radius)
radius = height;
/* fill the background using a big circle */
cairo_arc (cr, 0, 0, 4 * radius, 0, 2 * M_PI);
cairo_fill (cr);
/* a rotated square - overlapping the corners */
cairo_save (cr);
cairo_save (cr);
cairo_translate (cr, width/2, height/2);
cairo_rotate (cr, M_PI/4);
cairo_scale (cr, M_SQRT2, M_SQRT2);
cairo_rectangle (cr, -width/2, -height/2, width, height);
cairo_restore (cr);
cairo_set_source_rgba (cr, 0, 1, 0, .5);
cairo_set_line_width (cr, radius/2);
cairo_stroke (cr);
cairo_restore (cr);
/* and put some circles in the corners */
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_new_sub_path (cr);
cairo_arc (cr, 0, 0, radius/4, 0, 2 * M_PI);
cairo_new_sub_path (cr);
cairo_arc (cr, width, 0, radius/4, 0, 2 * M_PI);
cairo_new_sub_path (cr);
cairo_arc (cr, width, height, radius/4, 0, 2 * M_PI);
cairo_new_sub_path (cr);
cairo_arc (cr, 0, height, radius/4, 0, 2 * M_PI);
cairo_fill (cr);
/* a couple of pixel-aligned lines */
cairo_set_source_rgb (cr, 0, 0, 1);
cairo_move_to (cr, width/2, -height);
cairo_rel_line_to (cr, 0, 3*height);
cairo_move_to (cr, -width, height/2);
cairo_rel_line_to (cr, 3*width, 0);
cairo_stroke (cr);
/* a couple of dashed diagonals */
cairo_save (cr);
cairo_set_source_rgb (cr, 1, 0, 0);
cairo_set_dash (cr, dash, 2, 0);
cairo_set_line_width (cr, 4.);
cairo_move_to (cr, -width, -height);
cairo_line_to (cr, width+width, height+height);
cairo_move_to (cr, width+width, -height);
cairo_line_to (cr, -width, height+height);
cairo_stroke (cr);
cairo_restore (cr);
return CAIRO_TEST_SUCCESS;
}
CAIRO_TEST (clipped_trapezoids,
"Tests clipping of trapezoids larger than the surface",
"clip", /* keywords */
NULL, /* requirements */
40, 40,
NULL, draw)

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 B

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 482 B

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 576 B

View file

@ -69,7 +69,7 @@ draw (cairo_t *cr, int width, int height)
return CAIRO_TEST_SUCCESS;
}
CAIRO_TEST (random_intersections,
CAIRO_TEST (random_intersections_eo,
"Tests the tessellator trapezoid generation and intersection computation",
"trap", /* keywords */
NULL, /* requirements */

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View file

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Some files were not shown because too many files have changed in this diff Show more