pixman-gradient-walker.c (7746B)
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 <pixman-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 int64_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/red/green/blue channels are scaled to be in [0, 1]. 126 * This ensures that after premultiplication all channels will 127 * be in the [0, 1] interval. 128 */ 129 la = (left_c->alpha * (1.0f/257.0f)); 130 lr = (left_c->red * (1.0f/257.0f)); 131 lg = (left_c->green * (1.0f/257.0f)); 132 lb = (left_c->blue * (1.0f/257.0f)); 133 134 ra = (right_c->alpha * (1.0f/257.0f)); 135 rr = (right_c->red * (1.0f/257.0f)); 136 rg = (right_c->green * (1.0f/257.0f)); 137 rb = (right_c->blue * (1.0f/257.0f)); 138 139 lx = left_x * (1.0f/65536.0f); 140 rx = right_x * (1.0f/65536.0f); 141 142 if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX) 143 { 144 walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f; 145 walker->a_b = (la + ra) / 510.0f; 146 walker->r_b = (lr + rr) / 510.0f; 147 walker->g_b = (lg + rg) / 510.0f; 148 walker->b_b = (lb + rb) / 510.0f; 149 } 150 else 151 { 152 float w_rec = 1.0f / (rx - lx); 153 154 walker->a_b = (la * rx - ra * lx) * w_rec * (1.0f/255.0f); 155 walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f); 156 walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f); 157 walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f); 158 159 walker->a_s = (ra - la) * w_rec * (1.0f/255.0f); 160 walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f); 161 walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f); 162 walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f); 163 } 164 165 walker->left_x = left_x; 166 walker->right_x = right_x; 167 168 walker->need_reset = FALSE; 169 } 170 171 static argb_t 172 pixman_gradient_walker_pixel_float (pixman_gradient_walker_t *walker, 173 pixman_fixed_48_16_t x) 174 { 175 argb_t f; 176 float y; 177 178 if (walker->need_reset || x < walker->left_x || x >= walker->right_x) 179 gradient_walker_reset (walker, x); 180 181 y = x * (1.0f / 65536.0f); 182 183 f.a = walker->a_s * y + walker->a_b; 184 f.r = f.a * (walker->r_s * y + walker->r_b); 185 f.g = f.a * (walker->g_s * y + walker->g_b); 186 f.b = f.a * (walker->b_s * y + walker->b_b); 187 188 return f; 189 } 190 191 static uint32_t 192 pixman_gradient_walker_pixel_32 (pixman_gradient_walker_t *walker, 193 pixman_fixed_48_16_t x) 194 { 195 argb_t f; 196 float y; 197 198 if (walker->need_reset || x < walker->left_x || x >= walker->right_x) 199 gradient_walker_reset (walker, x); 200 201 y = x * (1.0f / 65536.0f); 202 203 /* Instead of [0...1] for ARGB, we want [0...255], 204 * multiply alpha with 255 and the color channels 205 * also get multiplied by the alpha multiplier. 206 * 207 * We don't use pixman_contract_from_float because it causes a 2x 208 * slowdown to do so, and the values are already normalized, 209 * so we don't have to worry about values < 0.f or > 1.f 210 */ 211 f.a = 255.f * (walker->a_s * y + walker->a_b); 212 f.r = f.a * (walker->r_s * y + walker->r_b); 213 f.g = f.a * (walker->g_s * y + walker->g_b); 214 f.b = f.a * (walker->b_s * y + walker->b_b); 215 216 return (((uint32_t)(f.a + .5f) << 24) & 0xff000000) | 217 (((uint32_t)(f.r + .5f) << 16) & 0x00ff0000) | 218 (((uint32_t)(f.g + .5f) << 8) & 0x0000ff00) | 219 (((uint32_t)(f.b + .5f) >> 0) & 0x000000ff); 220 } 221 222 void 223 _pixman_gradient_walker_write_narrow (pixman_gradient_walker_t *walker, 224 pixman_fixed_48_16_t x, 225 uint32_t *buffer) 226 { 227 *buffer = pixman_gradient_walker_pixel_32 (walker, x); 228 } 229 230 void 231 _pixman_gradient_walker_write_wide (pixman_gradient_walker_t *walker, 232 pixman_fixed_48_16_t x, 233 uint32_t *buffer) 234 { 235 *(argb_t *)buffer = pixman_gradient_walker_pixel_float (walker, x); 236 } 237 238 void 239 _pixman_gradient_walker_fill_narrow (pixman_gradient_walker_t *walker, 240 pixman_fixed_48_16_t x, 241 uint32_t *buffer, 242 uint32_t *end) 243 { 244 register uint32_t color; 245 246 color = pixman_gradient_walker_pixel_32 (walker, x); 247 while (buffer < end) 248 *buffer++ = color; 249 } 250 251 void 252 _pixman_gradient_walker_fill_wide (pixman_gradient_walker_t *walker, 253 pixman_fixed_48_16_t x, 254 uint32_t *buffer, 255 uint32_t *end) 256 { 257 register argb_t color; 258 argb_t *buffer_wide = (argb_t *)buffer; 259 argb_t *end_wide = (argb_t *)end; 260 261 color = pixman_gradient_walker_pixel_float (walker, x); 262 while (buffer_wide < end_wide) 263 *buffer_wide++ = color; 264 }