[cairo-commit] src/cairo-path-fill.c

Carl Worth cworth at kemper.freedesktop.org
Mon Mar 5 21:01:30 PST 2007


 src/cairo-path-fill.c |   93 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 93 insertions(+)

New commits:
diff-tree aa883123d2af905c846a8c0a67ff63fa6b16cd2b (from e15bb8efe62a4d2ffd1df31b092ca1fdd2743e43)
Author: Carl Worth <cworth at cworth.org>
Date:   Mon Mar 5 16:48:05 2007 -0800

    Optimize filling of a path that is a single device-axis-aligned rectangle.
    
    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
    ▏

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 7377960..71b2637 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_p
     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;
+}


More information about the cairo-commit mailing list