Home | History | Annotate | Line # | 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 <pixman-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 (pixman_iter_t                 *iter,
     93 		     const uint32_t                *mask,
     94 		     int                            Bpp,
     95 		     pixman_gradient_walker_write_t write_pixel,
     96 		     pixman_gradient_walker_fill_t  fill_pixel)
     97 {
     98     pixman_image_t *image  = iter->image;
     99     int             x      = iter->x;
    100     int             y      = iter->y;
    101     int             width  = iter->width;
    102     uint32_t *      buffer = iter->buffer;
    103 
    104     pixman_vector_t v, unit;
    105     pixman_fixed_32_32_t l;
    106     pixman_fixed_48_16_t dx, dy;
    107     gradient_t *gradient = (gradient_t *)image;
    108     linear_gradient_t *linear = (linear_gradient_t *)image;
    109     uint32_t *end = buffer + width * (Bpp / 4);
    110     pixman_gradient_walker_t walker;
    111 
    112     _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
    113 
    114     /* reference point is the center of the pixel */
    115     v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
    116     v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
    117     v.vector[2] = pixman_fixed_1;
    118 
    119     if (image->common.transform)
    120     {
    121 	if (!pixman_transform_point_3d (image->common.transform, &v))
    122 	    return iter->buffer;
    123 
    124 	unit.vector[0] = image->common.transform->matrix[0][0];
    125 	unit.vector[1] = image->common.transform->matrix[1][0];
    126 	unit.vector[2] = image->common.transform->matrix[2][0];
    127     }
    128     else
    129     {
    130 	unit.vector[0] = pixman_fixed_1;
    131 	unit.vector[1] = 0;
    132 	unit.vector[2] = 0;
    133     }
    134 
    135     dx = linear->p2.x - linear->p1.x;
    136     dy = linear->p2.y - linear->p1.y;
    137 
    138     l = dx * dx + dy * dy;
    139 
    140     if (l == 0 || unit.vector[2] == 0)
    141     {
    142 	/* affine transformation only */
    143 	pixman_fixed_32_32_t t, next_inc;
    144 	double inc;
    145 
    146 	if (l == 0 || v.vector[2] == 0)
    147 	{
    148 	    t = 0;
    149 	    inc = 0;
    150 	}
    151 	else
    152 	{
    153 	    double invden, v2;
    154 
    155 	    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
    156 		(l * (double) v.vector[2]);
    157 	    v2 = v.vector[2] * (1. / pixman_fixed_1);
    158 	    t = ((dx * v.vector[0] + dy * v.vector[1]) -
    159 		 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
    160 	    inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
    161 	}
    162 	next_inc = 0;
    163 
    164 	if (((pixman_fixed_32_32_t )(inc * width)) == 0)
    165 	{
    166 	    fill_pixel (&walker, t, buffer, end);
    167 	}
    168 	else
    169 	{
    170 	    int i;
    171 
    172 	    i = 0;
    173 	    while (buffer < end)
    174 	    {
    175 		if (!mask || *mask++)
    176 		{
    177 		    write_pixel (&walker, t + next_inc, buffer);
    178 		}
    179 		i++;
    180 		next_inc = inc * i;
    181 		buffer += (Bpp / 4);
    182 	    }
    183 	}
    184     }
    185     else
    186     {
    187 	/* projective transformation */
    188         double t;
    189 
    190 	t = 0;
    191 
    192 	while (buffer < end)
    193 	{
    194 	    if (!mask || *mask++)
    195 	    {
    196 	        if (v.vector[2] != 0)
    197 		{
    198 		    double invden, v2;
    199 
    200 		    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
    201 			(l * (double) v.vector[2]);
    202 		    v2 = v.vector[2] * (1. / pixman_fixed_1);
    203 		    t = ((dx * v.vector[0] + dy * v.vector[1]) -
    204 			 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
    205 		}
    206 
    207 		write_pixel (&walker, t, buffer);
    208 	    }
    209 
    210 	    buffer += (Bpp / 4);
    211 
    212 	    v.vector[0] += unit.vector[0];
    213 	    v.vector[1] += unit.vector[1];
    214 	    v.vector[2] += unit.vector[2];
    215 	}
    216     }
    217 
    218     iter->y++;
    219 
    220     return iter->buffer;
    221 }
    222 
    223 static uint32_t *
    224 linear_get_scanline_narrow (pixman_iter_t  *iter,
    225 			    const uint32_t *mask)
    226 {
    227     return linear_get_scanline (iter, mask, 4,
    228 				_pixman_gradient_walker_write_narrow,
    229 				_pixman_gradient_walker_fill_narrow);
    230 }
    231 
    232 
    233 static uint32_t *
    234 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
    235 {
    236     return linear_get_scanline (iter, NULL, 16,
    237 				_pixman_gradient_walker_write_wide,
    238 				_pixman_gradient_walker_fill_wide);
    239 }
    240 
    241 void
    242 _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
    243 {
    244     if (linear_gradient_is_horizontal (
    245 	    iter->image, iter->x, iter->y, iter->width, iter->height))
    246     {
    247 	if (iter->iter_flags & ITER_NARROW)
    248 	    linear_get_scanline_narrow (iter, NULL);
    249 	else
    250 	    linear_get_scanline_wide (iter, NULL);
    251 
    252 	iter->get_scanline = _pixman_iter_get_scanline_noop;
    253     }
    254     else
    255     {
    256 	if (iter->iter_flags & ITER_NARROW)
    257 	    iter->get_scanline = linear_get_scanline_narrow;
    258 	else
    259 	    iter->get_scanline = linear_get_scanline_wide;
    260     }
    261 }
    262 
    263 PIXMAN_EXPORT pixman_image_t *
    264 pixman_image_create_linear_gradient (const pixman_point_fixed_t *  p1,
    265                                      const pixman_point_fixed_t *  p2,
    266                                      const pixman_gradient_stop_t *stops,
    267                                      int                           n_stops)
    268 {
    269     pixman_image_t *image;
    270     linear_gradient_t *linear;
    271 
    272     image = _pixman_image_allocate ();
    273 
    274     if (!image)
    275 	return NULL;
    276 
    277     linear = &image->linear;
    278 
    279     if (!_pixman_init_gradient (&linear->common, stops, n_stops))
    280     {
    281 	free (image);
    282 	return NULL;
    283     }
    284 
    285     linear->p1 = *p1;
    286     linear->p2 = *p2;
    287 
    288     image->type = LINEAR;
    289 
    290     return image;
    291 }
    292 
    293