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