From aa883123d2af905c846a8c0a67ff63fa6b16cd2b Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Mon, 5 Mar 2007 16:48:05 -0800 Subject: [PATCH] Optimize filling of a path that is a single device-axis-aligned rectangle. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turns out that this case is extremely common and worth avoiding the overhead of the path iteration and tessellation code. The optimization here works only for device-axis-aligned rectangles It should be possible to generalize this to catch more cases, (such as any convex quadrilateral with 4 or fewer points). This fix results in a 1.4-1.8x speedup for the rectangles perf case: image-rgb rectangles-512 7.80 1.22% -> 4.35 1.62%: 1.79x speedup ▊ image-rgba rectangles-512 7.71 4.77% -> 4.37 0.30%: 1.77x speedup ▊ xlib-rgba rectangles-512 8.78 5.02% -> 5.58 5.54%: 1.57x speedup ▋ xlib-rgb rectangles-512 11.87 2.71% -> 8.75 0.08%: 1.36x speedup ▍ Which conveniently overcomes the ~ 1.3x slowdown we had been seeing for this case since 1.2. Now, compared to 1.2.6 we see only a speedup: image-rgba rectangles-512 6.19 0.29% -> 4.37 0.30%: 1.42x speedup ▎ image-rgb rectangles-512 6.12 1.68% -> 4.35 1.62%: 1.41x speedup ▎ xlib-rgba rectangles-512 7.48 1.07% -> 5.58 5.54%: 1.34x speedup ▏ xlib-rgb rectangles-512 10.35 1.03% -> 8.75 0.08%: 1.18x speedup ▏ --- src/cairo-path-fill.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index 7377960f8..71b263741 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -35,6 +35,7 @@ */ #include "cairoint.h" +#include "cairo-path-fixed-private.h" typedef struct cairo_filler { double tolerance; @@ -169,6 +170,10 @@ _cairo_filler_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, + cairo_traps_t *traps); + cairo_status_t _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, @@ -178,6 +183,12 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t filler; + /* 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, traps); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + _cairo_filler_init (&filler, tolerance, traps); status = _cairo_path_fixed_interpret (path, @@ -205,3 +216,85 @@ BAIL: 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. + * + * 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 (cairo_path_fixed_t *path, + cairo_traps_t *traps) +{ + cairo_path_op_buf_t *op = path->op_buf_head; + cairo_path_arg_buf_t *arg = path->arg_buf_head; + int final; + + /* Ensure the path has the operators we expect for a rectangular path. + */ + if (op == NULL || op->num_ops < 5) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op->op[0] != CAIRO_PATH_OP_MOVE_TO || + op->op[1] != CAIRO_PATH_OP_LINE_TO || + op->op[2] != CAIRO_PATH_OP_LINE_TO || + op->op[3] != CAIRO_PATH_OP_LINE_TO) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Now, there are choices. The rectangle might end with a LINE_TO + * (to the original point), but this isn't required. If it + * doesn't, then it must end with a CLOSE_PATH. */ + if (op->op[4] == CAIRO_PATH_OP_LINE_TO) { + if (arg->points[4].x != arg->points[0].x || + arg->points[4].y != arg->points[0].y) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } else if (op->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Finally, a trailing CLOSE_PATH or MOVE_TO after the rectangle + * is fine. But anything more than that means we must return + * unsupported. */ + final = 5; + if (final < op->num_ops && + op->op[final] == CAIRO_PATH_OP_CLOSE_PATH) + { + final++; + } + if (final < op->num_ops && + op->op[final] == CAIRO_PATH_OP_MOVE_TO) + { + final++; + } + if (final < op->num_ops) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Now that we've verified the operators, we must ensure that the + * path coordinates are consistent with a rectangle. There are two + * choices here. */ + if (arg->points[0].y == arg->points[1].y && + arg->points[1].x == arg->points[2].x && + arg->points[2].y == arg->points[3].y && + arg->points[3].x == arg->points[0].x) + { + return _cairo_traps_tessellate_convex_quad (traps, + arg->points); + } + + if (arg->points[0].x == arg->points[1].x && + arg->points[1].y == arg->points[2].y && + arg->points[2].x == arg->points[3].x && + arg->points[3].y == arg->points[0].y) + { + return _cairo_traps_tessellate_convex_quad (traps, + arg->points); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +}