Home | History | Annotate | Download | only in pixman
      1 /*
      2  *
      3  * Copyright  2000 Keith Packard, member of The XFree86 Project, Inc.
      4  *             2005 Lars Knoll & Zack Rusin, Trolltech
      5  *
      6  * Permission to use, copy, modify, distribute, and sell this software and its
      7  * documentation for any purpose is hereby granted without fee, provided that
      8  * the above copyright notice appear in all copies and that both that
      9  * copyright notice and this permission notice appear in supporting
     10  * documentation, and that the name of Keith Packard not be used in
     11  * advertising or publicity pertaining to distribution of the software without
     12  * specific, written prior permission.  Keith Packard makes no
     13  * representations about the suitability of this software for any purpose.  It
     14  * is provided "as is" without express or implied warranty.
     15  *
     16  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
     17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
     18  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
     19  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     21  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     22  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     23  * SOFTWARE.
     24  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include <config.h>
     28 #endif
     29 #include "pixman-private.h"
     30 
     31 void
     32 _pixman_gradient_walker_init (pixman_gradient_walker_t *walker,
     33                               gradient_t *              gradient,
     34                               pixman_repeat_t		repeat)
     35 {
     36     walker->num_stops = gradient->n_stops;
     37     walker->stops     = gradient->stops;
     38     walker->left_x    = 0;
     39     walker->right_x   = 0x10000;
     40     walker->a_s       = 0.0f;
     41     walker->a_b       = 0.0f;
     42     walker->r_s       = 0.0f;
     43     walker->r_b       = 0.0f;
     44     walker->g_s       = 0.0f;
     45     walker->g_b       = 0.0f;
     46     walker->b_s       = 0.0f;
     47     walker->b_b       = 0.0f;
     48     walker->repeat    = repeat;
     49 
     50     walker->need_reset = TRUE;
     51 }
     52 
     53 static void
     54 gradient_walker_reset (pixman_gradient_walker_t *walker,
     55 		       pixman_fixed_48_16_t      pos)
     56 {
     57     int32_t x, left_x, right_x;
     58     pixman_color_t *left_c, *right_c;
     59     int n, count = walker->num_stops;
     60     pixman_gradient_stop_t *stops = walker->stops;
     61     float la, lr, lg, lb;
     62     float ra, rr, rg, rb;
     63     float lx, rx;
     64 
     65     if (walker->repeat == PIXMAN_REPEAT_NORMAL)
     66     {
     67 	x = (int32_t)pos & 0xffff;
     68     }
     69     else if (walker->repeat == PIXMAN_REPEAT_REFLECT)
     70     {
     71 	x = (int32_t)pos & 0xffff;
     72 	if ((int32_t)pos & 0x10000)
     73 	    x = 0x10000 - x;
     74     }
     75     else
     76     {
     77 	x = pos;
     78     }
     79 
     80     for (n = 0; n < count; n++)
     81     {
     82 	if (x < stops[n].x)
     83 	    break;
     84     }
     85 
     86     left_x =  stops[n - 1].x;
     87     left_c = &stops[n - 1].color;
     88 
     89     right_x =  stops[n].x;
     90     right_c = &stops[n].color;
     91 
     92     if (walker->repeat == PIXMAN_REPEAT_NORMAL)
     93     {
     94 	left_x  += (pos - x);
     95 	right_x += (pos - x);
     96     }
     97     else if (walker->repeat == PIXMAN_REPEAT_REFLECT)
     98     {
     99 	if ((int32_t)pos & 0x10000)
    100 	{
    101 	    pixman_color_t  *tmp_c;
    102 	    int32_t tmp_x;
    103 
    104 	    tmp_x   = 0x10000 - right_x;
    105 	    right_x = 0x10000 - left_x;
    106 	    left_x  = tmp_x;
    107 
    108 	    tmp_c   = right_c;
    109 	    right_c = left_c;
    110 	    left_c  = tmp_c;
    111 
    112 	    x = 0x10000 - x;
    113 	}
    114 	left_x  += (pos - x);
    115 	right_x += (pos - x);
    116     }
    117     else if (walker->repeat == PIXMAN_REPEAT_NONE)
    118     {
    119 	if (n == 0)
    120 	    right_c = left_c;
    121 	else if (n == count)
    122 	    left_c = right_c;
    123     }
    124 
    125     /* The alpha channel is scaled to be in the [0, 255] interval,
    126      * and the red/green/blue channels are scaled to be in [0, 1].
    127      * This ensures that after premultiplication all channels will
    128      * be in the [0, 255] interval.
    129      */
    130     la = (left_c->alpha * (1.0f/257.0f));
    131     lr = (left_c->red * (1.0f/257.0f));
    132     lg = (left_c->green * (1.0f/257.0f));
    133     lb = (left_c->blue * (1.0f/257.0f));
    134 
    135     ra = (right_c->alpha * (1.0f/257.0f));
    136     rr = (right_c->red * (1.0f/257.0f));
    137     rg = (right_c->green * (1.0f/257.0f));
    138     rb = (right_c->blue * (1.0f/257.0f));
    139 
    140     lx = left_x * (1.0f/65536.0f);
    141     rx = right_x * (1.0f/65536.0f);
    142 
    143     if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX)
    144     {
    145 	walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f;
    146 	walker->a_b = (la + ra) / 2.0f;
    147 	walker->r_b = (lr + rr) / 510.0f;
    148 	walker->g_b = (lg + rg) / 510.0f;
    149 	walker->b_b = (lb + rb) / 510.0f;
    150     }
    151     else
    152     {
    153 	float w_rec = 1.0f / (rx - lx);
    154 
    155 	walker->a_b = (la * rx - ra * lx) * w_rec;
    156 	walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f);
    157 	walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f);
    158 	walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f);
    159 
    160 	walker->a_s = (ra - la) * w_rec;
    161 	walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f);
    162 	walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f);
    163 	walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f);
    164     }
    165 
    166     walker->left_x = left_x;
    167     walker->right_x = right_x;
    168 
    169     walker->need_reset = FALSE;
    170 }
    171 
    172 uint32_t
    173 _pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
    174                                pixman_fixed_48_16_t      x)
    175 {
    176     float a, r, g, b;
    177     uint8_t a8, r8, g8, b8;
    178     uint32_t v;
    179     float y;
    180 
    181     if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
    182         gradient_walker_reset (walker, x);
    183 
    184     y = x * (1.0f / 65536.0f);
    185 
    186     a = walker->a_s * y + walker->a_b;
    187     r = a * (walker->r_s * y + walker->r_b);
    188     g = a * (walker->g_s * y + walker->g_b);
    189     b = a * (walker->b_s * y + walker->b_b);
    190 
    191     a8 = a + 0.5f;
    192     r8 = r + 0.5f;
    193     g8 = g + 0.5f;
    194     b8 = b + 0.5f;
    195 
    196     v = ((a8 << 24) & 0xff000000) |
    197         ((r8 << 16) & 0x00ff0000) |
    198         ((g8 <<  8) & 0x0000ff00) |
    199         ((b8 >>  0) & 0x000000ff);
    200 
    201     return v;
    202 }
    203