Home | History | Annotate | Download | only in pixman
      1 /* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
      2 /*
      3  * Copyright  2000 SuSE, Inc.
      4  * Copyright  2007 Red Hat, Inc.
      5  * Copyright  2000 Keith Packard, member of The XFree86 Project, Inc.
      6  *             2005 Lars Knoll & Zack Rusin, Trolltech
      7  *
      8  * Permission to use, copy, modify, distribute, and sell this software and its
      9  * documentation for any purpose is hereby granted without fee, provided that
     10  * the above copyright notice appear in all copies and that both that
     11  * copyright notice and this permission notice appear in supporting
     12  * documentation, and that the name of Keith Packard not be used in
     13  * advertising or publicity pertaining to distribution of the software without
     14  * specific, written prior permission.  Keith Packard makes no
     15  * representations about the suitability of this software for any purpose.  It
     16  * is provided "as is" without express or implied warranty.
     17  *
     18  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
     19  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
     21  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     22  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     23  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     24  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     25  * SOFTWARE.
     26  */
     27 
     28 #ifdef HAVE_CONFIG_H
     29 #include <config.h>
     30 #endif
     31 #include <stdlib.h>
     32 #include "pixman-private.h"
     33 
     34 static pixman_bool_t
     35 linear_gradient_is_horizontal (pixman_image_t *image,
     36 			       int             x,
     37 			       int             y,
     38 			       int             width,
     39 			       int             height)
     40 {
     41     linear_gradient_t *linear = (linear_gradient_t *)image;
     42     pixman_vector_t v;
     43     pixman_fixed_32_32_t l;
     44     pixman_fixed_48_16_t dx, dy;
     45     double inc;
     46 
     47     if (image->common.transform)
     48     {
     49 	/* projective transformation */
     50 	if (image->common.transform->matrix[2][0] != 0 ||
     51 	    image->common.transform->matrix[2][1] != 0 ||
     52 	    image->common.transform->matrix[2][2] == 0)
     53 	{
     54 	    return FALSE;
     55 	}
     56 
     57 	v.vector[0] = image->common.transform->matrix[0][1];
     58 	v.vector[1] = image->common.transform->matrix[1][1];
     59 	v.vector[2] = image->common.transform->matrix[2][2];
     60     }
     61     else
     62     {
     63 	v.vector[0] = 0;
     64 	v.vector[1] = pixman_fixed_1;
     65 	v.vector[2] = pixman_fixed_1;
     66     }
     67 
     68     dx = linear->p2.x - linear->p1.x;
     69     dy = linear->p2.y - linear->p1.y;
     70 
     71     l = dx * dx + dy * dy;
     72 
     73     if (l == 0)
     74 	return FALSE;
     75 
     76     /*
     77      * compute how much the input of the gradient walked changes
     78      * when moving vertically through the whole image
     79      */
     80     inc = height * (double) pixman_fixed_1 * pixman_fixed_1 *
     81 	(dx * v.vector[0] + dy * v.vector[1]) /
     82 	(v.vector[2] * (double) l);
     83 
     84     /* check that casting to integer would result in 0 */
     85     if (-1 < inc && inc < 1)
     86 	return TRUE;
     87 
     88     return FALSE;
     89 }
     90 
     91 static uint32_t *
     92 linear_get_scanline_narrow (pixman_iter_t  *iter,
     93 			    const uint32_t *mask)
     94 {
     95     pixman_image_t *image  = iter->image;
     96     int             x      = iter->x;
     97     int             y      = iter->y;
     98     int             width  = iter->width;
     99     uint32_t *      buffer = iter->buffer;
    100 
    101     pixman_vector_t v, unit;
    102     pixman_fixed_32_32_t l;
    103     pixman_fixed_48_16_t dx, dy;
    104     gradient_t *gradient = (gradient_t *)image;
    105     linear_gradient_t *linear = (linear_gradient_t *)image;
    106     uint32_t *end = buffer + width;
    107     pixman_gradient_walker_t walker;
    108 
    109     _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
    110 
    111     /* reference point is the center of the pixel */
    112     v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
    113     v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
    114     v.vector[2] = pixman_fixed_1;
    115 
    116     if (image->common.transform)
    117     {
    118 	if (!pixman_transform_point_3d (image->common.transform, &v))
    119 	    return iter->buffer;
    120 
    121 	unit.vector[0] = image->common.transform->matrix[0][0];
    122 	unit.vector[1] = image->common.transform->matrix[1][0];
    123 	unit.vector[2] = image->common.transform->matrix[2][0];
    124     }
    125     else
    126     {
    127 	unit.vector[0] = pixman_fixed_1;
    128 	unit.vector[1] = 0;
    129 	unit.vector[2] = 0;
    130     }
    131 
    132     dx = linear->p2.x - linear->p1.x;
    133     dy = linear->p2.y - linear->p1.y;
    134 
    135     l = dx * dx + dy * dy;
    136 
    137     if (l == 0 || unit.vector[2] == 0)
    138     {
    139 	/* affine transformation only */
    140         pixman_fixed_32_32_t t, next_inc;
    141 	double inc;
    142 
    143 	if (l == 0 || v.vector[2] == 0)
    144 	{
    145 	    t = 0;
    146 	    inc = 0;
    147 	}
    148 	else
    149 	{
    150 	    double invden, v2;
    151 
    152 	    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
    153 		(l * (double) v.vector[2]);
    154 	    v2 = v.vector[2] * (1. / pixman_fixed_1);
    155 	    t = ((dx * v.vector[0] + dy * v.vector[1]) -
    156 		 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
    157 	    inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
    158 	}
    159 	next_inc = 0;
    160 
    161 	if (((pixman_fixed_32_32_t )(inc * width)) == 0)
    162 	{
    163 	    register uint32_t color;
    164 
    165 	    color = _pixman_gradient_walker_pixel (&walker, t);
    166 	    while (buffer < end)
    167 		*buffer++ = color;
    168 	}
    169 	else
    170 	{
    171 	    int i;
    172 
    173 	    i = 0;
    174 	    while (buffer < end)
    175 	    {
    176 		if (!mask || *mask++)
    177 		{
    178 		    *buffer = _pixman_gradient_walker_pixel (&walker,
    179 							     t + next_inc);
    180 		}
    181 		i++;
    182 		next_inc = inc * i;
    183 		buffer++;
    184 	    }
    185 	}
    186     }
    187     else
    188     {
    189 	/* projective transformation */
    190         double t;
    191 
    192 	t = 0;
    193 
    194 	while (buffer < end)
    195 	{
    196 	    if (!mask || *mask++)
    197 	    {
    198 	        if (v.vector[2] != 0)
    199 		{
    200 		    double invden, v2;
    201 
    202 		    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
    203 			(l * (double) v.vector[2]);
    204 		    v2 = v.vector[2] * (1. / pixman_fixed_1);
    205 		    t = ((dx * v.vector[0] + dy * v.vector[1]) -
    206 			 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
    207 		}
    208 
    209 		*buffer = _pixman_gradient_walker_pixel (&walker, t);
    210 	    }
    211 
    212 	    ++buffer;
    213 
    214 	    v.vector[0] += unit.vector[0];
    215 	    v.vector[1] += unit.vector[1];
    216 	    v.vector[2] += unit.vector[2];
    217 	}
    218     }
    219 
    220     iter->y++;
    221 
    222     return iter->buffer;
    223 }
    224 
    225 static uint32_t *
    226 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
    227 {
    228     uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
    229 
    230     pixman_expand_to_float (
    231 	(argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
    232 
    233     return buffer;
    234 }
    235 
    236 void
    237 _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
    238 {
    239     if (linear_gradient_is_horizontal (
    240 	    iter->image, iter->x, iter->y, iter->width, iter->height))
    241     {
    242 	if (iter->iter_flags & ITER_NARROW)
    243 	    linear_get_scanline_narrow (iter, NULL);
    244 	else
    245 	    linear_get_scanline_wide (iter, NULL);
    246 
    247 	iter->get_scanline = _pixman_iter_get_scanline_noop;
    248     }
    249     else
    250     {
    251 	if (iter->iter_flags & ITER_NARROW)
    252 	    iter->get_scanline = linear_get_scanline_narrow;
    253 	else
    254 	    iter->get_scanline = linear_get_scanline_wide;
    255     }
    256 }
    257 
    258 PIXMAN_EXPORT pixman_image_t *
    259 pixman_image_create_linear_gradient (const pixman_point_fixed_t *  p1,
    260                                      const pixman_point_fixed_t *  p2,
    261                                      const pixman_gradient_stop_t *stops,
    262                                      int                           n_stops)
    263 {
    264     pixman_image_t *image;
    265     linear_gradient_t *linear;
    266 
    267     image = _pixman_image_allocate ();
    268 
    269     if (!image)
    270 	return NULL;
    271 
    272     linear = &image->linear;
    273 
    274     if (!_pixman_init_gradient (&linear->common, stops, n_stops))
    275     {
    276 	free (image);
    277 	return NULL;
    278     }
    279 
    280     linear->p1 = *p1;
    281     linear->p2 = *p2;
    282 
    283     image->type = LINEAR;
    284 
    285     return image;
    286 }
    287 
    288