This is a patch file for "cairo" circa release 1.2.6. This file was obtained originally from: http://david.freetype.org/cairo/cairo-performance-1.patchset This file implements a speed optimization by David Turner. Note: This version of the file contains exactly one change that's incompatible with the 1.2.6 sources; specifically, the change that replaces the misspelled word "ciaro" with "cairo". The contents are unchanged except for these comments. ====================================================================== From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] - changing _cairo_fixed_to_double into a macro From: David Turner Date: 1157061150 +0200 - introducing _cairo_polygon_init_static - introducing _cairo_spline_decompose_direct - performance optimization of the spline flatening algorithm (doesn't change the spline decomposition, simply computes it faster) --- src/cairo-fixed.c | 6 - src/cairo-polygon.c | 11 ++ src/cairo-spline.c | 266 ++++++++++++++++++++++++++++----------------------- src/cairoint.h | 19 +++- 4 files changed, 175 insertions(+), 127 deletions(-) 864ab0b1d051eb4b02f7c4e086ae557cec7fd99c diff --git a/src/cairo-fixed.c b/src/cairo-fixed.c index 604c9e7..8c057b3 100644 --- a/src/cairo-fixed.c +++ b/src/cairo-fixed.c @@ -54,12 +54,6 @@ _cairo_fixed_from_26_6 (uint32_t i) return i << 10; } -double -_cairo_fixed_to_double (cairo_fixed_t f) -{ - return ((double) f) / 65536.0; -} - int _cairo_fixed_is_integer (cairo_fixed_t f) { diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c index dc9d380..60162d5 100644 --- a/src/cairo-polygon.c +++ b/src/cairo-polygon.c @@ -54,6 +54,17 @@ _cairo_polygon_init (cairo_polygon_t *po } void +_cairo_polygon_init_static (cairo_polygon_t *polygon, + int num_edges, + cairo_edge_t *edges) +{ + _cairo_polygon_init (polygon); + + polygon->edges = edges; + polygon->edges_size = num_edges; +} + +void _cairo_polygon_fini (cairo_polygon_t *polygon) { if (polygon->edges_size) { diff --git a/src/cairo-spline.c b/src/cairo-spline.c index 900d3ca..5030703 100644 --- a/src/cairo-spline.c +++ b/src/cairo-spline.c @@ -42,18 +42,6 @@ _cairo_spline_grow_by (cairo_spline_t *s static cairo_status_t _cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point); -static void -_lerp_half (cairo_point_t *a, cairo_point_t *b, cairo_point_t *result); - -static void -_de_casteljau (cairo_spline_t *spline, cairo_spline_t *s1, cairo_spline_t *s2); - -static double -_cairo_spline_error_squared (cairo_spline_t *spline); - -static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_t *spline, double tolerance_squared, cairo_spline_t *result); - cairo_int_status_t _cairo_spline_init (cairo_spline_t *spline, cairo_point_t *a, cairo_point_t *b, @@ -144,142 +132,182 @@ _cairo_spline_add_point (cairo_spline_t return CAIRO_STATUS_SUCCESS; } -static void -_lerp_half (cairo_point_t *a, cairo_point_t *b, cairo_point_t *result) -{ - result->x = a->x + ((b->x - a->x) >> 1); - result->y = a->y + ((b->y - a->y) >> 1); -} - -static void -_de_casteljau (cairo_spline_t *spline, cairo_spline_t *s1, cairo_spline_t *s2) -{ - cairo_point_t ab, bc, cd; - cairo_point_t abbc, bccd; - cairo_point_t final; - - _lerp_half (&spline->a, &spline->b, &ab); - _lerp_half (&spline->b, &spline->c, &bc); - _lerp_half (&spline->c, &spline->d, &cd); - _lerp_half (&ab, &bc, &abbc); - _lerp_half (&bc, &cd, &bccd); - _lerp_half (&abbc, &bccd, &final); - - s1->a = spline->a; - s1->b = ab; - s1->c = abbc; - s1->d = final; - s2->a = final; - s2->b = bccd; - s2->c = cd; - s2->d = spline->d; -} -static double -_PointDistanceSquaredToPoint (cairo_point_t *a, cairo_point_t *b) -{ - double dx = _cairo_fixed_to_double (b->x - a->x); - double dy = _cairo_fixed_to_double (b->y - a->y); - - return dx*dx + dy*dy; -} - -static double -_PointDistanceSquaredToSegment (cairo_point_t *p, cairo_point_t *p1, cairo_point_t *p2) +static int +_cairo_spline_point_proximity (cairo_point_t *a, + cairo_point_t *b, + double tolerance2) +{ + double dx = b->x - a->x; + double dy = b->y - a->y; + + return (dx*dx+dy*dy) < tolerance2*(65536.0*65536.0); +} + +/* this function determines wether a bezier is sufficiently + * 'straight' to be approximated by a simple line segment. + * + * this implementation simply checks that both control points + * are sufficiently near the segment, i.e., + * + * dist(b,ad)^2 < tolerance_squared && + * dist(c,ad)^2 < tolerance_squared + * + * this is a re-implementation of the original _cairo_spline_error_squared + * function that backs-off as soon as possible, and avoids re-computing + * various things for each control point + * + * it is possible to use a much faster algorithm based on + * shifts and adds and some useful properties of cubic beziers, but + * this will slightly change the interpretation of the tolerance + * parameter, and is likely to subtly change the number of + * spline sub-beziers generated, and this would fail the cairo + * test suite. + */ +static int +_cairo_spline_is_straight (cairo_point_t *base, double tolerance2) { double u; - double dx, dy; + double dx, dy, len2; double pdx, pdy; cairo_point_t px; + cairo_point_t *p1, *p2, *p; - /* intersection point (px): - - px = p1 + u(p2 - p1) - (p - px) . (p2 - p1) = 0 - - Thus: + p1 = &base[3]; + p2 = &base[0]; - u = ((p - p1) . (p2 - p1)) / (||(p2 - p1)|| ^ 2); - */ + if ( p1->x == p2->x && p1->y && p2->y ) + return _cairo_spline_point_proximity( &base[1], p1, tolerance2) && + _cairo_spline_point_proximity( &base[2], p2, tolerance2); - dx = _cairo_fixed_to_double (p2->x - p1->x); - dy = _cairo_fixed_to_double (p2->y - p1->y); - - if (dx == 0 && dy == 0) - return _PointDistanceSquaredToPoint (p, p1); + dx = _cairo_fixed_to_double (p2->x - p1->x); + dy = _cairo_fixed_to_double (p2->y - p1->y); + len2 = dx*dx + dy*dy; + /* handle first control point */ + p = &base[1]; pdx = _cairo_fixed_to_double (p->x - p1->x); pdy = _cairo_fixed_to_double (p->y - p1->y); - u = (pdx * dx + pdy * dy) / (dx*dx + dy*dy); + u = (pdx * dx + pdy * dy); - if (u <= 0) - return _PointDistanceSquaredToPoint (p, p1); - else if (u >= 1) - return _PointDistanceSquaredToPoint (p, p2); + if (u <= 0) { + if (!_cairo_spline_point_proximity (p, p1, tolerance2)) + return 0; + } else if (u >= len2) { + if (!_cairo_spline_point_proximity (p, p2, tolerance2)) + return 0; + } else { + u /= len2; - px.x = p1->x + u * (p2->x - p1->x); - px.y = p1->y + u * (p2->y - p1->y); + px.x = p1->x + u * (p2->x - p1->x); + px.y = p1->y + u * (p2->y - p1->y); - return _PointDistanceSquaredToPoint (p, &px); -} + if (!_cairo_spline_point_proximity (p, &px, tolerance2)) + return 0; + } -/* Return an upper bound on the error (squared) that could result from approximating - a spline as a line segment connecting the two endpoints */ -static double -_cairo_spline_error_squared (cairo_spline_t *spline) -{ - double berr, cerr; + /* handle second control point */ + p = &base[2]; + pdx = _cairo_fixed_to_double (p->x - p1->x); + pdy = _cairo_fixed_to_double (p->y - p1->y); - berr = _PointDistanceSquaredToSegment (&spline->b, &spline->a, &spline->d); - cerr = _PointDistanceSquaredToSegment (&spline->c, &spline->a, &spline->d); + u = (pdx * dx + pdy * dy); - if (berr > cerr) - return berr; - else - return cerr; -} + if (u <= 0) { + return _cairo_spline_point_proximity (p, p1, tolerance2); + } else if (u >= len2) { + return _cairo_spline_point_proximity (p, p2, tolerance2); + } else { + u /= len2; -static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_t *spline, double tolerance_squared, cairo_spline_t *result) -{ - cairo_status_t status; - cairo_spline_t s1, s2; + px.x = p1->x + u * (p2->x - p1->x); + px.y = p1->y + u * (p2->y - p1->y); - if (_cairo_spline_error_squared (spline) < tolerance_squared) { - return _cairo_spline_add_point (result, &spline->a); + return _cairo_spline_point_proximity (p, &px, tolerance2); } - - _de_casteljau (spline, &s1, &s2); - - status = _cairo_spline_decompose_into (&s1, tolerance_squared, result); - if (status) - return status; - - status = _cairo_spline_decompose_into (&s2, tolerance_squared, result); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_spline_decompose (cairo_spline_t *spline, double tolerance) -{ - cairo_status_t status; +_cairo_spline_decompose_direct (cairo_spline_t *spline, + double tolerance, + cairo_spline_point_func_t *point_func, + void *point_closure, + cairo_bool_t keep_first) +{ + cairo_point_t stack[64]; + cairo_point_t *base; + cairo_status_t status; + double tolerance_squared = tolerance*tolerance; if (spline->points_size) { - _cairo_spline_fini (spline); + _cairo_spline_fini (spline); } - status = _cairo_spline_decompose_into (spline, tolerance * tolerance, spline); - if (status) - return status; + base = stack; + base[0] = spline->d; + base[1] = spline->c; + base[2] = spline->b; + base[3] = spline->a; + + for (;;) + { + if (base + 7 > stack + 64 || + _cairo_spline_is_straight (base, tolerance_squared)) + { + if (!keep_first) { + keep_first = 1; + } else { + status = (*point_func) (point_closure, &base[3]); + if (status) + goto BAIL; + } + + if (base == stack) + break; + + base -= 3; + } + else + { + /* decasteljau directly in the stack */ + cairo_fixed_t a, b, c, d; + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c ) / 2; + base[5].x = b = ( base[3].x + d ) / 2; + c = ( c + d ) / 2; + base[2].x = a = ( a + c ) / 2; + base[4].x = b = ( b + c ) / 2; + base[3].x = ( a + b ) / 2; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c ) / 2; + base[5].y = b = ( base[3].y + d ) / 2; + c = ( c + d ) / 2; + base[2].y = a = ( a + c ) / 2; + base[4].y = b = ( b + c ) / 2; + base[3].y = ( a + b ) / 2; - status = _cairo_spline_add_point (spline, &spline->d); - if (status) - return status; + base += 3; + } + } - return CAIRO_STATUS_SUCCESS; + status = (*point_func) (point_closure, &spline->d); +BAIL: + return status; +} + + +cairo_status_t +_cairo_spline_decompose (cairo_spline_t *spline, double tolerance) +{ + return _cairo_spline_decompose_direct (spline, tolerance, + (cairo_spline_point_func_t*) &_cairo_spline_add_point, + spline, 1); } diff --git a/src/cairoint.h b/src/cairoint.h index 119ee26..76d52fc 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -356,6 +356,10 @@ typedef struct _cairo_spline { cairo_point_t *points; } cairo_spline_t; +typedef cairo_status_t +(cairo_spline_point_func_t) (void *closure, + cairo_point_t *point); + typedef struct _cairo_pen_vertex { cairo_point_t point; @@ -1126,8 +1130,7 @@ _cairo_fixed_from_double (double d); cairo_private cairo_fixed_t _cairo_fixed_from_26_6 (uint32_t i); -cairo_private double -_cairo_fixed_to_double (cairo_fixed_t f); +#define _cairo_fixed_to_double(f) ((double)(f)/65536.0) cairo_private int _cairo_fixed_is_integer (cairo_fixed_t f); @@ -2082,6 +2085,11 @@ cairo_private void _cairo_polygon_init (cairo_polygon_t *polygon); cairo_private void +_cairo_polygon_init_static (cairo_polygon_t *polygon, + int num_edges, + cairo_edge_t *edges); + +cairo_private void _cairo_polygon_fini (cairo_polygon_t *polygon); cairo_private cairo_status_t @@ -2107,6 +2115,13 @@ _cairo_spline_init (cairo_spline_t *spli cairo_private cairo_status_t _cairo_spline_decompose (cairo_spline_t *spline, double tolerance); +cairo_private cairo_status_t +_cairo_spline_decompose_direct (cairo_spline_t *spline, + double tolerance, + cairo_spline_point_func_t *point_func, + void *point_closure, + cairo_bool_t keep_first); + cairo_private void _cairo_spline_fini (cairo_spline_t *spline); -- 1.1.3 From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] - modification of the path filler to use _cairo_spline_decompose_direct, From: David Turner Date: 1157061846 +0200 in order to avoid un-necessary memory allocations and copies - introducing _cairo_path_fixed_fill_extents, to compute the extents of a path without any tessellation (major speedup here). --- src/cairo-gstate.c | 17 ++----- src/cairo-path-fill.c | 120 ++++++++++++++++++++++++++++++++++++++++++++----- src/cairoint.h | 6 ++ 3 files changed, 118 insertions(+), 25 deletions(-) cbf0e6a3cbd5ed7d60d437fb5129ec68a9b68dd0 diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 2f9079b..543286b 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1085,20 +1085,15 @@ _cairo_gstate_fill_extents (cairo_gstate double *x2, double *y2) { cairo_status_t status; - cairo_traps_t traps; - cairo_box_t extents; + cairo_box_t extents; - _cairo_traps_init (&traps); - - status = _cairo_path_fixed_fill_to_traps (path, - gstate->fill_rule, - gstate->tolerance, - &traps); + status = _cairo_path_fixed_fill_extents (path, + gstate->fill_rule, + gstate->tolerance, + &extents); if (status) goto BAIL; - _cairo_traps_extents (&traps, &extents); - *x1 = _cairo_fixed_to_double (extents.p1.x); *y1 = _cairo_fixed_to_double (extents.p1.y); *x2 = _cairo_fixed_to_double (extents.p2.x); @@ -1108,8 +1103,6 @@ _cairo_gstate_fill_extents (cairo_gstate _cairo_gstate_backend_to_user (gstate, x2, y2); BAIL: - _cairo_traps_fini (&traps); - return status; } diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index 51e067f..d0f0951 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -126,7 +126,6 @@ _cairo_filler_curve_to (void *closure, cairo_point_t *c, cairo_point_t *d) { - int i; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; @@ -137,18 +136,11 @@ _cairo_filler_curve_to (void *closure, if (status == CAIRO_INT_STATUS_DEGENERATE) return CAIRO_STATUS_SUCCESS; - _cairo_spline_decompose (&spline, filler->tolerance); - if (status) - goto CLEANUP_SPLINE; - - for (i = 1; i < spline.num_points; i++) { - status = _cairo_polygon_line_to (polygon, &spline.points[i]); - if (status) - break; - } - - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); + /* avoid un-necessary memory copies */ + status = _cairo_spline_decompose_direct + (&spline, filler->tolerance, + (cairo_spline_point_func_t*) &_cairo_polygon_line_to, + polygon, 0); filler->current_point = *d; @@ -205,3 +197,105 @@ BAIL: return status; } + + +typedef struct +{ + cairo_fixed_t xmin; + cairo_fixed_t ymin; + cairo_fixed_t xmax; + cairo_fixed_t ymax; + + cairo_point_t current_point; + double tolerance; + +} cairo_fillbox_t; + +static cairo_status_t +_cairo_fillbox_move_to (void *closure, cairo_point_t *point) +{ + cairo_fillbox_t *box = closure; + cairo_fixed_t x = point->x; + cairo_fixed_t y = point->y; + + if ( x < box->xmin ) + box->xmin = x; + + if ( x > box->xmax ) + box->xmax = x; + + if ( y < box->ymin ) + box->ymin = y; + + if ( y > box->ymax ) + box->ymax = y; + + box->current_point = point[0]; + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_cairo_fillbox_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) +{ + cairo_fillbox_t *box = closure; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_spline_t spline; + + status = _cairo_spline_init (&spline, &box->current_point, b, c, d); + + if (status == CAIRO_INT_STATUS_DEGENERATE) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_spline_decompose_direct (&spline, box->tolerance, + (cairo_spline_point_func_t*)&_cairo_fillbox_move_to, + box, 0); + _cairo_spline_fini (&spline); + + box->current_point = *d; + + return status; +} + +static cairo_status_t +_cairo_fillbox_close_path (void *closure) +{ + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_fill_extents (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_box_t *extents) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_fillbox_t fillbox; + + fillbox.xmin = fillbox.ymin = +INT_MAX; + fillbox.xmax = fillbox.ymax = -INT_MAX; + fillbox.tolerance = tolerance; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_fillbox_move_to, + _cairo_fillbox_move_to, + _cairo_fillbox_curve_to, + _cairo_fillbox_close_path, + &fillbox); + if (status) + goto BAIL; + + extents->p1.x = fillbox.xmin; + extents->p1.y = fillbox.ymin; + extents->p2.x = fillbox.xmax; + extents->p2.y = fillbox.ymax; + +BAIL: + + return status; +} diff --git a/src/cairoint.h b/src/cairoint.h index 76d52fc..2f6b593 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1589,6 +1589,12 @@ _cairo_path_fixed_fill_to_traps (cairo_p double tolerance, cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_path_fixed_fill_extents (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_box_t *extents); + /* cairo_path_stroke.c */ cairo_private cairo_status_t _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, -- 1.1.3 From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] - modified the path stroker to use _cairo_spline_decompose_direct in order From: David Turner Date: 1157062567 +0200 to avoid un-necessary mallocs and memory copies - introducing _cairo_path_fixed_stroke_extents in order to compute the bounds of a given stroke without tessellation (big speedup) --- src/cairo-gstate.c | 14 +- src/cairo-path-stroke.c | 379 +++++++++++++++++++++++++++++++++++++++-------- src/cairo-pen.c | 5 - src/cairoint.h | 14 ++ 4 files changed, 334 insertions(+), 78 deletions(-) 29e375119b9467928c20958a74c2ce5b5e96e776 diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 543286b..917010d 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1053,17 +1053,15 @@ _cairo_gstate_stroke_extents (cairo_gsta _cairo_traps_init (&traps); - status = _cairo_path_fixed_stroke_to_traps (path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - &traps); + status = _cairo_path_fixed_stroke_extents (path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + &extents); if (status) goto BAIL; - _cairo_traps_extents (&traps, &extents); - *x1 = _cairo_fixed_to_double (extents.p1.x); *y1 = _cairo_fixed_to_double (extents.p1.y); *x2 = _cairo_fixed_to_double (extents.p2.x); diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index 792fbfa..c3914b8 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -36,6 +36,16 @@ #include "cairoint.h" +typedef cairo_status_t +(cairo_stroker_tri_func_t)(void* closure, + cairo_point_t *tri); +typedef cairo_status_t +(cairo_stroker_quad_func_t)(void* closure, + cairo_point_t *points); +typedef cairo_status_t +(cairo_stroker_polygon_func_t)(void* closure, + cairo_polygon_t *polygon); + typedef struct cairo_stroker { cairo_stroke_style_t *style; @@ -62,6 +72,11 @@ typedef struct cairo_stroker { unsigned int dash_index; int dash_on; double dash_remain; + void *func_closure; + cairo_stroker_tri_func_t *func_tri; + cairo_stroker_quad_func_t *func_quad; + cairo_stroker_polygon_func_t *func_polygon; + } cairo_stroker_t; /* private functions */ @@ -244,8 +259,11 @@ _cairo_stroker_join (cairo_stroker_t *st while (i != stop) { tri[2] = in->point; _translate_point (&tri[2], &pen->vertices[i].point); - _cairo_traps_tessellate_triangle (stroker->traps, tri); - tri[1] = tri[2]; + status = (*stroker->func_tri)(stroker->func_closure,tri); + if (status) + return status; + + tri[1] = tri[2]; i += step; if (i < 0) i = pen->num_vertices - 1; @@ -255,7 +273,7 @@ _cairo_stroker_join (cairo_stroker_t *st tri[2] = *outpt; - return _cairo_traps_tessellate_triangle (stroker->traps, tri); + return (*stroker->func_tri)(stroker->func_closure, tri); } case CAIRO_LINE_JOIN_MITER: default: { @@ -296,7 +314,6 @@ _cairo_stroker_join (cairo_stroker_t *st double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; - cairo_polygon_t polygon; cairo_point_t outer; /* @@ -339,17 +356,16 @@ _cairo_stroker_join (cairo_stroker_t *st */ outer.x = _cairo_fixed_from_double (mx); outer.y = _cairo_fixed_from_double (my); - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &in->point); - _cairo_polygon_line_to (&polygon, inpt); - _cairo_polygon_line_to (&polygon, &outer); - _cairo_polygon_line_to (&polygon, outpt); - _cairo_polygon_close (&polygon); - status = _cairo_traps_tessellate_polygon (stroker->traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - _cairo_polygon_fini (&polygon); + { + cairo_point_t p[4]; + + p[0] = in->point; + p[1] = inpt[0]; + p[2] = outer; + p[3] = outpt[0]; + status = (*stroker->func_quad) (stroker->func_closure, p); + } return status; } /* fall through ... */ @@ -360,7 +376,7 @@ _cairo_stroker_join (cairo_stroker_t *st tri[1] = *inpt; tri[2] = *outpt; - return _cairo_traps_tessellate_triangle (stroker->traps, tri); + return (*stroker->func_tri) (stroker->func_closure, tri); } } } @@ -392,18 +408,20 @@ _cairo_stroker_add_cap (cairo_stroker_t for (i=start; i != stop; i = (i+1) % pen->num_vertices) { tri[2] = f->point; _translate_point (&tri[2], &pen->vertices[i].point); - _cairo_traps_tessellate_triangle (stroker->traps, tri); - tri[1] = tri[2]; + status = (*stroker->func_tri) (stroker->func_closure, tri); + if (status) + return status; + + tri[1] = tri[2]; } tri[2] = f->ccw; - return _cairo_traps_tessellate_triangle (stroker->traps, tri); + return (*stroker->func_tri) (stroker->func_closure, tri); } case CAIRO_LINE_CAP_SQUARE: { double dx, dy; cairo_slope_t fvector; cairo_point_t occw, ocw; - cairo_polygon_t polygon; dx = f->usr_vector.x; dy = f->usr_vector.y; @@ -417,16 +435,16 @@ _cairo_stroker_add_cap (cairo_stroker_t ocw.x = f->cw.x + fvector.dx; ocw.y = f->cw.y + fvector.dy; - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &f->cw); - _cairo_polygon_line_to (&polygon, &ocw); - _cairo_polygon_line_to (&polygon, &occw); - _cairo_polygon_line_to (&polygon, &f->ccw); - _cairo_polygon_close (&polygon); + { + cairo_point_t p[4]; - status = _cairo_traps_tessellate_polygon (stroker->traps, &polygon, CAIRO_FILL_RULE_WINDING); - _cairo_polygon_fini (&polygon); + p[0] = f->cw; + p[1] = ocw; + p[2] = occw; + p[3] = f->ccw; + status = (*stroker->func_quad) (stroker->func_closure, p); + } return status; } case CAIRO_LINE_CAP_BUTT: @@ -574,7 +592,6 @@ _cairo_stroker_add_sub_edge (cairo_strok cairo_stroke_face_t *end) { cairo_status_t status; - cairo_polygon_t polygon; _compute_face (p1, slope, stroker, start); @@ -586,28 +603,16 @@ _cairo_stroker_add_sub_edge (cairo_strok if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; - /* XXX: I should really check the return value of the - move_to/line_to functions here to catch out of memory - conditions. But since that would be ugly, I'd prefer to add a - status flag to the polygon object that I could check only once - at then end of this sequence, (like we do with cairo_t - already). */ - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &start->cw); - _cairo_polygon_line_to (&polygon, &start->ccw); - _cairo_polygon_line_to (&polygon, &end->ccw); - _cairo_polygon_line_to (&polygon, &end->cw); - _cairo_polygon_close (&polygon); + { + cairo_point_t p[4]; - /* XXX: We can't use tessellate_rectangle as the matrix may have - skewed this into a non-rectangular shape. Perhaps it would be - worth checking the matrix for skew so that the common case - could use the faster tessellate_rectangle rather than - tessellate_polygon? */ - status = _cairo_traps_tessellate_polygon (stroker->traps, - &polygon, CAIRO_FILL_RULE_WINDING); + p[0] = start->cw; + p[1] = start->ccw; + p[2] = end->ccw; + p[3] = end->cw; - _cairo_polygon_fini (&polygon); + status = (*stroker->func_quad) (stroker->func_closure, p); + } return status; } @@ -802,6 +807,47 @@ _cairo_stroker_line_to_dashed (void *clo return status; } +/* the following is a slightly modified copy of _cairo_pen_stroke_spline */ + +/* Compute outline of a given spline using the pen. + The trapezoids needed to fill that outline will be added to traps +*/ +static cairo_status_t +_cairo_stroker_stroke_spline (cairo_stroker_t *stroker, + cairo_pen_t *pen, + cairo_spline_t *spline, + double tolerance) +{ + cairo_status_t status; + cairo_polygon_t polygon; + + /* If the line width is so small that the pen is reduced to a + single point, then we have nothing to do. */ + if (pen->num_vertices <= 1) + return CAIRO_STATUS_SUCCESS; + + _cairo_polygon_init (&polygon); + + status = _cairo_spline_decompose (spline, tolerance); + if (status) + goto BAIL; + + status = _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_FORWARD, &polygon); + if (status) + goto BAIL; + + status = _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_REVERSE, &polygon); + if (status) + goto BAIL; + + _cairo_polygon_close (&polygon); + status = (*stroker->func_polygon) (stroker->func_closure, &polygon); + +BAIL: + _cairo_polygon_fini (&polygon); + return status; +} + static cairo_status_t _cairo_stroker_curve_to (void *closure, cairo_point_t *b, @@ -857,7 +903,7 @@ _cairo_stroker_curve_to (void *closure, if (status) goto CLEANUP_PEN; - status = _cairo_pen_stroke_spline (&pen, &spline, stroker->tolerance, stroker->traps); + status = _cairo_stroker_stroke_spline (stroker, &pen, &spline, stroker->tolerance); if (status) goto CLEANUP_PEN; @@ -900,7 +946,6 @@ _cairo_stroker_curve_to_dashed (void *cl cairo_spline_t spline; cairo_point_t *a = &stroker->current_point; cairo_line_join_t line_join_save; - int i; status = _cairo_spline_init (&spline, a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) @@ -909,31 +954,30 @@ _cairo_stroker_curve_to_dashed (void *cl /* 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) - goto CLEANUP_SPLINE; + goto BAIL; /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->style->line_join; stroker->style->line_join = CAIRO_LINE_JOIN_ROUND; - status = _cairo_spline_decompose (&spline, stroker->tolerance); - if (status) - goto CLEANUP_GSTATE; - - for (i = 1; i < spline.num_points; i++) { - if (stroker->dashed) - status = _cairo_stroker_line_to_dashed (stroker, &spline.points[i]); - else - status = _cairo_stroker_line_to (stroker, &spline.points[i]); - if (status) - break; + if (stroker->dashed) + { + status = _cairo_spline_decompose_direct + (&spline, stroker->tolerance, + (cairo_spline_point_func_t*) &_cairo_stroker_line_to_dashed, + stroker, 0); + } + else + { + status = _cairo_spline_decompose_direct + (&spline, stroker->tolerance, + (cairo_spline_point_func_t*) &_cairo_stroker_line_to, + stroker, 0); } - - CLEANUP_GSTATE: stroker->style->line_join = line_join_save; - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); + BAIL: return status; } @@ -968,6 +1012,42 @@ _cairo_stroker_close_path (void *closure return CAIRO_STATUS_SUCCESS; } + +static cairo_status_t +_cairo_stroker_tessellate_quad (void* closure, + cairo_point_t *points) +{ + cairo_polygon_t polygon; + cairo_edge_t polygon_edges[4]; + cairo_traps_t *traps = closure; + cairo_status_t status; + + _cairo_polygon_init_static (&polygon, 4, polygon_edges); + + /* the following can't fail, because we allocated enough + * space for the edges array + */ + _cairo_polygon_move_to (&polygon, &points[0]); + _cairo_polygon_line_to (&polygon, &points[1]); + _cairo_polygon_line_to (&polygon, &points[2]); + _cairo_polygon_line_to (&polygon, &points[3]); + _cairo_polygon_close (&polygon); + + status = _cairo_traps_tessellate_polygon (traps, + &polygon, + CAIRO_FILL_RULE_WINDING); + return status; +} + +static cairo_status_t +_cairo_stroker_tessellate_polygon (void* closure, + cairo_polygon_t *polygon) +{ + cairo_traps_t *traps = closure; + + return _cairo_traps_tessellate_polygon (traps, polygon, CAIRO_FILL_RULE_WINDING); +} + cairo_status_t _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, @@ -983,6 +1063,11 @@ _cairo_path_fixed_stroke_to_traps (cairo ctm, ctm_inverse, tolerance, traps); + stroker.func_closure = traps; + stroker.func_tri = (cairo_stroker_tri_func_t*) &_cairo_traps_tessellate_triangle; + stroker.func_quad = &_cairo_stroker_tessellate_quad; + stroker.func_polygon = &_cairo_stroker_tessellate_polygon; + if (stroker.style->dash) status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, @@ -1009,3 +1094,165 @@ BAIL: return status; } + + +typedef struct +{ + cairo_fixed_t xmin; + cairo_fixed_t ymin; + cairo_fixed_t xmax; + cairo_fixed_t ymax; + +} cairo_stroker_box_t; + +static cairo_status_t +_cairo_stroker_extents_triangle (void *closure, + cairo_point_t *tri) +{ + cairo_stroker_box_t *box = closure; + int nn; + + for ( nn = 0; nn < 3; nn++ ) { + cairo_fixed_t x, y; + + x = tri[nn].x; + y = tri[nn].y; + + if ( x < box->xmin ) + box->xmin = x; + if ( x > box->xmax ) + box->xmax = x; + + if ( y < box->ymin ) + box->ymin = y; + else if ( y > box->ymax ) + box->ymax = y; + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_extents_quad (void *closure, + cairo_point_t *quad) +{ + cairo_stroker_box_t *box = closure; + int nn; + + for ( nn = 0; nn < 4; nn++ ) { + cairo_fixed_t x, y; + + x = quad[nn].x; + y = quad[nn].y; + + if ( x < box->xmin ) + box->xmin = x; + if ( x > box->xmax ) + box->xmax = x; + + if ( y < box->ymin ) + box->ymin = y; + else if ( y > box->ymax ) + box->ymax = y; + } + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_cairo_stroker_extents_polygon (void *closure, + cairo_polygon_t *polygon) +{ + cairo_stroker_box_t *box = closure; + int nn; + + for (nn=0; nn < polygon->num_edges; nn++) + { + cairo_line_t *line = &polygon->edges[nn].edge; + cairo_fixed_t x, y; + + x = line->p1.x; + y = line->p1.y; + + if ( x < box->xmin ) + box->xmin = x; + if ( x > box->xmax ) + box->xmax = x; + + if ( y < box->ymin ) + box->ymin = y; + else if ( y > box->ymax ) + box->ymax = y; + + x = line->p2.x; + y = line->p2.y; + + if ( x < box->xmin ) + box->xmin = x; + if ( x > box->xmax ) + box->xmax = x; + + if ( y < box->ymin ) + box->ymin = y; + else if ( y > box->ymax ) + box->ymax = y; + } + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_stroke_extents (cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_box_t *extents) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_stroker_t stroker; + cairo_stroker_box_t box; + + _cairo_stroker_init (&stroker, stroke_style, + ctm, ctm_inverse, tolerance, + NULL); + + box.xmin = box.ymin = +INT_MAX; + box.xmax = box.ymax = -INT_MAX; + + stroker.func_closure = &box; + stroker.func_tri = &_cairo_stroker_extents_triangle; + stroker.func_quad = &_cairo_stroker_extents_quad; + stroker.func_polygon = &_cairo_stroker_extents_polygon; + + if (stroker.style->dash) + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to_dashed, + _cairo_stroker_line_to_dashed, + _cairo_stroker_curve_to_dashed, + _cairo_stroker_close_path, + &stroker); + else + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + if (status) + goto BAIL; + + status = _cairo_stroker_add_caps (&stroker); + if (status) + goto BAIL; + + extents->p1.x = box.xmin; + extents->p1.y = box.ymin; + extents->p2.x = box.xmax; + extents->p2.y = box.ymax; + +BAIL: + _cairo_stroker_fini (&stroker); + + return status; +} diff --git a/src/cairo-pen.c b/src/cairo-pen.c index 87de9a4..ad6cbc4 100644 --- a/src/cairo-pen.c +++ b/src/cairo-pen.c @@ -42,9 +42,6 @@ _cairo_pen_vertices_needed (double toler static void _cairo_pen_compute_slopes (cairo_pen_t *pen); -static cairo_status_t -_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon); - cairo_status_t _cairo_pen_init_empty (cairo_pen_t *pen) { @@ -348,7 +345,7 @@ _cairo_pen_find_active_ccw_vertex_index return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +cairo_status_t _cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, diff --git a/src/cairoint.h b/src/cairoint.h index 2f6b593..27d775f 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1604,6 +1604,14 @@ _cairo_path_fixed_stroke_to_traps (cairo double tolerance, cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_path_fixed_stroke_extents (cairo_path_fixed_t *path, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_box_t *extents); + /* cairo-scaled-font.c */ cairo_private cairo_status_t @@ -2086,6 +2094,12 @@ _cairo_pen_stroke_spline (cairo_pen_t *p double tolerance, cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_pen_stroke_spline_half (cairo_pen_t *pen, + cairo_spline_t *spline, + cairo_direction_t dir, + cairo_polygon_t *polygon); + /* cairo_polygon.c */ cairo_private void _cairo_polygon_init (cairo_polygon_t *polygon); -- 1.1.3 From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] - speeding up tessellator through inline assembly (doesn't fix the From: David Turner Date: 1157065187 +0200 algorihtm) - other uses of _cairo_spline_decompose_direct --- src/cairo-path-data.c | 20 +++------ src/cairo-traps.c | 110 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 26 deletions(-) f7784315ec16ee586e1f3f760e325b360106570e diff --git a/src/cairo-path-data.c b/src/cairo-path-data.c index cd15192..b45776d 100644 --- a/src/cairo-path-data.c +++ b/src/cairo-path-data.c @@ -94,7 +94,6 @@ _cpdc_curve_to_flatten (void *clos cpdc_t *cpdc = closure; cairo_status_t status; cairo_spline_t spline; - int i; cairo_point_t *p0 = &cpdc->current_point; @@ -102,19 +101,17 @@ _cpdc_curve_to_flatten (void *clos if (status == CAIRO_INT_STATUS_DEGENERATE) return CAIRO_STATUS_SUCCESS; - status = _cairo_spline_decompose (&spline, cpdc->tolerance); + status = _cairo_spline_decompose_direct (&spline, cpdc->tolerance, + (cairo_spline_point_func_t*) &_cpdc_line_to, + cpdc, 0); if (status) - goto out; - - for (i=1; i < spline.num_points; i++) - _cpdc_line_to (cpdc, &spline.points[i]); + goto out; cpdc->current_point = *p3; status = CAIRO_STATUS_SUCCESS; out: - _cairo_spline_fini (&spline); return status; } @@ -266,7 +263,6 @@ _cpdp_curve_to_flatten (void *clos cpdp_t *cpdp = closure; cairo_status_t status; cairo_spline_t spline; - int i; cairo_point_t *p0 = &cpdp->current_point; @@ -274,19 +270,17 @@ _cpdp_curve_to_flatten (void *clos if (status == CAIRO_INT_STATUS_DEGENERATE) return CAIRO_STATUS_SUCCESS; - status = _cairo_spline_decompose (&spline, cpdp->gstate->tolerance); + status = _cairo_spline_decompose_direct (&spline, cpdp->gstate->tolerance, + (cairo_spline_point_func_t*) &_cpdp_line_to, + cpdp, 0); if (status) goto out; - for (i=1; i < spline.num_points; i++) - _cpdp_line_to (cpdp, &spline.points[i]); - cpdp->current_point = *p3; status = CAIRO_STATUS_SUCCESS; out: - _cairo_spline_fini (&spline); return status; } diff --git a/src/cairo-traps.c b/src/cairo-traps.c index 107276c..a88d8f7 100644 --- a/src/cairo-traps.c +++ b/src/cairo-traps.c @@ -60,12 +60,106 @@ _compare_cairo_edge_by_top (const void * static int _compare_cairo_edge_by_slope (const void *av, const void *bv); -static cairo_fixed_16_16_t -_compute_x (cairo_line_t *line, cairo_fixed_t y); - static int _line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret); +#if defined(__GNUC__) && defined(i386) +/* the following function computes dx*ey/dy with + * 64-bits intermediate accuracy. the inline assembly + * provided is much faster than using GCC's 64-bit integer + * types, which always tries to divide two 64-bit numbers, + * which is slower than the 64-by-32 division we perform + * here. + * + * assumes that dy > 0 + */ +static cairo_fixed_16_16_t +_compute_x_helper (cairo_fixed_16_16_t dx, + cairo_fixed_16_16_t ey, + cairo_fixed_16_16_t dy) +{ +#if 0 + return (cairo_fixed_32_32_t)dx*ey / dy; +#else + cairo_fixed_16_16_t ex; + + __asm__ ( + /* compute ey*dx in edx:eax */ + "imul %%edx\n" + + /* check for overflows and underflows */ + /* separate the case positive and negative cases */ + "test %%edx, %%edx\n" + "js Negative\n" + "cmpl %%ecx, %%edx\n" + "jae Overflow\n" + "divl %%ecx\n" /* note: an _unsigned_ division */ + "testl %%eax, %%eax\n" /* its result must fit in 31 bits */ + "jns Next\n" + + "Overflow:\n" + "movl $0x7fffffff, %%eax\n" + "jmp Next\n" + + "Negative:\n" + "negl %%eax\n" /* negate edx:eax */ + "adcl $0, %%edx\n" + "negl %%edx\n" + + "cmpl %%ecx, %%edx\n" + "jae Underflow\n" + "divl %%ecx\n" /* note: an _unsigned_ division */ + "testl %%eax, %%eax\n" + "js Underflow\n" + "negl %%eax\n" + "jmp Next\n" + + "Underflow:\n" + "movl $0x80000000, %%eax\n" + + "Next:\n" + + : "=a"(ex) + : "d"(dx), "a"(ey), "c"(dy) + ); + return ex; +#endif +} + +static __inline__ cairo_fixed_16_16_t +_compute_x (cairo_line_t *line, cairo_fixed_t y) +{ + cairo_fixed_16_16_t dx = line->p2.x - line->p1.x; + cairo_fixed_16_16_t ey = y - line->p1.y; + cairo_fixed_16_16_t dy = line->p2.y - line->p1.y; + cairo_fixed_16_16_t ex; + + if (ey < dy && ey > -dy) { + __asm__ __volatile__ ( + "imul %%edx\n" + "idiv %%ecx\n" + : "=a" (ex) + : "a"(dx), "d"(ey), "c"(dy) + ); + return line->p1.x + ex; + } + else { + return line->p1.x + _compute_x_helper(dx,ey,dy); + } +} +#else /* other platforms */ +static __inline__ cairo_fixed_16_16_t +_compute_x (cairo_line_t *line, cairo_fixed_t y) +{ + cairo_fixed_16_16_t dx = line->p2.x - line->p1.x; + cairo_fixed_32_32_t ex = (cairo_fixed_48_16_t) (y - line->p1.y) * (cairo_fixed_48_16_t) dx; + cairo_fixed_16_16_t dy = line->p2.y - line->p1.y; + + return line->p1.x + (ex / dy); +} +#endif + + void _cairo_traps_init (cairo_traps_t *traps) { @@ -596,16 +690,6 @@ _line_segs_intersect_ceil (cairo_line_t } #endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ -static cairo_fixed_16_16_t -_compute_x (cairo_line_t *line, cairo_fixed_t y) -{ - cairo_fixed_16_16_t dx = line->p2.x - line->p1.x; - cairo_fixed_32_32_t ex = (cairo_fixed_48_16_t) (y - line->p1.y) * (cairo_fixed_48_16_t) dx; - cairo_fixed_16_16_t dy = line->p2.y - line->p1.y; - - return line->p1.x + (ex / dy); -} - #if ! CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE static double _compute_inverse_slope (cairo_line_t *l) -- 1.1.3 From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] optimize the gradient computations - WARNING: this breaks the test suite !! From: David Turner Date: 1157067679 +0200 --- pixman/src/fbcompose.c | 458 ++++++++++++++++++++++++++++++++++-------------- 1 files changed, 320 insertions(+), 138 deletions(-) 1307a4b791977ede80717fc2e70e6abe8993927f diff --git a/pixman/src/fbcompose.c b/pixman/src/fbcompose.c index 4958241..ec9a515 100644 --- a/pixman/src/fbcompose.c +++ b/pixman/src/fbcompose.c @@ -2731,78 +2731,242 @@ static void fbFetch(PicturePtr pict, int #define DIV(a,b) ((((a) < 0) == ((b) < 0)) ? (a) / (b) :\ ((a) - (b) + 1 - (((b) < 0) << 1)) / (b)) -static CARD32 -xRenderColorMultToCard32 (pixman_color_t *c) +typedef struct +{ + CARD32 left_ag; + CARD32 left_rb; + CARD32 right_ag; + CARD32 right_rb; + int32_t left_x; + int32_t right_x; + int32_t width_x; + int32_t stepper; + + pixman_gradient_stop_t *stops; + int num_stops; + unsigned int spread; + +} GradientWalker; + +static void +_gradient_walker_init (GradientWalker *walker, + SourcePictPtr pGradient, + unsigned int spread) { - return - ((((uint32_t) c->red * c->alpha) >> 24) << 16) | - ((((uint32_t) c->green * c->alpha) >> 24) << 8) | - ((((uint32_t) c->blue * c->alpha) >> 24) << 0) | - ((((uint32_t) c->alpha ) >> 8) << 24); + walker->num_stops = pGradient->gradient.nstops; + walker->stops = pGradient->gradient.stops; + walker->left_x = 0; + walker->right_x = 0x10000; + walker->width_x = 0; /* will force a reset */ + walker->stepper = 0; + walker->left_ag = 0; + walker->left_rb = 0; + walker->right_ag = 0; + walker->right_rb = 0; + walker->spread = spread; } -static CARD32 gradientPixel(const SourcePictPtr pGradient, xFixed_48_16 pos, unsigned int spread) +static void +_gradient_walker_reset (GradientWalker *walker, + xFixed_32_32 pos) { - int ipos = (pos * pGradient->gradient.stopRange - 1) >> 16; + int32_t x, left_x, right_x; + pixman_color_t *left_c, *right_c; + int n, count = walker->num_stops; + pixman_gradient_stop_t *stops = walker->stops; + + static const pixman_color_t transparent_black = { 0, 0, 0, 0 }; + + switch (walker->spread) + { + case RepeatNormal: + x = (int32_t)pos & 0xFFFF; + for (n = 0; n < count; n++) + if (x < stops[n].x) + break; + if (n == 0) { + left_x = stops[count-1].x - 0x10000; + left_c = &stops[count-1].color; + } else { + left_x = stops[n-1].x; + left_c = &stops[n-1].color; + } + + if (n == count) { + right_x = stops[0].x + 0x10000; + right_c = &stops[0].color; + } else { + right_x = stops[n].x; + right_c = &stops[n].color; + } + left_x += (pos - x); + right_x += (pos - x); + break; + + case RepeatPad: + for (n = 0; n < count; n++) + if (pos < stops[n].x) + break; + + if (n == 0) { + left_x = INT_MIN; + left_c = &stops[0].color; + } else { + left_x = stops[n-1].x; + left_c = &stops[n-1].color; + } + + if (n == count) { + right_x = INT_MAX; + right_c = &stops[n-1].color; + } else { + right_x = stops[n].x; + right_c = &stops[n].color; + } + break; + + case RepeatReflect: + x = (int32_t)pos & 0xFFFF; + if ((int32_t)pos & 0x10000) + x = 0x10000 - x; + for (n = 0; n < count; n++) + if (x < stops[n].x) + break; + + if (n == 0) { + left_x = -stops[0].x; + left_c = &stops[0].color; + } else { + left_x = stops[n-1].x; + left_c = &stops[n-1].color; + } + + if (n == count) { + right_x = 0x20000 - stops[n-1].x; + right_c = &stops[n-1].color; + } else { + right_x = stops[n].x; + right_c = &stops[n].color; + } + + if ((int32_t)pos & 0x10000) { + pixman_color_t *tmp_c; + int32_t tmp_x; + + tmp_x = 0x20000 - right_x; + right_x = 0x20000 - left_x; + left_x = tmp_x; + + tmp_c = right_c; + right_c = left_c; + left_c = tmp_c; + } + left_x += (pos - x); + right_x += (pos - x); + break; + + default: /* RepeatNone */ + for (n = 0; n < count; n++) + if (pos < stops[n].x) + break; + + if (n == 0) + { + left_x = INT_MIN; + right_x = stops[0].x; + left_c = right_c = (pixman_color_t*) &transparent_black; + } + else if (n == count) + { + left_x = stops[n-1].x; + right_x = INT_MAX; + left_c = right_c = (pixman_color_t*) &transparent_black; + } + else + { + left_x = stops[n-1].x; + right_x = stops[n].x; + left_c = &stops[n-1].color; + right_c = &stops[n].color; + } + } + + walker->left_x = left_x; + walker->right_x = right_x; + walker->width_x = right_x - left_x; + walker->left_ag = ((left_c->alpha >> 8) << 16) | (left_c->green >> 8); + walker->left_rb = ((left_c->red & 0xff00) << 8) | (left_c->blue >> 8); + walker->right_ag = ((right_c->alpha >> 8) << 16) | (right_c->green >> 8); + walker->right_rb = ((right_c->red & 0xff00) << 8) | (right_c->blue >> 8); + + if ( walker->width_x == 0 || + ( walker->left_ag == walker->right_ag && + walker->left_rb == walker->right_rb ) ) + { + walker->width_x = 1; + walker->stepper = 0; + } + else + { + walker->stepper = ((1 << 24) + walker->width_x/2)/walker->width_x; + } +} - /* calculate the actual offset. */ - if (ipos < 0 || ipos >= pGradient->gradient.stopRange) - { - if (pGradient->type == SourcePictTypeConical || spread == RepeatNormal) - { - ipos = ipos % pGradient->gradient.stopRange; - ipos = ipos < 0 ? pGradient->gradient.stopRange + ipos : ipos; +#define GRADIENT_WALKER_NEED_RESET(w,x) \ + ( (x) < (w)->left_x || (x) - (w)->left_x >= (w)->width_x ) - } - else if (spread == RepeatReflect) - { - const int limit = pGradient->gradient.stopRange * 2 - 1; +/* the following assumes that GRADIENT_WALKER_NEED_RESET(w,x) is FALSE */ +static CARD32 +_gradient_walker_pixel (GradientWalker *walker, + xFixed_32_32 x) +{ + int dist, idist; + uint32_t t1, t2, a, color; - ipos = ipos % limit; - ipos = ipos < 0 ? limit + ipos : ipos; - ipos = ipos >= pGradient->gradient.stopRange ? limit - ipos : ipos; + if (GRADIENT_WALKER_NEED_RESET (walker, x)) + _gradient_walker_reset (walker, x); - } - else if (spread == RepeatPad) - { - if (ipos < 0) - ipos = 0; - else - ipos = pGradient->gradient.stopRange - 1; - } - else /* RepeatNone */ - { - return 0; - } - } + dist = ((int)(x - walker->left_x)*walker->stepper) >> 16; + idist = 256 - dist; - if (pGradient->gradient.colorTableSize) - { - return pGradient->gradient.colorTable[ipos]; - } - else - { - int i; + /* combined INTERPOLATE and premultiply */ + t1 = walker->left_rb*idist + walker->right_rb*dist; + t1 = (t1 >> 8) & 0xff00ff; - if (ipos <= pGradient->gradient.stops->x) - return xRenderColorMultToCard32 (&pGradient->gradient.stops->color); + t2 = walker->left_ag*idist + walker->right_ag*dist; + t2 &= 0xff00ff00; - for (i = 1; i < pGradient->gradient.nstops; i++) - { - if (pGradient->gradient.stops[i].x >= ipos) - return PictureGradientColor (&pGradient->gradient.stops[i - 1], - &pGradient->gradient.stops[i], - ipos); - } + color = t2 & 0xff000000; + a = t2 >> 24; - return xRenderColorMultToCard32 (&pGradient->gradient.stops[--i].color); - } +#if 0 + t1 = t1*a + 0x800080; + t1 = (t1 + ((t1 >> 8) & 0xff00ff)) >> 8; + + t2 = (t2 >> 8)*a + 0x800080; + t2 = (t2 + ((t2 >> 8) & 0xff00ff)); +#else + /* this is faster and the difference is completely + * un-noticeable + */ + a += (a >> 7); + + t1 = t1*a >> 8; + t2 = t2*a >> 8; +#endif + + return (color | (t1 & 0xff00ff) | (t2 & 0xff00)); } + + static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *buffer, CARD32 *mask, CARD32 maskBits) { - SourcePictPtr pGradient = pict->pSourcePict; - CARD32 *end = buffer + width; + SourcePictPtr pGradient = pict->pSourcePict; + GradientWalker walker; + CARD32 *end = buffer + width; + + _gradient_walker_init (&walker, pGradient, pict->repeat); if (pGradient->type == SourcePictTypeSolidFill) { register CARD32 color = pGradient->solidFill.color; @@ -2853,20 +3017,29 @@ static void fbFetchSourcePict(PicturePtr { register CARD32 color; - color = gradientPixel (pGradient, t, pict->repeat); + color = _gradient_walker_pixel( &walker, t ); while (buffer < end) *buffer++ = color; } else { - while (buffer < end) { - if (!mask || *mask++ & maskBits) - { - *buffer = gradientPixel (pGradient, t, pict->repeat); - } - ++buffer; - t += inc; - } + if (!mask) { + while (buffer < end) + { + *buffer = _gradient_walker_pixel (&walker, t); + buffer += 1; + t += inc; + } + } else { + while (buffer < end) { + if (*mask++ & maskBits) + { + *buffer = _gradient_walker_pixel (&walker, t); + } + buffer += 1; + t += inc; + } + } } } else /* projective transformation */ @@ -2890,31 +3063,31 @@ static void fbFetchSourcePict(PicturePtr t = ((a * x + b * y) >> 16) + off; } - color = gradientPixel (pGradient, t, pict->repeat); + color = _gradient_walker_pixel( &walker, t ); while (buffer < end) *buffer++ = color; } else { - while (buffer < end) - { - if (!mask || *mask++ & maskBits) - { - if (v.vector[2] == 0) { - t = 0; - } else { - xFixed_48_16 x, y; - x = ((xFixed_48_16)v.vector[0] << 16) / v.vector[2]; - y = ((xFixed_48_16)v.vector[1] << 16) / v.vector[2]; - t = ((a*x + b*y) >> 16) + off; - } - *buffer = gradientPixel(pGradient, t, pict->repeat); - } - ++buffer; - v.vector[0] += unit.vector[0]; - v.vector[1] += unit.vector[1]; - v.vector[2] += unit.vector[2]; - } + while (buffer < end) + { + if (!mask || *mask++ & maskBits) + { + if (v.vector[2] == 0) { + t = 0; + } else { + xFixed_48_16 x, y; + x = ((xFixed_48_16)v.vector[0] << 16) / v.vector[2]; + y = ((xFixed_48_16)v.vector[1] << 16) / v.vector[2]; + t = ((a*x + b*y) >> 16) + off; + } + *buffer = _gradient_walker_pixel (&walker, t); + } + ++buffer; + v.vector[0] += unit.vector[0]; + v.vector[1] += unit.vector[1]; + v.vector[2] += unit.vector[2]; + } } } } else { @@ -2951,19 +3124,22 @@ static void fbFetchSourcePict(PicturePtr ry -= pGradient->radial.fy; while (buffer < end) { - double b, c, det, s; + double b, c, det, s; - if (!mask || *mask++ & maskBits) - { - b = 2*(rx*pGradient->radial.dx + ry*pGradient->radial.dy); - c = -(rx*rx + ry*ry); - det = (b * b) - (4 * pGradient->radial.a * c); - s = (-b + sqrt(det))/(2. * pGradient->radial.a); - *buffer = gradientPixel(pGradient, - (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536), - pict->repeat); - } - ++buffer; + if (!mask || *mask++ & maskBits) + { + xFixed_48_16 t; + + b = 2*(rx*pGradient->radial.dx + ry*pGradient->radial.dy); + c = -(rx*rx + ry*ry); + det = (b * b) - (4 * pGradient->radial.a * c); + s = (-b + sqrt(det))/(2. * pGradient->radial.a); + + t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536); + + *buffer = _gradient_walker_pixel (&walker, t); + } + ++buffer; rx += cx; ry += cy; } @@ -2972,25 +3148,27 @@ static void fbFetchSourcePict(PicturePtr double x, y; double b, c, det, s; - if (!mask || *mask++ & maskBits) - { - if (rz != 0) { - x = rx/rz; - y = ry/rz; - } else { - x = y = 0.; - } - x -= pGradient->radial.fx; - y -= pGradient->radial.fy; - b = 2*(x*pGradient->radial.dx + y*pGradient->radial.dy); - c = -(x*x + y*y); - det = (b * b) - (4 * pGradient->radial.a * c); - s = (-b + sqrt(det))/(2. * pGradient->radial.a); - *buffer = gradientPixel(pGradient, - (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536), - pict->repeat); - } - ++buffer; + if (!mask || *mask++ & maskBits) + { + xFixed_48_16 t; + + if (rz != 0) { + x = rx/rz; + y = ry/rz; + } else { + x = y = 0.; + } + x -= pGradient->radial.fx; + y -= pGradient->radial.fy; + b = 2*(x*pGradient->radial.dx + y*pGradient->radial.dy); + c = -(x*x + y*y); + det = (b * b) - (4 * pGradient->radial.a * c); + s = (-b + sqrt(det))/(2. * pGradient->radial.a); + t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536); + + *buffer = _gradient_walker_pixel (&walker, t); + } + ++buffer; rx += cx; ry += cy; rz += cz; @@ -3005,37 +3183,41 @@ static void fbFetchSourcePict(PicturePtr while (buffer < end) { double angle; - if (!mask || *mask++ & maskBits) - { - angle = atan2(ry, rx) + a; + if (!mask || *mask++ & maskBits) + { + xFixed_48_16 t; - *buffer = gradientPixel(pGradient, (xFixed_48_16) (angle * (65536. / (2*M_PI))), - pict->repeat); - } + angle = atan2(ry, rx) + a; + t = (xFixed_48_16) (angle * (65536. / (2*M_PI))); + + *buffer = _gradient_walker_pixel (&walker, t); + } ++buffer; rx += cx; ry += cy; } } else { - while (buffer < end) { double x, y; - double angle; + double angle; - if (!mask || *mask++ & maskBits) - { - if (rz != 0) { - x = rx/rz; - y = ry/rz; - } else { - x = y = 0.; - } - x -= pGradient->conical.center.x/65536.; - y -= pGradient->conical.center.y/65536.; - angle = atan2(y, x) + a; - *buffer = gradientPixel(pGradient, (xFixed_48_16) (angle * (65536. / (2*M_PI))), - pict->repeat); - } + if (!mask || *mask++ & maskBits) + { + xFixed_48_16 t; + + if (rz != 0) { + x = rx/rz; + y = ry/rz; + } else { + x = y = 0.; + } + x -= pGradient->conical.center.x/65536.; + y -= pGradient->conical.center.y/65536.; + angle = atan2(y, x) + a; + t = (xFixed_48_16) (angle * (65536. / (2*M_PI))); + + *buffer = _gradient_walker_pixel (&walker, t); + } ++buffer; rx += cx; ry += cy; -- 1.1.3 From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] fixing stupid compilation bugs in the previous merge From: David Turner Date: 1159284897 +0200 --- src/cairo-gstate.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) 8ec51fa6cba5dedb65828985284028908a12d337 diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index ce66c9f..dc7ee0f 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1062,7 +1062,7 @@ _cairo_gstate_show_page (cairo_gstate_t static void -_cairo_gstate_extents_to_user_rectangle (ciaro_gstate_t *gstate, +_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t *gstate, cairo_box_t *extents, double *x1, double *y1, double *x2, double *y2) @@ -1101,6 +1101,7 @@ _cairo_gstate_stroke_extents (cairo_gsta double *x2, double *y2) { cairo_status_t status; + cairo_box_t extents; status = _cairo_path_fixed_stroke_extents (path, &gstate->stroke_style, -- 1.1.3 From nobody Mon Sep 17 00:00:00 2001 Subject: [PATCH] disabling the alpha-mul computation optimization in the gradient rendering code From: David Turner Date: 1160441808 +0200 interestingly, this does create artefacts in some rare cases. --- pixman/src/fbcompose.c | 10 ---------- 1 files changed, 0 insertions(+), 10 deletions(-) 1f1bab38a5442e4e04b0494d79a8dccbf814c0e0 diff --git a/pixman/src/fbcompose.c b/pixman/src/fbcompose.c index ec9a515..b7af838 100644 --- a/pixman/src/fbcompose.c +++ b/pixman/src/fbcompose.c @@ -2939,21 +2939,11 @@ _gradient_walker_pixel (GradientWalker color = t2 & 0xff000000; a = t2 >> 24; -#if 0 t1 = t1*a + 0x800080; t1 = (t1 + ((t1 >> 8) & 0xff00ff)) >> 8; t2 = (t2 >> 8)*a + 0x800080; t2 = (t2 + ((t2 >> 8) & 0xff00ff)); -#else - /* this is faster and the difference is completely - * un-noticeable - */ - a += (a >> 7); - - t1 = t1*a >> 8; - t2 = t2*a >> 8; -#endif return (color | (t1 & 0xff00ff) | (t2 & 0xff00)); } -- 1.1.3