Pass the full image flags to iterators
[profile/ivi/pixman.git] / pixman / pixman-linear-gradient.c
index 04bd796..32b8ba7 100644 (file)
+/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
 /*
  * Copyright © 2000 SuSE, Inc.
  * Copyright © 2007 Red Hat, Inc.
+ * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
+ *             2005 Lars Knoll & Zack Rusin, Trolltech
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * the above copyright notice appear in all copies and that both that
  * copyright notice and this permission notice appear in supporting
- * documentation, and that the name of SuSE not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission.  SuSE makes no representations about the
- * suitability of this software for any purpose.  It is provided "as is"
- * without express or implied warranty.
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  Keith Packard makes no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
  *
- * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
- * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
  */
 
+#ifdef HAVE_CONFIG_H
 #include <config.h>
+#endif
 #include <stdlib.h>
 #include "pixman-private.h"
 
-static source_pict_class_t
-linear_gradient_classify (pixman_image_t *image,
-                         int             x,
-                         int             y,
-                         int             width,
-                         int             height)
+static pixman_bool_t
+linear_gradient_is_horizontal (pixman_image_t *image,
+                              int             x,
+                              int             y,
+                              int             width,
+                              int             height)
 {
     linear_gradient_t *linear = (linear_gradient_t *)image;
-    pixman_vector_t   v;
+    pixman_vector_t v;
     pixman_fixed_32_32_t l;
-    pixman_fixed_48_16_t dx, dy, a, b, off;
-    pixman_fixed_48_16_t factors[4];
-    int             i;
-    
-    image->source.class = SOURCE_IMAGE_CLASS_UNKNOWN;
-    
+    pixman_fixed_48_16_t dx, dy;
+    double inc;
+
+    if (image->common.transform)
+    {
+       /* projective transformation */
+       if (image->common.transform->matrix[2][0] != 0 ||
+           image->common.transform->matrix[2][1] != 0 ||
+           image->common.transform->matrix[2][2] == 0)
+       {
+           return FALSE;
+       }
+
+       v.vector[0] = image->common.transform->matrix[0][1];
+       v.vector[1] = image->common.transform->matrix[1][1];
+       v.vector[2] = image->common.transform->matrix[2][2];
+    }
+    else
+    {
+       v.vector[0] = 0;
+       v.vector[1] = pixman_fixed_1;
+       v.vector[2] = pixman_fixed_1;
+    }
+
     dx = linear->p2.x - linear->p1.x;
     dy = linear->p2.y - linear->p1.y;
+
     l = dx * dx + dy * dy;
-    if (l)
+
+    if (l == 0)
+       return FALSE;
+
+    /*
+     * compute how much the input of the gradient walked changes
+     * when moving vertically through the whole image
+     */
+    inc = height * (double) pixman_fixed_1 * pixman_fixed_1 *
+       (dx * v.vector[0] + dy * v.vector[1]) /
+       (v.vector[2] * (double) l);
+
+    /* check that casting to integer would result in 0 */
+    if (-1 < inc && inc < 1)
+       return TRUE;
+
+    return FALSE;
+}
+
+static uint32_t *
+linear_get_scanline_narrow (pixman_iter_t  *iter,
+                           const uint32_t *mask)
+{
+    pixman_image_t *image  = iter->image;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    uint32_t *      buffer = iter->buffer;
+
+    pixman_vector_t v, unit;
+    pixman_fixed_32_32_t l;
+    pixman_fixed_48_16_t dx, dy;
+    gradient_t *gradient = (gradient_t *)image;
+    linear_gradient_t *linear = (linear_gradient_t *)image;
+    uint32_t *end = buffer + width;
+    pixman_gradient_walker_t walker;
+
+    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
+
+    /* reference point is the center of the pixel */
+    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
+    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
+    v.vector[2] = pixman_fixed_1;
+
+    if (image->common.transform)
     {
-       a = (dx << 32) / l;
-       b = (dy << 32) / l;
+       if (!pixman_transform_point_3d (image->common.transform, &v))
+           return iter->buffer;
+
+       unit.vector[0] = image->common.transform->matrix[0][0];
+       unit.vector[1] = image->common.transform->matrix[1][0];
+       unit.vector[2] = image->common.transform->matrix[2][0];
     }
     else
     {
-       a = b = 0;
+       unit.vector[0] = pixman_fixed_1;
+       unit.vector[1] = 0;
+       unit.vector[2] = 0;
     }
-    
-    off = (-a * linear->p1.x
-          -b * linear->p1.y) >> 16;
-    
-    for (i = 0; i < 3; i++)
+
+    dx = linear->p2.x - linear->p1.x;
+    dy = linear->p2.y - linear->p1.y;
+
+    l = dx * dx + dy * dy;
+
+    if (l == 0 || unit.vector[2] == 0)
     {
-       v.vector[0] = pixman_int_to_fixed ((i % 2) * (width  - 1) + x);
-       v.vector[1] = pixman_int_to_fixed ((i / 2) * (height - 1) + y);
-       v.vector[2] = pixman_fixed_1;
-       
-       if (image->common.transform)
+       /* affine transformation only */
+        pixman_fixed_32_32_t t, next_inc;
+       double inc;
+
+       if (l == 0 || v.vector[2] == 0)
+       {
+           t = 0;
+           inc = 0;
+       }
+       else
+       {
+           double invden, v2;
+
+           invden = pixman_fixed_1 * (double) pixman_fixed_1 /
+               (l * (double) v.vector[2]);
+           v2 = v.vector[2] * (1. / pixman_fixed_1);
+           t = ((dx * v.vector[0] + dy * v.vector[1]) - 
+                (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+           inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
+       }
+       next_inc = 0;
+
+       if (((pixman_fixed_32_32_t )(inc * width)) == 0)
        {
-           if (!pixman_transform_point_3d (image->common.transform, &v))
+           register uint32_t color;
+
+           color = _pixman_gradient_walker_pixel (&walker, t);
+           while (buffer < end)
+               *buffer++ = color;
+       }
+       else
+       {
+           int i;
+
+           i = 0;
+           while (buffer < end)
+           {
+               if (!mask || *mask++)
+               {
+                   *buffer = _pixman_gradient_walker_pixel (&walker,
+                                                            t + next_inc);
+               }
+               i++;
+               next_inc = inc * i;
+               buffer++;
+           }
+       }
+    }
+    else
+    {
+       /* projective transformation */
+        double t;
+
+       t = 0;
+
+       while (buffer < end)
+       {
+           if (!mask || *mask++)
            {
-               image->source.class = SOURCE_IMAGE_CLASS_UNKNOWN;
-               
-               return image->source.class;
+               if (v.vector[2] != 0)
+               {
+                   double invden, v2;
+
+                   invden = pixman_fixed_1 * (double) pixman_fixed_1 /
+                       (l * (double) v.vector[2]);
+                   v2 = v.vector[2] * (1. / pixman_fixed_1);
+                   t = ((dx * v.vector[0] + dy * v.vector[1]) - 
+                        (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+               }
+
+               *buffer = _pixman_gradient_walker_pixel (&walker, t);
            }
+
+           ++buffer;
+
+           v.vector[0] += unit.vector[0];
+           v.vector[1] += unit.vector[1];
+           v.vector[2] += unit.vector[2];
        }
-       
-       factors[i] = ((a * v.vector[0] + b * v.vector[1]) >> 16) + off;
     }
-    
-    if (factors[2] == factors[0])
-       image->source.class = SOURCE_IMAGE_CLASS_HORIZONTAL;
-    else if (factors[1] == factors[0])
-       image->source.class = SOURCE_IMAGE_CLASS_VERTICAL;
 
-    return image->source.class;
+    iter->y++;
+
+    return iter->buffer;
+}
+
+static uint32_t *
+linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+    uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
+
+    pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+    return buffer;
+}
+
+void
+_pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
+{
+    if (linear_gradient_is_horizontal (
+           iter->image, iter->x, iter->y, iter->width, iter->height))
+    {
+       if (iter->iter_flags & ITER_NARROW)
+           linear_get_scanline_narrow (iter, NULL);
+       else
+           linear_get_scanline_wide (iter, NULL);
+
+       iter->get_scanline = _pixman_iter_get_scanline_noop;
+    }
+    else
+    {
+       if (iter->iter_flags & ITER_NARROW)
+           iter->get_scanline = linear_get_scanline_narrow;
+       else
+           iter->get_scanline = linear_get_scanline_wide;
+    }
 }
 
 PIXMAN_EXPORT pixman_image_t *
-pixman_image_create_linear_gradient (pixman_point_fixed_t         *p1,
-                                    pixman_point_fixed_t         *p2,
-                                    const pixman_gradient_stop_t *stops,
-                                    int                           n_stops)
+pixman_image_create_linear_gradient (pixman_point_fixed_t *        p1,
+                                     pixman_point_fixed_t *        p2,
+                                     const pixman_gradient_stop_t *stops,
+                                     int                           n_stops)
 {
     pixman_image_t *image;
     linear_gradient_t *linear;
-    
-    return_val_if_fail (n_stops >= 2, NULL);
-    
-    image = _pixman_image_allocate();
-    
+
+    image = _pixman_image_allocate ();
+
     if (!image)
        return NULL;
-    
+
     linear = &image->linear;
-    
+
     if (!_pixman_init_gradient (&linear->common, stops, n_stops))
     {
        free (image);
        return NULL;
     }
-    
+
     linear->p1 = *p1;
     linear->p2 = *p2;
-    
+
     image->type = LINEAR;
-    image->source.class = SOURCE_IMAGE_CLASS_UNKNOWN;
-    image->common.classify = linear_gradient_classify;
-    
+
     return image;
 }
+