pixman-linear-gradient.c (7741B)
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 }