[cairo] Another Gradient rendering speedup patch

David Turner david at freetype.org
Thu Feb 1 00:30:20 PST 2007


Hello,

here's a small patch that speeds up gradient rendering in the very common case
where all color stops are opaque. We simply avoid performing un-needed
alpha pre-multiplications :-)

cairo-perf-diff indicates, in the image backend, a 1.56 speedup for linear gradients,
and a 1.15 one for radial ones.

it also fixes a small bug in cairo-perf-diff that prevented it to work properly
when trying to compare perf files directly on my workstation.

Enjoy,

- David Turner
-------------- next part --------------
From b8cdb62c0a63e2f2ac843eecb0c673038380be89 Mon Sep 17 00:00:00 2001
From: David Turner <david at freetype.org>
Date: Thu, 1 Feb 2007 09:17:43 +0100
Subject: [PATCH] optimize the gradient fill for the case where all color stops are opaque,
cairo-perf-diff indicates a 1.56 speedup for linear gradients, and a 1.15 one for radial ones in the image backend
---
 perf/cairo-perf-diff   |    5 ++
 pixman/src/fbcompose.c |  120 +++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 102 insertions(+), 23 deletions(-)

diff --git a/perf/cairo-perf-diff b/perf/cairo-perf-diff
index f873917..7228697 100755
--- a/perf/cairo-perf-diff
+++ b/perf/cairo-perf-diff
@@ -71,6 +71,9 @@ git_setup() {
     SUBDIRECTORY_OK='Yes'
     . git-sh-setup
     CAIRO_DIR=$(dirname $GIT_DIR)
+    if [ "x$CAIRO_DIR" = "x" ]; then
+        CAIRO_DIR="."
+    fi
     if [ "$CAIRO_DIR" = "." ]; then
 	CAIRO_DIR=$(pwd)
     fi
@@ -152,6 +155,8 @@ run_cairo_perf_if_not_cached() {
     cd $owd
 }
 
+git_setup
+
 if [ ! -e $old ]; then
     git_setup
     run_cairo_perf_if_not_cached $old old
diff --git a/pixman/src/fbcompose.c b/pixman/src/fbcompose.c
index d698eb4..68a7cb3 100644
--- a/pixman/src/fbcompose.c
+++ b/pixman/src/fbcompose.c
@@ -2744,6 +2744,7 @@ typedef struct
 
     pixman_gradient_stop_t  *stops;
     int                      num_stops;
+    int                      all_opaque;  /* TRUE if all stops have opaque colors */
     unsigned int             spread;
 } GradientWalker;
 
@@ -2763,6 +2764,24 @@ _gradient_walker_init (GradientWalker  *
     walker->right_ag  = 0;
     walker->right_rb  = 0;
     walker->spread    = spread;
+
+   /* now check wether all stop colors are opaque or not
+    * if they are, we'll use a slightly optimized version of
+    * _gradient_walker_pixel that doesn't do any pre-multiplications
+    */
+    {
+        int  nn;
+        int  all_opaque = 1;
+
+        for (nn = 0; nn < walker->num_stops; nn++) {
+            if ( (walker->stops[nn].color.alpha >> 8) != 0xFF )
+            {
+                all_opaque = 0;
+                break;
+            }
+        }
+        walker->all_opaque = all_opaque;
+    }
 }
 
 static void
@@ -2948,6 +2967,29 @@ _gradient_walker_pixel (GradientWalker  
 }
 
 
+static CARD32
+_gradient_walker_pixel_opaque (GradientWalker  *walker,
+                               xFixed_32_32     x)
+{
+    int  dist, idist;
+    uint32_t  t1, t2;
+
+    if (GRADIENT_WALKER_NEED_RESET (walker, x))
+        _gradient_walker_reset (walker, x);
+
+    dist  = ((int)(x - walker->left_x)*walker->stepper) >> 16;
+    idist = 256 - dist;
+
+    /* combined INTERPOLATE and premultiply */
+    t1 = walker->left_rb*idist + walker->right_rb*dist;
+    t1 = (t1 >> 8) & 0xff00ff;
+
+    t2  = walker->left_ag*idist + walker->right_ag*dist;
+    t2 &= 0xff00ff00;
+
+    return (t1 | t2);
+}
+
 
 static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *buffer, CARD32 *mask, CARD32 maskBits)
 {
@@ -3013,11 +3055,20 @@ static void fbFetchSourcePict(PicturePtr
 	    else
 	    {
                 if (!mask) {
-                    while (buffer < end)
-                    {
-                        *buffer = _gradient_walker_pixel (&walker, t);
-                        buffer += 1;
-                        t      += inc;
+                    if ( walker.all_opaque ) {
+                        while (buffer < end)
+                        {
+                            *buffer = _gradient_walker_pixel_opaque (&walker, t);
+                            buffer += 1;
+                            t      += inc;
+                        }
+                    }else {
+                        while (buffer < end)
+                        {
+                            *buffer = _gradient_walker_pixel (&walker, t);
+                            buffer += 1;
+                            t      += inc;
+                        }
                     }
                 } else {
                     while (buffer < end) {
@@ -3112,25 +3163,48 @@ static void fbFetchSourcePict(PicturePtr
                 rx -= pGradient->radial.fx;
                 ry -= pGradient->radial.fy;
 
-                while (buffer < end) {
-                    double b, c, det, s;
-
-                    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);
+                if (walker.all_opaque) {
+                    while (buffer < end) {
+                        double b, c, det, s;
+    
+                        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_opaque (&walker, t);
+                        }
+                        ++buffer;
+                        rx += cx;
+                        ry += cy;
+                    }
+                } else {
+                    while (buffer < end) {
+                        double b, c, det, s;
+    
+                        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;
                     }
-                    ++buffer;
-                    rx += cx;
-                    ry += cy;
                 }
             } else {
                 while (buffer < end) {
-- 
1.4.1



More information about the cairo mailing list