Pass the full image flags to iterators
[profile/ivi/pixman.git] / pixman / pixman-image.c
index 1aa9de1..6a965e4 100644 (file)
 #include <assert.h>
 
 #include "pixman-private.h"
-#include "pixman-combine32.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,
@@ -39,61 +82,111 @@ _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;
 }
 
-pixman_image_t *
-_pixman_image_allocate (void)
+void
+_pixman_image_init (pixman_image_t *image)
 {
-    pixman_image_t *image = malloc (sizeof (pixman_image_t));
+    image_common_t *common = &image->common;
+
+    pixman_region32_init (&common->clip_region);
+
+    common->alpha_count = 0;
+    common->have_clip_region = FALSE;
+    common->clip_sources = FALSE;
+    common->transform = NULL;
+    common->repeat = PIXMAN_REPEAT_NONE;
+    common->filter = PIXMAN_FILTER_NEAREST;
+    common->filter_params = NULL;
+    common->n_filter_params = 0;
+    common->alpha_map = NULL;
+    common->component_alpha = FALSE;
+    common->ref_count = 1;
+    common->property_changed = NULL;
+    common->client_clip = FALSE;
+    common->destroy_func = NULL;
+    common->destroy_data = NULL;
+    common->dirty = TRUE;
+}
 
-    if (image)
+pixman_bool_t
+_pixman_image_fini (pixman_image_t *image)
+{
+    image_common_t *common = (image_common_t *)image;
+
+    common->ref_count--;
+
+    if (common->ref_count == 0)
     {
-       image_common_t *common = &image->common;
+       if (image->common.destroy_func)
+           image->common.destroy_func (image, image->common.destroy_data);
 
-       pixman_region32_init (&common->clip_region);
+       pixman_region32_fini (&common->clip_region);
 
-       common->alpha_count = 0;
-       common->have_clip_region = FALSE;
-       common->clip_sources = FALSE;
-       common->transform = NULL;
-       common->repeat = PIXMAN_REPEAT_NONE;
-       common->filter = PIXMAN_FILTER_NEAREST;
-       common->filter_params = NULL;
-       common->n_filter_params = 0;
-       common->alpha_map = NULL;
-       common->component_alpha = FALSE;
-       common->ref_count = 1;
-       common->classify = NULL;
-       common->property_changed = NULL;
-       common->client_clip = FALSE;
-       common->destroy_func = NULL;
-       common->destroy_data = NULL;
-       common->dirty = TRUE;
+       free (common->transform);
+       free (common->filter_params);
+
+       if (common->alpha_map)
+           pixman_image_unref ((pixman_image_t *)common->alpha_map);
+
+       if (image->type == LINEAR ||
+           image->type == RADIAL ||
+           image->type == CONICAL)
+       {
+           if (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)
+           free (image->bits.free_me);
+
+       return TRUE;
     }
 
-    return image;
+    return FALSE;
 }
 
-source_image_class_t
-_pixman_image_classify (pixman_image_t *image,
-                        int             x,
-                        int             y,
-                        int             width,
-                        int             height)
+pixman_image_t *
+_pixman_image_allocate (void)
 {
-    if (image->common.classify)
-       return image->common.classify (image, x, y, width, height);
-    else
-       return SOURCE_IMAGE_CLASS_UNKNOWN;
+    pixman_image_t *image = malloc (sizeof (pixman_image_t));
+
+    if (image)
+       _pixman_image_init (image);
+
+    return image;
 }
 
 static void
@@ -115,39 +208,9 @@ pixman_image_ref (pixman_image_t *image)
 PIXMAN_EXPORT pixman_bool_t
 pixman_image_unref (pixman_image_t *image)
 {
-    image_common_t *common = (image_common_t *)image;
-
-    common->ref_count--;
-
-    if (common->ref_count == 0)
+    if (_pixman_image_fini (image))
     {
-       if (image->common.destroy_func)
-           image->common.destroy_func (image, image->common.destroy_data);
-
-       pixman_region32_fini (&common->clip_region);
-
-       if (common->transform)
-           free (common->transform);
-
-       if (common->filter_params)
-           free (common->filter_params);
-
-       if (common->alpha_map)
-           pixman_image_unref ((pixman_image_t *)common->alpha_map);
-
-       if (image->type == LINEAR ||
-           image->type == RADIAL ||
-           image->type == CONICAL)
-       {
-           if (image->gradient.stops)
-               free (image->gradient.stops);
-       }
-
-       if (image->type == BITS && image->bits.free_me)
-           free (image->bits.free_me);
-
        free (image);
-
        return TRUE;
     }
 
@@ -225,8 +288,24 @@ compute_image_info (pixman_image_t *image)
            if (image->common.transform->matrix[0][1] == 0 &&
                image->common.transform->matrix[1][0] == 0)
            {
+               if (image->common.transform->matrix[0][0] == -pixman_fixed_1 &&
+                   image->common.transform->matrix[1][1] == -pixman_fixed_1)
+               {
+                   flags |= FAST_PATH_ROTATE_180_TRANSFORM;
+               }
                flags |= FAST_PATH_SCALE_TRANSFORM;
            }
+           else if (image->common.transform->matrix[0][0] == 0 &&
+                    image->common.transform->matrix[1][1] == 0)
+           {
+               pixman_fixed_t m01 = image->common.transform->matrix[0][1];
+               pixman_fixed_t m10 = image->common.transform->matrix[1][0];
+
+               if (m01 == -1 && m10 == 1)
+                   flags |= FAST_PATH_ROTATE_90_TRANSFORM;
+               else if (m01 == 1 && m10 == -1)
+                   flags |= FAST_PATH_ROTATE_270_TRANSFORM;
+           }
        }
 
        if (image->common.transform->matrix[0][0] > 0)
@@ -248,6 +327,47 @@ compute_image_info (pixman_image_t *image)
     case PIXMAN_FILTER_GOOD:
     case PIXMAN_FILTER_BEST:
        flags |= (FAST_PATH_BILINEAR_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER);
+
+       /* Here we have a chance to optimize BILINEAR filter to NEAREST if
+        * they are equivalent for the currently used transformation matrix.
+        */
+       if (flags & FAST_PATH_ID_TRANSFORM)
+       {
+           flags |= FAST_PATH_NEAREST_FILTER;
+       }
+       else if (
+           /* affine and integer translation components in matrix ... */
+           ((flags & FAST_PATH_AFFINE_TRANSFORM) &&
+            !pixman_fixed_frac (image->common.transform->matrix[0][2] |
+                                image->common.transform->matrix[1][2])) &&
+           (
+               /* ... combined with a simple rotation */
+               (flags & (FAST_PATH_ROTATE_90_TRANSFORM |
+                         FAST_PATH_ROTATE_180_TRANSFORM |
+                         FAST_PATH_ROTATE_270_TRANSFORM)) ||
+               /* ... or combined with a simple non-rotated translation */
+               (image->common.transform->matrix[0][0] == pixman_fixed_1 &&
+                image->common.transform->matrix[1][1] == pixman_fixed_1 &&
+                image->common.transform->matrix[0][1] == 0 &&
+                image->common.transform->matrix[1][0] == 0)
+               )
+           )
+       {
+           /* FIXME: there are some affine-test failures, showing that
+            * handling of BILINEAR and NEAREST filter is not quite
+            * equivalent when getting close to 32K for the translation
+            * components of the matrix. That's likely some bug, but for
+            * now just skip BILINEAR->NEAREST optimization in this case.
+            */
+           pixman_fixed_t magic_limit = pixman_int_to_fixed (30000);
+           if (image->common.transform->matrix[0][2] <= magic_limit  &&
+               image->common.transform->matrix[1][2] <= magic_limit  &&
+               image->common.transform->matrix[0][2] >= -magic_limit &&
+               image->common.transform->matrix[1][2] >= -magic_limit)
+           {
+               flags |= FAST_PATH_NEAREST_FILTER;
+           }
+       }
        break;
 
     case PIXMAN_FILTER_CONVOLUTION:
@@ -318,6 +438,7 @@ compute_image_info (pixman_image_t *image)
        else
        {
            code = image->bits.format;
+           flags |= FAST_PATH_BITS_IMAGE;
        }
 
        if (!PIXMAN_FORMAT_A (image->bits.format)                               &&
@@ -499,7 +620,7 @@ pixman_image_set_transform (pixman_image_t *          image,
     if (common->transform == transform)
        return TRUE;
 
-    if (memcmp (&id, transform, sizeof (pixman_transform_t)) == 0)
+    if (!transform || memcmp (&id, transform, sizeof (pixman_transform_t)) == 0)
     {
        free (common->transform);
        common->transform = NULL;
@@ -508,6 +629,12 @@ pixman_image_set_transform (pixman_image_t *          image,
        goto out;
     }
 
+    if (common->transform &&
+       memcmp (common->transform, transform, sizeof (pixman_transform_t)) == 0)
+    {
+       return TRUE;
+    }
+
     if (common->transform == NULL)
        common->transform = malloc (sizeof (pixman_transform_t));
 
@@ -532,6 +659,9 @@ PIXMAN_EXPORT void
 pixman_image_set_repeat (pixman_image_t *image,
                          pixman_repeat_t repeat)
 {
+    if (image->common.repeat == repeat)
+       return;
+
     image->common.repeat = repeat;
 
     image_property_changed (image);
@@ -576,6 +706,9 @@ PIXMAN_EXPORT void
 pixman_image_set_source_clipping (pixman_image_t *image,
                                   pixman_bool_t   clip_sources)
 {
+    if (image->common.clip_sources == clip_sources)
+       return;
+
     image->common.clip_sources = clip_sources;
 
     image_property_changed (image);
@@ -591,6 +724,9 @@ pixman_image_set_indexed (pixman_image_t *        image,
 {
     bits_image_t *bits = (bits_image_t *)image;
 
+    if (bits->indexed == indexed)
+       return;
+
     bits->indexed = indexed;
 
     image_property_changed (image);
@@ -653,6 +789,9 @@ PIXMAN_EXPORT void
 pixman_image_set_component_alpha   (pixman_image_t *image,
                                     pixman_bool_t   component_alpha)
 {
+    if (image->common.component_alpha == component_alpha)
+       return;
+
     image->common.component_alpha = component_alpha;
 
     image_property_changed (image);
@@ -744,7 +883,7 @@ _pixman_image_get_solid (pixman_implementation_t *imp,
 
     _pixman_implementation_src_iter_init (
        imp, &iter, image, 0, 0, 1, 1,
-       (uint8_t *)&result, ITER_NARROW);
+       (uint8_t *)&result, ITER_NARROW, image->common.flags);
 
     result = *iter.get_scanline (&iter, NULL);