Use sentinels instead of special casing first and last stops
authorSøren Sandmann Pedersen <ssp@redhat.com>
Fri, 14 Oct 2011 13:02:14 +0000 (09:02 -0400)
committerSøren Sandmann Pedersen <ssp@redhat.com>
Sat, 15 Oct 2011 14:50:20 +0000 (10:50 -0400)
When storing the gradient stops internally, allocate two more stops,
one before the beginning of the stop list and one after the
end. Initialize those stops based on the repeat property of the
gradient.

This allows gradient_walker_reset() to be simplified because it can
now simply pick the two closest stops to the position without special
casing the first and last stops.

pixman/pixman-gradient-walker.c
pixman/pixman-image.c

index 53d0b30..3848247 100644 (file)
@@ -56,8 +56,6 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
     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->repeat)
     {
     case PIXMAN_REPEAT_NORMAL:
@@ -68,27 +66,12 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
                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;
-       }
+       left_x =  stops[n - 1].x;
+       left_c = &stops[n - 1].color;
+
+       right_x =  stops[n].x;
+       right_c = &stops[n].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;
@@ -100,27 +83,11 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
                break;
        }
 
-       if (n == 0)
-       {
-           left_x =  INT32_MIN;
-           left_c = &stops[0].color;
-       }
-       else
-       {
-           left_x =  stops[n - 1].x;
-           left_c = &stops[n - 1].color;
-       }
+       left_x =  stops[n - 1].x;
+       left_c = &stops[n - 1].color;
 
-       if (n == count)
-       {
-           right_x =  INT32_MAX;
-           right_c = &stops[n - 1].color;
-       }
-       else
-       {
-           right_x =  stops[n].x;
-           right_c = &stops[n].color;
-       }
+       right_x =  stops[n].x;
+       right_c = &stops[n].color;
        break;
 
     case PIXMAN_REPEAT_REFLECT:
@@ -134,27 +101,11 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
                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;
-       }
+       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;
-       }
+       right_x =  stops[n].x;
+       right_c = &stops[n].color;
 
        if ((int32_t)pos & 0x10000)
        {
@@ -182,25 +133,11 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
                break;
        }
 
-       if (n == 0)
-       {
-           left_x  =  INT32_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 = INT32_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;
-       }
+       left_x  =  stops[n - 1].x;
+       left_c  = &stops[n - 1].color;
+
+       right_x =  stops[n].x;
+       right_c = &stops[n].color;
     }
 
     walker->left_x   = left_x;
index afe587f..09d7cbc 100644 (file)
 
 #include "pixman-private.h"
 
+static const pixman_color_t transparent_black = { 0, 0, 0, 0 };
+
+static void
+gradient_property_changed (pixman_image_t *image)
+{
+    gradient_t *gradient = &image->gradient;
+    int n = gradient->n_stops;
+    pixman_gradient_stop_t *stops = gradient->stops;
+    pixman_gradient_stop_t *begin = &(gradient->stops[-1]);
+    pixman_gradient_stop_t *end = &(gradient->stops[n]);
+
+    switch (gradient->common.repeat)
+    {
+    default:
+    case PIXMAN_REPEAT_NONE:
+       begin->x = INT32_MIN;
+       begin->color = transparent_black;
+       end->x = INT32_MAX;
+       end->color = transparent_black;
+       break;
+
+    case PIXMAN_REPEAT_NORMAL:
+       begin->x = stops[n - 1].x - pixman_fixed_1;
+       begin->color = stops[n - 1].color;
+       end->x = stops[0].x + pixman_fixed_1;
+       end->color = stops[0].color;
+       break;
+
+    case PIXMAN_REPEAT_REFLECT:
+       begin->x = - stops[0].x;
+       begin->color = stops[0].color;
+       end->x = pixman_int_to_fixed (2) - stops[n - 1].x;
+       end->color = stops[n - 1].color;
+       break;
+
+    case PIXMAN_REPEAT_PAD:
+       begin->x = INT32_MIN;
+       begin->color = stops[0].color;
+       end->x = INT32_MAX;
+       end->color = stops[n - 1].color;
+       break;
+    }
+}
+
 pixman_bool_t
 _pixman_init_gradient (gradient_t *                  gradient,
                        const pixman_gradient_stop_t *stops,
@@ -38,14 +82,27 @@ _pixman_init_gradient (gradient_t *                  gradient,
 {
     return_val_if_fail (n_stops > 0, FALSE);
 
-    gradient->stops = pixman_malloc_ab (n_stops, sizeof (pixman_gradient_stop_t));
+    /* We allocate two extra stops, one before the beginning of the stop list,
+     * and one after the end. These stops are initialized to whatever color
+     * would be used for positions outside the range of the stop list.
+     *
+     * This saves a bit of computation in the gradient walker.
+     *
+     * The pointer we store in the gradient_t struct still points to the
+     * first user-supplied struct, so when freeing, we will have to
+     * subtract one.
+     */
+    gradient->stops =
+       pixman_malloc_ab (n_stops + 2, sizeof (pixman_gradient_stop_t));
     if (!gradient->stops)
        return FALSE;
 
+    gradient->stops += 1;
     memcpy (gradient->stops, stops, n_stops * sizeof (pixman_gradient_stop_t));
-
     gradient->n_stops = n_stops;
 
+    gradient->common.property_changed = gradient_property_changed;
+
     return TRUE;
 }
 
@@ -102,7 +159,17 @@ _pixman_image_fini (pixman_image_t *image)
            image->type == CONICAL)
        {
            if (image->gradient.stops)
-               free (image->gradient.stops);
+           {
+               /* See _pixman_init_gradient() for an explanation of the - 1 */
+               free (image->gradient.stops - 1);
+           }
+
+           /* This will trigger if someone adds a property_changed
+            * method to the linear/radial/conical gradient overwriting
+            * the general one.
+            */
+           assert (
+               image->common.property_changed == gradient_property_changed);
        }
 
        if (image->type == BITS && image->bits.free_me)