gfx/cairo/libpixman/src/pixman-radial-gradient.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,727 @@
     1.4 +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
     1.5 +/*
     1.6 + *
     1.7 + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
     1.8 + * Copyright © 2000 SuSE, Inc.
     1.9 + *             2005 Lars Knoll & Zack Rusin, Trolltech
    1.10 + * Copyright © 2007 Red Hat, Inc.
    1.11 + *
    1.12 + *
    1.13 + * Permission to use, copy, modify, distribute, and sell this software and its
    1.14 + * documentation for any purpose is hereby granted without fee, provided that
    1.15 + * the above copyright notice appear in all copies and that both that
    1.16 + * copyright notice and this permission notice appear in supporting
    1.17 + * documentation, and that the name of Keith Packard not be used in
    1.18 + * advertising or publicity pertaining to distribution of the software without
    1.19 + * specific, written prior permission.  Keith Packard makes no
    1.20 + * representations about the suitability of this software for any purpose.  It
    1.21 + * is provided "as is" without express or implied warranty.
    1.22 + *
    1.23 + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
    1.24 + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
    1.25 + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
    1.26 + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    1.27 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    1.28 + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
    1.29 + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
    1.30 + * SOFTWARE.
    1.31 + */
    1.32 +
    1.33 +#ifdef HAVE_CONFIG_H
    1.34 +#include <config.h>
    1.35 +#endif
    1.36 +#include <stdlib.h>
    1.37 +#include <math.h>
    1.38 +#include "pixman-private.h"
    1.39 +
    1.40 +#include "pixman-dither.h"
    1.41 +
    1.42 +static inline pixman_fixed_32_32_t
    1.43 +dot (pixman_fixed_48_16_t x1,
    1.44 +     pixman_fixed_48_16_t y1,
    1.45 +     pixman_fixed_48_16_t z1,
    1.46 +     pixman_fixed_48_16_t x2,
    1.47 +     pixman_fixed_48_16_t y2,
    1.48 +     pixman_fixed_48_16_t z2)
    1.49 +{
    1.50 +    /*
    1.51 +     * Exact computation, assuming that the input values can
    1.52 +     * be represented as pixman_fixed_16_16_t
    1.53 +     */
    1.54 +    return x1 * x2 + y1 * y2 + z1 * z2;
    1.55 +}
    1.56 +
    1.57 +static inline double
    1.58 +fdot (double x1,
    1.59 +      double y1,
    1.60 +      double z1,
    1.61 +      double x2,
    1.62 +      double y2,
    1.63 +      double z2)
    1.64 +{
    1.65 +    /*
    1.66 +     * Error can be unbound in some special cases.
    1.67 +     * Using clever dot product algorithms (for example compensated
    1.68 +     * dot product) would improve this but make the code much less
    1.69 +     * obvious
    1.70 +     */
    1.71 +    return x1 * x2 + y1 * y2 + z1 * z2;
    1.72 +}
    1.73 +
    1.74 +static uint32_t
    1.75 +radial_compute_color (double                    a,
    1.76 +		      double                    b,
    1.77 +		      double                    c,
    1.78 +		      double                    inva,
    1.79 +		      double                    dr,
    1.80 +		      double                    mindr,
    1.81 +		      pixman_gradient_walker_t *walker,
    1.82 +		      pixman_repeat_t           repeat)
    1.83 +{
    1.84 +    /*
    1.85 +     * In this function error propagation can lead to bad results:
    1.86 +     *  - discr can have an unbound error (if b*b-a*c is very small),
    1.87 +     *    potentially making it the opposite sign of what it should have been
    1.88 +     *    (thus clearing a pixel that would have been colored or vice-versa)
    1.89 +     *    or propagating the error to sqrtdiscr;
    1.90 +     *    if discr has the wrong sign or b is very small, this can lead to bad
    1.91 +     *    results
    1.92 +     *
    1.93 +     *  - the algorithm used to compute the solutions of the quadratic
    1.94 +     *    equation is not numerically stable (but saves one division compared
    1.95 +     *    to the numerically stable one);
    1.96 +     *    this can be a problem if a*c is much smaller than b*b
    1.97 +     *
    1.98 +     *  - the above problems are worse if a is small (as inva becomes bigger)
    1.99 +     */
   1.100 +    double discr;
   1.101 +
   1.102 +    if (a == 0)
   1.103 +    {
   1.104 +	double t;
   1.105 +
   1.106 +	if (b == 0)
   1.107 +	    return 0;
   1.108 +
   1.109 +	t = pixman_fixed_1 / 2 * c / b;
   1.110 +	if (repeat == PIXMAN_REPEAT_NONE)
   1.111 +	{
   1.112 +	    if (0 <= t && t <= pixman_fixed_1)
   1.113 +		return _pixman_gradient_walker_pixel (walker, t);
   1.114 +	}
   1.115 +	else
   1.116 +	{
   1.117 +	    if (t * dr >= mindr)
   1.118 +		return _pixman_gradient_walker_pixel (walker, t);
   1.119 +	}
   1.120 +
   1.121 +	return 0;
   1.122 +    }
   1.123 +
   1.124 +    discr = fdot (b, a, 0, b, -c, 0);
   1.125 +    if (discr >= 0)
   1.126 +    {
   1.127 +	double sqrtdiscr, t0, t1;
   1.128 +
   1.129 +	sqrtdiscr = sqrt (discr);
   1.130 +	t0 = (b + sqrtdiscr) * inva;
   1.131 +	t1 = (b - sqrtdiscr) * inva;
   1.132 +
   1.133 +	/*
   1.134 +	 * The root that must be used is the biggest one that belongs
   1.135 +	 * to the valid range ([0,1] for PIXMAN_REPEAT_NONE, any
   1.136 +	 * solution that results in a positive radius otherwise).
   1.137 +	 *
   1.138 +	 * If a > 0, t0 is the biggest solution, so if it is valid, it
   1.139 +	 * is the correct result.
   1.140 +	 *
   1.141 +	 * If a < 0, only one of the solutions can be valid, so the
   1.142 +	 * order in which they are tested is not important.
   1.143 +	 */
   1.144 +	if (repeat == PIXMAN_REPEAT_NONE)
   1.145 +	{
   1.146 +	    if (0 <= t0 && t0 <= pixman_fixed_1)
   1.147 +		return _pixman_gradient_walker_pixel (walker, t0);
   1.148 +	    else if (0 <= t1 && t1 <= pixman_fixed_1)
   1.149 +		return _pixman_gradient_walker_pixel (walker, t1);
   1.150 +	}
   1.151 +	else
   1.152 +	{
   1.153 +	    if (t0 * dr >= mindr)
   1.154 +		return _pixman_gradient_walker_pixel (walker, t0);
   1.155 +	    else if (t1 * dr >= mindr)
   1.156 +		return _pixman_gradient_walker_pixel (walker, t1);
   1.157 +	}
   1.158 +    }
   1.159 +
   1.160 +    return 0;
   1.161 +}
   1.162 +
   1.163 +static uint32_t *
   1.164 +radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
   1.165 +{
   1.166 +    /*
   1.167 +     * Implementation of radial gradients following the PDF specification.
   1.168 +     * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
   1.169 +     * Manual (PDF 32000-1:2008 at the time of this writing).
   1.170 +     *
   1.171 +     * In the radial gradient problem we are given two circles (c₁,r₁) and
   1.172 +     * (c₂,r₂) that define the gradient itself.
   1.173 +     *
   1.174 +     * Mathematically the gradient can be defined as the family of circles
   1.175 +     *
   1.176 +     *     ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
   1.177 +     *
   1.178 +     * excluding those circles whose radius would be < 0. When a point
   1.179 +     * belongs to more than one circle, the one with a bigger t is the only
   1.180 +     * one that contributes to its color. When a point does not belong
   1.181 +     * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
   1.182 +     * Further limitations on the range of values for t are imposed when
   1.183 +     * the gradient is not repeated, namely t must belong to [0,1].
   1.184 +     *
   1.185 +     * The graphical result is the same as drawing the valid (radius > 0)
   1.186 +     * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
   1.187 +     * is not repeated) using SOURCE operator composition.
   1.188 +     *
   1.189 +     * It looks like a cone pointing towards the viewer if the ending circle
   1.190 +     * is smaller than the starting one, a cone pointing inside the page if
   1.191 +     * the starting circle is the smaller one and like a cylinder if they
   1.192 +     * have the same radius.
   1.193 +     *
   1.194 +     * What we actually do is, given the point whose color we are interested
   1.195 +     * in, compute the t values for that point, solving for t in:
   1.196 +     *
   1.197 +     *     length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
   1.198 +     *
   1.199 +     * Let's rewrite it in a simpler way, by defining some auxiliary
   1.200 +     * variables:
   1.201 +     *
   1.202 +     *     cd = c₂ - c₁
   1.203 +     *     pd = p - c₁
   1.204 +     *     dr = r₂ - r₁
   1.205 +     *     length(t·cd - pd) = r₁ + t·dr
   1.206 +     *
   1.207 +     * which actually means
   1.208 +     *
   1.209 +     *     hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
   1.210 +     *
   1.211 +     * or
   1.212 +     *
   1.213 +     *     ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
   1.214 +     *
   1.215 +     * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
   1.216 +     *
   1.217 +     *     (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
   1.218 +     *
   1.219 +     * where we can actually expand the squares and solve for t:
   1.220 +     *
   1.221 +     *     t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
   1.222 +     *       = r₁² + 2·r₁·t·dr + t²·dr²
   1.223 +     *
   1.224 +     *     (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
   1.225 +     *         (pdx² + pdy² - r₁²) = 0
   1.226 +     *
   1.227 +     *     A = cdx² + cdy² - dr²
   1.228 +     *     B = pdx·cdx + pdy·cdy + r₁·dr
   1.229 +     *     C = pdx² + pdy² - r₁²
   1.230 +     *     At² - 2Bt + C = 0
   1.231 +     *
   1.232 +     * The solutions (unless the equation degenerates because of A = 0) are:
   1.233 +     *
   1.234 +     *     t = (B ± ⎷(B² - A·C)) / A
   1.235 +     *
   1.236 +     * The solution we are going to prefer is the bigger one, unless the
   1.237 +     * radius associated to it is negative (or it falls outside the valid t
   1.238 +     * range).
   1.239 +     *
   1.240 +     * Additional observations (useful for optimizations):
   1.241 +     * A does not depend on p
   1.242 +     *
   1.243 +     * A < 0 <=> one of the two circles completely contains the other one
   1.244 +     *   <=> for every p, the radiuses associated with the two t solutions
   1.245 +     *       have opposite sign
   1.246 +     */
   1.247 +    pixman_image_t *image = iter->image;
   1.248 +    int x = iter->x;
   1.249 +    int y = iter->y;
   1.250 +    int width = iter->width;
   1.251 +    uint32_t *buffer = iter->buffer;
   1.252 +
   1.253 +    gradient_t *gradient = (gradient_t *)image;
   1.254 +    radial_gradient_t *radial = (radial_gradient_t *)image;
   1.255 +    uint32_t *end = buffer + width;
   1.256 +    pixman_gradient_walker_t walker;
   1.257 +    pixman_vector_t v, unit;
   1.258 +
   1.259 +    /* reference point is the center of the pixel */
   1.260 +    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
   1.261 +    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
   1.262 +    v.vector[2] = pixman_fixed_1;
   1.263 +
   1.264 +    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
   1.265 +
   1.266 +    if (image->common.transform)
   1.267 +    {
   1.268 +	if (!pixman_transform_point_3d (image->common.transform, &v))
   1.269 +	    return iter->buffer;
   1.270 +
   1.271 +	unit.vector[0] = image->common.transform->matrix[0][0];
   1.272 +	unit.vector[1] = image->common.transform->matrix[1][0];
   1.273 +	unit.vector[2] = image->common.transform->matrix[2][0];
   1.274 +    }
   1.275 +    else
   1.276 +    {
   1.277 +	unit.vector[0] = pixman_fixed_1;
   1.278 +	unit.vector[1] = 0;
   1.279 +	unit.vector[2] = 0;
   1.280 +    }
   1.281 +
   1.282 +    if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1)
   1.283 +    {
   1.284 +	/*
   1.285 +	 * Given:
   1.286 +	 *
   1.287 +	 * t = (B ± ⎷(B² - A·C)) / A
   1.288 +	 *
   1.289 +	 * where
   1.290 +	 *
   1.291 +	 * A = cdx² + cdy² - dr²
   1.292 +	 * B = pdx·cdx + pdy·cdy + r₁·dr
   1.293 +	 * C = pdx² + pdy² - r₁²
   1.294 +	 * det = B² - A·C
   1.295 +	 *
   1.296 +	 * Since we have an affine transformation, we know that (pdx, pdy)
   1.297 +	 * increase linearly with each pixel,
   1.298 +	 *
   1.299 +	 * pdx = pdx₀ + n·ux,
   1.300 +	 * pdy = pdy₀ + n·uy,
   1.301 +	 *
   1.302 +	 * we can then express B, C and det through multiple differentiation.
   1.303 +	 */
   1.304 +	pixman_fixed_32_32_t b, db, c, dc, ddc;
   1.305 +
   1.306 +	/* warning: this computation may overflow */
   1.307 +	v.vector[0] -= radial->c1.x;
   1.308 +	v.vector[1] -= radial->c1.y;
   1.309 +
   1.310 +	/*
   1.311 +	 * B and C are computed and updated exactly.
   1.312 +	 * If fdot was used instead of dot, in the worst case it would
   1.313 +	 * lose 11 bits of precision in each of the multiplication and
   1.314 +	 * summing up would zero out all the bit that were preserved,
   1.315 +	 * thus making the result 0 instead of the correct one.
   1.316 +	 * This would mean a worst case of unbound relative error or
   1.317 +	 * about 2^10 absolute error
   1.318 +	 */
   1.319 +	b = dot (v.vector[0], v.vector[1], radial->c1.radius,
   1.320 +		 radial->delta.x, radial->delta.y, radial->delta.radius);
   1.321 +	db = dot (unit.vector[0], unit.vector[1], 0,
   1.322 +		  radial->delta.x, radial->delta.y, 0);
   1.323 +
   1.324 +	c = dot (v.vector[0], v.vector[1],
   1.325 +		 -((pixman_fixed_48_16_t) radial->c1.radius),
   1.326 +		 v.vector[0], v.vector[1], radial->c1.radius);
   1.327 +	dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0],
   1.328 +		  2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1],
   1.329 +		  0,
   1.330 +		  unit.vector[0], unit.vector[1], 0);
   1.331 +	ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
   1.332 +		       unit.vector[0], unit.vector[1], 0);
   1.333 +
   1.334 +	while (buffer < end)
   1.335 +	{
   1.336 +	    if (!mask || *mask++)
   1.337 +	    {
   1.338 +		*buffer = radial_compute_color (radial->a, b, c,
   1.339 +						radial->inva,
   1.340 +						radial->delta.radius,
   1.341 +						radial->mindr,
   1.342 +						&walker,
   1.343 +						image->common.repeat);
   1.344 +	    }
   1.345 +
   1.346 +	    b += db;
   1.347 +	    c += dc;
   1.348 +	    dc += ddc;
   1.349 +	    ++buffer;
   1.350 +	}
   1.351 +    }
   1.352 +    else
   1.353 +    {
   1.354 +	/* projective */
   1.355 +	/* Warning:
   1.356 +	 * error propagation guarantees are much looser than in the affine case
   1.357 +	 */
   1.358 +	while (buffer < end)
   1.359 +	{
   1.360 +	    if (!mask || *mask++)
   1.361 +	    {
   1.362 +		if (v.vector[2] != 0)
   1.363 +		{
   1.364 +		    double pdx, pdy, invv2, b, c;
   1.365 +
   1.366 +		    invv2 = 1. * pixman_fixed_1 / v.vector[2];
   1.367 +
   1.368 +		    pdx = v.vector[0] * invv2 - radial->c1.x;
   1.369 +		    /*    / pixman_fixed_1 */
   1.370 +
   1.371 +		    pdy = v.vector[1] * invv2 - radial->c1.y;
   1.372 +		    /*    / pixman_fixed_1 */
   1.373 +
   1.374 +		    b = fdot (pdx, pdy, radial->c1.radius,
   1.375 +			      radial->delta.x, radial->delta.y,
   1.376 +			      radial->delta.radius);
   1.377 +		    /*  / pixman_fixed_1 / pixman_fixed_1 */
   1.378 +
   1.379 +		    c = fdot (pdx, pdy, -radial->c1.radius,
   1.380 +			      pdx, pdy, radial->c1.radius);
   1.381 +		    /*  / pixman_fixed_1 / pixman_fixed_1 */
   1.382 +
   1.383 +		    *buffer = radial_compute_color (radial->a, b, c,
   1.384 +						    radial->inva,
   1.385 +						    radial->delta.radius,
   1.386 +						    radial->mindr,
   1.387 +						    &walker,
   1.388 +						    image->common.repeat);
   1.389 +		}
   1.390 +		else
   1.391 +		{
   1.392 +		    *buffer = 0;
   1.393 +		}
   1.394 +	    }
   1.395 +
   1.396 +	    ++buffer;
   1.397 +
   1.398 +	    v.vector[0] += unit.vector[0];
   1.399 +	    v.vector[1] += unit.vector[1];
   1.400 +	    v.vector[2] += unit.vector[2];
   1.401 +	}
   1.402 +    }
   1.403 +
   1.404 +    iter->y++;
   1.405 +    return iter->buffer;
   1.406 +}
   1.407 +
   1.408 +static uint32_t *
   1.409 +radial_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
   1.410 +{
   1.411 +    /*
   1.412 +     * Implementation of radial gradients following the PDF specification.
   1.413 +     * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
   1.414 +     * Manual (PDF 32000-1:2008 at the time of this writing).
   1.415 +     *
   1.416 +     * In the radial gradient problem we are given two circles (c₁,r₁) and
   1.417 +     * (c₂,r₂) that define the gradient itself.
   1.418 +     *
   1.419 +     * Mathematically the gradient can be defined as the family of circles
   1.420 +     *
   1.421 +     *     ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
   1.422 +     *
   1.423 +     * excluding those circles whose radius would be < 0. When a point
   1.424 +     * belongs to more than one circle, the one with a bigger t is the only
   1.425 +     * one that contributes to its color. When a point does not belong
   1.426 +     * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
   1.427 +     * Further limitations on the range of values for t are imposed when
   1.428 +     * the gradient is not repeated, namely t must belong to [0,1].
   1.429 +     *
   1.430 +     * The graphical result is the same as drawing the valid (radius > 0)
   1.431 +     * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
   1.432 +     * is not repeated) using SOURCE operator composition.
   1.433 +     *
   1.434 +     * It looks like a cone pointing towards the viewer if the ending circle
   1.435 +     * is smaller than the starting one, a cone pointing inside the page if
   1.436 +     * the starting circle is the smaller one and like a cylinder if they
   1.437 +     * have the same radius.
   1.438 +     *
   1.439 +     * What we actually do is, given the point whose color we are interested
   1.440 +     * in, compute the t values for that point, solving for t in:
   1.441 +     *
   1.442 +     *     length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
   1.443 +     *
   1.444 +     * Let's rewrite it in a simpler way, by defining some auxiliary
   1.445 +     * variables:
   1.446 +     *
   1.447 +     *     cd = c₂ - c₁
   1.448 +     *     pd = p - c₁
   1.449 +     *     dr = r₂ - r₁
   1.450 +     *     length(t·cd - pd) = r₁ + t·dr
   1.451 +     *
   1.452 +     * which actually means
   1.453 +     *
   1.454 +     *     hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
   1.455 +     *
   1.456 +     * or
   1.457 +     *
   1.458 +     *     ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
   1.459 +     *
   1.460 +     * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
   1.461 +     *
   1.462 +     *     (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
   1.463 +     *
   1.464 +     * where we can actually expand the squares and solve for t:
   1.465 +     *
   1.466 +     *     t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
   1.467 +     *       = r₁² + 2·r₁·t·dr + t²·dr²
   1.468 +     *
   1.469 +     *     (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
   1.470 +     *         (pdx² + pdy² - r₁²) = 0
   1.471 +     *
   1.472 +     *     A = cdx² + cdy² - dr²
   1.473 +     *     B = pdx·cdx + pdy·cdy + r₁·dr
   1.474 +     *     C = pdx² + pdy² - r₁²
   1.475 +     *     At² - 2Bt + C = 0
   1.476 +     *
   1.477 +     * The solutions (unless the equation degenerates because of A = 0) are:
   1.478 +     *
   1.479 +     *     t = (B ± ⎷(B² - A·C)) / A
   1.480 +     *
   1.481 +     * The solution we are going to prefer is the bigger one, unless the
   1.482 +     * radius associated to it is negative (or it falls outside the valid t
   1.483 +     * range).
   1.484 +     *
   1.485 +     * Additional observations (useful for optimizations):
   1.486 +     * A does not depend on p
   1.487 +     *
   1.488 +     * A < 0 <=> one of the two circles completely contains the other one
   1.489 +     *   <=> for every p, the radiuses associated with the two t solutions
   1.490 +     *       have opposite sign
   1.491 +     */
   1.492 +    pixman_image_t *image = iter->image;
   1.493 +    int x = iter->x;
   1.494 +    int y = iter->y;
   1.495 +    int width = iter->width;
   1.496 +    uint16_t *buffer = iter->buffer;
   1.497 +    pixman_bool_t toggle = ((x ^ y) & 1);
   1.498 +
   1.499 +    gradient_t *gradient = (gradient_t *)image;
   1.500 +    radial_gradient_t *radial = (radial_gradient_t *)image;
   1.501 +    uint16_t *end = buffer + width;
   1.502 +    pixman_gradient_walker_t walker;
   1.503 +    pixman_vector_t v, unit;
   1.504 +
   1.505 +    /* reference point is the center of the pixel */
   1.506 +    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
   1.507 +    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
   1.508 +    v.vector[2] = pixman_fixed_1;
   1.509 +
   1.510 +    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
   1.511 +
   1.512 +    if (image->common.transform)
   1.513 +    {
   1.514 +	if (!pixman_transform_point_3d (image->common.transform, &v))
   1.515 +	    return iter->buffer;
   1.516 +
   1.517 +	unit.vector[0] = image->common.transform->matrix[0][0];
   1.518 +	unit.vector[1] = image->common.transform->matrix[1][0];
   1.519 +	unit.vector[2] = image->common.transform->matrix[2][0];
   1.520 +    }
   1.521 +    else
   1.522 +    {
   1.523 +	unit.vector[0] = pixman_fixed_1;
   1.524 +	unit.vector[1] = 0;
   1.525 +	unit.vector[2] = 0;
   1.526 +    }
   1.527 +
   1.528 +    if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1)
   1.529 +    {
   1.530 +	/*
   1.531 +	 * Given:
   1.532 +	 *
   1.533 +	 * t = (B ± ⎷(B² - A·C)) / A
   1.534 +	 *
   1.535 +	 * where
   1.536 +	 *
   1.537 +	 * A = cdx² + cdy² - dr²
   1.538 +	 * B = pdx·cdx + pdy·cdy + r₁·dr
   1.539 +	 * C = pdx² + pdy² - r₁²
   1.540 +	 * det = B² - A·C
   1.541 +	 *
   1.542 +	 * Since we have an affine transformation, we know that (pdx, pdy)
   1.543 +	 * increase linearly with each pixel,
   1.544 +	 *
   1.545 +	 * pdx = pdx₀ + n·ux,
   1.546 +	 * pdy = pdy₀ + n·uy,
   1.547 +	 *
   1.548 +	 * we can then express B, C and det through multiple differentiation.
   1.549 +	 */
   1.550 +	pixman_fixed_32_32_t b, db, c, dc, ddc;
   1.551 +
   1.552 +	/* warning: this computation may overflow */
   1.553 +	v.vector[0] -= radial->c1.x;
   1.554 +	v.vector[1] -= radial->c1.y;
   1.555 +
   1.556 +	/*
   1.557 +	 * B and C are computed and updated exactly.
   1.558 +	 * If fdot was used instead of dot, in the worst case it would
   1.559 +	 * lose 11 bits of precision in each of the multiplication and
   1.560 +	 * summing up would zero out all the bit that were preserved,
   1.561 +	 * thus making the result 0 instead of the correct one.
   1.562 +	 * This would mean a worst case of unbound relative error or
   1.563 +	 * about 2^10 absolute error
   1.564 +	 */
   1.565 +	b = dot (v.vector[0], v.vector[1], radial->c1.radius,
   1.566 +		 radial->delta.x, radial->delta.y, radial->delta.radius);
   1.567 +	db = dot (unit.vector[0], unit.vector[1], 0,
   1.568 +		  radial->delta.x, radial->delta.y, 0);
   1.569 +
   1.570 +	c = dot (v.vector[0], v.vector[1],
   1.571 +		 -((pixman_fixed_48_16_t) radial->c1.radius),
   1.572 +		 v.vector[0], v.vector[1], radial->c1.radius);
   1.573 +	dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0],
   1.574 +		  2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1],
   1.575 +		  0,
   1.576 +		  unit.vector[0], unit.vector[1], 0);
   1.577 +	ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
   1.578 +		       unit.vector[0], unit.vector[1], 0);
   1.579 +
   1.580 +	while (buffer < end)
   1.581 +	{
   1.582 +	    if (!mask || *mask++)
   1.583 +	    {
   1.584 +		*buffer = dither_8888_to_0565(
   1.585 +			  radial_compute_color (radial->a, b, c,
   1.586 +						radial->inva,
   1.587 +						radial->delta.radius,
   1.588 +						radial->mindr,
   1.589 +						&walker,
   1.590 +						image->common.repeat),
   1.591 +			  toggle);
   1.592 +	    }
   1.593 +
   1.594 +	    toggle ^= 1;
   1.595 +	    b += db;
   1.596 +	    c += dc;
   1.597 +	    dc += ddc;
   1.598 +	    ++buffer;
   1.599 +	}
   1.600 +    }
   1.601 +    else
   1.602 +    {
   1.603 +	/* projective */
   1.604 +	/* Warning:
   1.605 +	 * error propagation guarantees are much looser than in the affine case
   1.606 +	 */
   1.607 +	while (buffer < end)
   1.608 +	{
   1.609 +	    if (!mask || *mask++)
   1.610 +	    {
   1.611 +		if (v.vector[2] != 0)
   1.612 +		{
   1.613 +		    double pdx, pdy, invv2, b, c;
   1.614 +
   1.615 +		    invv2 = 1. * pixman_fixed_1 / v.vector[2];
   1.616 +
   1.617 +		    pdx = v.vector[0] * invv2 - radial->c1.x;
   1.618 +		    /*    / pixman_fixed_1 */
   1.619 +
   1.620 +		    pdy = v.vector[1] * invv2 - radial->c1.y;
   1.621 +		    /*    / pixman_fixed_1 */
   1.622 +
   1.623 +		    b = fdot (pdx, pdy, radial->c1.radius,
   1.624 +			      radial->delta.x, radial->delta.y,
   1.625 +			      radial->delta.radius);
   1.626 +		    /*  / pixman_fixed_1 / pixman_fixed_1 */
   1.627 +
   1.628 +		    c = fdot (pdx, pdy, -radial->c1.radius,
   1.629 +			      pdx, pdy, radial->c1.radius);
   1.630 +		    /*  / pixman_fixed_1 / pixman_fixed_1 */
   1.631 +
   1.632 +		    *buffer = dither_8888_to_0565 (
   1.633 +			      radial_compute_color (radial->a, b, c,
   1.634 +						    radial->inva,
   1.635 +						    radial->delta.radius,
   1.636 +						    radial->mindr,
   1.637 +						    &walker,
   1.638 +						    image->common.repeat),
   1.639 +			      toggle);
   1.640 +		}
   1.641 +		else
   1.642 +		{
   1.643 +		    *buffer = 0;
   1.644 +		}
   1.645 +	    }
   1.646 +
   1.647 +	    ++buffer;
   1.648 +	    toggle ^= 1;
   1.649 +
   1.650 +	    v.vector[0] += unit.vector[0];
   1.651 +	    v.vector[1] += unit.vector[1];
   1.652 +	    v.vector[2] += unit.vector[2];
   1.653 +	}
   1.654 +    }
   1.655 +
   1.656 +    iter->y++;
   1.657 +    return iter->buffer;
   1.658 +}
   1.659 +static uint32_t *
   1.660 +radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
   1.661 +{
   1.662 +    uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
   1.663 +
   1.664 +    pixman_expand_to_float (
   1.665 +	(argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
   1.666 +
   1.667 +    return buffer;
   1.668 +}
   1.669 +
   1.670 +void
   1.671 +_pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
   1.672 +{
   1.673 +    if (iter->iter_flags & ITER_16)
   1.674 +	iter->get_scanline = radial_get_scanline_16;
   1.675 +    else if (iter->iter_flags & ITER_NARROW)
   1.676 +	iter->get_scanline = radial_get_scanline_narrow;
   1.677 +    else
   1.678 +	iter->get_scanline = radial_get_scanline_wide;
   1.679 +}
   1.680 +
   1.681 +
   1.682 +PIXMAN_EXPORT pixman_image_t *
   1.683 +pixman_image_create_radial_gradient (const pixman_point_fixed_t *  inner,
   1.684 +                                     const pixman_point_fixed_t *  outer,
   1.685 +                                     pixman_fixed_t                inner_radius,
   1.686 +                                     pixman_fixed_t                outer_radius,
   1.687 +                                     const pixman_gradient_stop_t *stops,
   1.688 +                                     int                           n_stops)
   1.689 +{
   1.690 +    pixman_image_t *image;
   1.691 +    radial_gradient_t *radial;
   1.692 +
   1.693 +    image = _pixman_image_allocate ();
   1.694 +
   1.695 +    if (!image)
   1.696 +	return NULL;
   1.697 +
   1.698 +    radial = &image->radial;
   1.699 +
   1.700 +    if (!_pixman_init_gradient (&radial->common, stops, n_stops))
   1.701 +    {
   1.702 +	free (image);
   1.703 +	return NULL;
   1.704 +    }
   1.705 +
   1.706 +    image->type = RADIAL;
   1.707 +
   1.708 +    radial->c1.x = inner->x;
   1.709 +    radial->c1.y = inner->y;
   1.710 +    radial->c1.radius = inner_radius;
   1.711 +    radial->c2.x = outer->x;
   1.712 +    radial->c2.y = outer->y;
   1.713 +    radial->c2.radius = outer_radius;
   1.714 +
   1.715 +    /* warning: this computations may overflow */
   1.716 +    radial->delta.x = radial->c2.x - radial->c1.x;
   1.717 +    radial->delta.y = radial->c2.y - radial->c1.y;
   1.718 +    radial->delta.radius = radial->c2.radius - radial->c1.radius;
   1.719 +
   1.720 +    /* computed exactly, then cast to double -> every bit of the double
   1.721 +       representation is correct (53 bits) */
   1.722 +    radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius,
   1.723 +		     radial->delta.x, radial->delta.y, radial->delta.radius);
   1.724 +    if (radial->a != 0)
   1.725 +	radial->inva = 1. * pixman_fixed_1 / radial->a;
   1.726 +
   1.727 +    radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius;
   1.728 +
   1.729 +    return image;
   1.730 +}

mercurial