michael@0: /* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ michael@0: /* michael@0: * michael@0: * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. michael@0: * Copyright © 2000 SuSE, Inc. michael@0: * 2005 Lars Knoll & Zack Rusin, Trolltech michael@0: * Copyright © 2007 Red Hat, Inc. michael@0: * michael@0: * michael@0: * Permission to use, copy, modify, distribute, and sell this software and its michael@0: * documentation for any purpose is hereby granted without fee, provided that michael@0: * the above copyright notice appear in all copies and that both that michael@0: * copyright notice and this permission notice appear in supporting michael@0: * documentation, and that the name of Keith Packard not be used in michael@0: * advertising or publicity pertaining to distribution of the software without michael@0: * specific, written prior permission. Keith Packard makes no michael@0: * representations about the suitability of this software for any purpose. It michael@0: * is provided "as is" without express or implied warranty. michael@0: * michael@0: * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS michael@0: * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND michael@0: * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY michael@0: * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES michael@0: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN michael@0: * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING michael@0: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS michael@0: * SOFTWARE. michael@0: */ michael@0: michael@0: #ifdef HAVE_CONFIG_H michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include "pixman-private.h" michael@0: michael@0: #include "pixman-dither.h" michael@0: michael@0: static inline pixman_fixed_32_32_t michael@0: dot (pixman_fixed_48_16_t x1, michael@0: pixman_fixed_48_16_t y1, michael@0: pixman_fixed_48_16_t z1, michael@0: pixman_fixed_48_16_t x2, michael@0: pixman_fixed_48_16_t y2, michael@0: pixman_fixed_48_16_t z2) michael@0: { michael@0: /* michael@0: * Exact computation, assuming that the input values can michael@0: * be represented as pixman_fixed_16_16_t michael@0: */ michael@0: return x1 * x2 + y1 * y2 + z1 * z2; michael@0: } michael@0: michael@0: static inline double michael@0: fdot (double x1, michael@0: double y1, michael@0: double z1, michael@0: double x2, michael@0: double y2, michael@0: double z2) michael@0: { michael@0: /* michael@0: * Error can be unbound in some special cases. michael@0: * Using clever dot product algorithms (for example compensated michael@0: * dot product) would improve this but make the code much less michael@0: * obvious michael@0: */ michael@0: return x1 * x2 + y1 * y2 + z1 * z2; michael@0: } michael@0: michael@0: static uint32_t michael@0: radial_compute_color (double a, michael@0: double b, michael@0: double c, michael@0: double inva, michael@0: double dr, michael@0: double mindr, michael@0: pixman_gradient_walker_t *walker, michael@0: pixman_repeat_t repeat) michael@0: { michael@0: /* michael@0: * In this function error propagation can lead to bad results: michael@0: * - discr can have an unbound error (if b*b-a*c is very small), michael@0: * potentially making it the opposite sign of what it should have been michael@0: * (thus clearing a pixel that would have been colored or vice-versa) michael@0: * or propagating the error to sqrtdiscr; michael@0: * if discr has the wrong sign or b is very small, this can lead to bad michael@0: * results michael@0: * michael@0: * - the algorithm used to compute the solutions of the quadratic michael@0: * equation is not numerically stable (but saves one division compared michael@0: * to the numerically stable one); michael@0: * this can be a problem if a*c is much smaller than b*b michael@0: * michael@0: * - the above problems are worse if a is small (as inva becomes bigger) michael@0: */ michael@0: double discr; michael@0: michael@0: if (a == 0) michael@0: { michael@0: double t; michael@0: michael@0: if (b == 0) michael@0: return 0; michael@0: michael@0: t = pixman_fixed_1 / 2 * c / b; michael@0: if (repeat == PIXMAN_REPEAT_NONE) michael@0: { michael@0: if (0 <= t && t <= pixman_fixed_1) michael@0: return _pixman_gradient_walker_pixel (walker, t); michael@0: } michael@0: else michael@0: { michael@0: if (t * dr >= mindr) michael@0: return _pixman_gradient_walker_pixel (walker, t); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: discr = fdot (b, a, 0, b, -c, 0); michael@0: if (discr >= 0) michael@0: { michael@0: double sqrtdiscr, t0, t1; michael@0: michael@0: sqrtdiscr = sqrt (discr); michael@0: t0 = (b + sqrtdiscr) * inva; michael@0: t1 = (b - sqrtdiscr) * inva; michael@0: michael@0: /* michael@0: * The root that must be used is the biggest one that belongs michael@0: * to the valid range ([0,1] for PIXMAN_REPEAT_NONE, any michael@0: * solution that results in a positive radius otherwise). michael@0: * michael@0: * If a > 0, t0 is the biggest solution, so if it is valid, it michael@0: * is the correct result. michael@0: * michael@0: * If a < 0, only one of the solutions can be valid, so the michael@0: * order in which they are tested is not important. michael@0: */ michael@0: if (repeat == PIXMAN_REPEAT_NONE) michael@0: { michael@0: if (0 <= t0 && t0 <= pixman_fixed_1) michael@0: return _pixman_gradient_walker_pixel (walker, t0); michael@0: else if (0 <= t1 && t1 <= pixman_fixed_1) michael@0: return _pixman_gradient_walker_pixel (walker, t1); michael@0: } michael@0: else michael@0: { michael@0: if (t0 * dr >= mindr) michael@0: return _pixman_gradient_walker_pixel (walker, t0); michael@0: else if (t1 * dr >= mindr) michael@0: return _pixman_gradient_walker_pixel (walker, t1); michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static uint32_t * michael@0: radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) michael@0: { michael@0: /* michael@0: * Implementation of radial gradients following the PDF specification. michael@0: * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference michael@0: * Manual (PDF 32000-1:2008 at the time of this writing). michael@0: * michael@0: * In the radial gradient problem we are given two circles (c₁,r₁) and michael@0: * (c₂,r₂) that define the gradient itself. michael@0: * michael@0: * Mathematically the gradient can be defined as the family of circles michael@0: * michael@0: * ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂) michael@0: * michael@0: * excluding those circles whose radius would be < 0. When a point michael@0: * belongs to more than one circle, the one with a bigger t is the only michael@0: * one that contributes to its color. When a point does not belong michael@0: * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0). michael@0: * Further limitations on the range of values for t are imposed when michael@0: * the gradient is not repeated, namely t must belong to [0,1]. michael@0: * michael@0: * The graphical result is the same as drawing the valid (radius > 0) michael@0: * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient michael@0: * is not repeated) using SOURCE operator composition. michael@0: * michael@0: * It looks like a cone pointing towards the viewer if the ending circle michael@0: * is smaller than the starting one, a cone pointing inside the page if michael@0: * the starting circle is the smaller one and like a cylinder if they michael@0: * have the same radius. michael@0: * michael@0: * What we actually do is, given the point whose color we are interested michael@0: * in, compute the t values for that point, solving for t in: michael@0: * michael@0: * length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂ michael@0: * michael@0: * Let's rewrite it in a simpler way, by defining some auxiliary michael@0: * variables: michael@0: * michael@0: * cd = c₂ - c₁ michael@0: * pd = p - c₁ michael@0: * dr = r₂ - r₁ michael@0: * length(t·cd - pd) = r₁ + t·dr michael@0: * michael@0: * which actually means michael@0: * michael@0: * hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr michael@0: * michael@0: * or michael@0: * michael@0: * ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr. michael@0: * michael@0: * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes: michael@0: * michael@0: * (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)² michael@0: * michael@0: * where we can actually expand the squares and solve for t: michael@0: * michael@0: * t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² = michael@0: * = r₁² + 2·r₁·t·dr + t²·dr² michael@0: * michael@0: * (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t + michael@0: * (pdx² + pdy² - r₁²) = 0 michael@0: * michael@0: * A = cdx² + cdy² - dr² michael@0: * B = pdx·cdx + pdy·cdy + r₁·dr michael@0: * C = pdx² + pdy² - r₁² michael@0: * At² - 2Bt + C = 0 michael@0: * michael@0: * The solutions (unless the equation degenerates because of A = 0) are: michael@0: * michael@0: * t = (B ± ⎷(B² - A·C)) / A michael@0: * michael@0: * The solution we are going to prefer is the bigger one, unless the michael@0: * radius associated to it is negative (or it falls outside the valid t michael@0: * range). michael@0: * michael@0: * Additional observations (useful for optimizations): michael@0: * A does not depend on p michael@0: * michael@0: * A < 0 <=> one of the two circles completely contains the other one michael@0: * <=> for every p, the radiuses associated with the two t solutions michael@0: * have opposite sign michael@0: */ michael@0: pixman_image_t *image = iter->image; michael@0: int x = iter->x; michael@0: int y = iter->y; michael@0: int width = iter->width; michael@0: uint32_t *buffer = iter->buffer; michael@0: michael@0: gradient_t *gradient = (gradient_t *)image; michael@0: radial_gradient_t *radial = (radial_gradient_t *)image; michael@0: uint32_t *end = buffer + width; michael@0: pixman_gradient_walker_t walker; michael@0: pixman_vector_t v, unit; michael@0: michael@0: /* reference point is the center of the pixel */ michael@0: v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; michael@0: v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; michael@0: v.vector[2] = pixman_fixed_1; michael@0: michael@0: _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); michael@0: michael@0: if (image->common.transform) michael@0: { michael@0: if (!pixman_transform_point_3d (image->common.transform, &v)) michael@0: return iter->buffer; michael@0: michael@0: unit.vector[0] = image->common.transform->matrix[0][0]; michael@0: unit.vector[1] = image->common.transform->matrix[1][0]; michael@0: unit.vector[2] = image->common.transform->matrix[2][0]; michael@0: } michael@0: else michael@0: { michael@0: unit.vector[0] = pixman_fixed_1; michael@0: unit.vector[1] = 0; michael@0: unit.vector[2] = 0; michael@0: } michael@0: michael@0: if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1) michael@0: { michael@0: /* michael@0: * Given: michael@0: * michael@0: * t = (B ± ⎷(B² - A·C)) / A michael@0: * michael@0: * where michael@0: * michael@0: * A = cdx² + cdy² - dr² michael@0: * B = pdx·cdx + pdy·cdy + r₁·dr michael@0: * C = pdx² + pdy² - r₁² michael@0: * det = B² - A·C michael@0: * michael@0: * Since we have an affine transformation, we know that (pdx, pdy) michael@0: * increase linearly with each pixel, michael@0: * michael@0: * pdx = pdx₀ + n·ux, michael@0: * pdy = pdy₀ + n·uy, michael@0: * michael@0: * we can then express B, C and det through multiple differentiation. michael@0: */ michael@0: pixman_fixed_32_32_t b, db, c, dc, ddc; michael@0: michael@0: /* warning: this computation may overflow */ michael@0: v.vector[0] -= radial->c1.x; michael@0: v.vector[1] -= radial->c1.y; michael@0: michael@0: /* michael@0: * B and C are computed and updated exactly. michael@0: * If fdot was used instead of dot, in the worst case it would michael@0: * lose 11 bits of precision in each of the multiplication and michael@0: * summing up would zero out all the bit that were preserved, michael@0: * thus making the result 0 instead of the correct one. michael@0: * This would mean a worst case of unbound relative error or michael@0: * about 2^10 absolute error michael@0: */ michael@0: b = dot (v.vector[0], v.vector[1], radial->c1.radius, michael@0: radial->delta.x, radial->delta.y, radial->delta.radius); michael@0: db = dot (unit.vector[0], unit.vector[1], 0, michael@0: radial->delta.x, radial->delta.y, 0); michael@0: michael@0: c = dot (v.vector[0], v.vector[1], michael@0: -((pixman_fixed_48_16_t) radial->c1.radius), michael@0: v.vector[0], v.vector[1], radial->c1.radius); michael@0: dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0], michael@0: 2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1], michael@0: 0, michael@0: unit.vector[0], unit.vector[1], 0); michael@0: ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, michael@0: unit.vector[0], unit.vector[1], 0); michael@0: michael@0: while (buffer < end) michael@0: { michael@0: if (!mask || *mask++) michael@0: { michael@0: *buffer = radial_compute_color (radial->a, b, c, michael@0: radial->inva, michael@0: radial->delta.radius, michael@0: radial->mindr, michael@0: &walker, michael@0: image->common.repeat); michael@0: } michael@0: michael@0: b += db; michael@0: c += dc; michael@0: dc += ddc; michael@0: ++buffer; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* projective */ michael@0: /* Warning: michael@0: * error propagation guarantees are much looser than in the affine case michael@0: */ michael@0: while (buffer < end) michael@0: { michael@0: if (!mask || *mask++) michael@0: { michael@0: if (v.vector[2] != 0) michael@0: { michael@0: double pdx, pdy, invv2, b, c; michael@0: michael@0: invv2 = 1. * pixman_fixed_1 / v.vector[2]; michael@0: michael@0: pdx = v.vector[0] * invv2 - radial->c1.x; michael@0: /* / pixman_fixed_1 */ michael@0: michael@0: pdy = v.vector[1] * invv2 - radial->c1.y; michael@0: /* / pixman_fixed_1 */ michael@0: michael@0: b = fdot (pdx, pdy, radial->c1.radius, michael@0: radial->delta.x, radial->delta.y, michael@0: radial->delta.radius); michael@0: /* / pixman_fixed_1 / pixman_fixed_1 */ michael@0: michael@0: c = fdot (pdx, pdy, -radial->c1.radius, michael@0: pdx, pdy, radial->c1.radius); michael@0: /* / pixman_fixed_1 / pixman_fixed_1 */ michael@0: michael@0: *buffer = radial_compute_color (radial->a, b, c, michael@0: radial->inva, michael@0: radial->delta.radius, michael@0: radial->mindr, michael@0: &walker, michael@0: image->common.repeat); michael@0: } michael@0: else michael@0: { michael@0: *buffer = 0; michael@0: } michael@0: } michael@0: michael@0: ++buffer; michael@0: michael@0: v.vector[0] += unit.vector[0]; michael@0: v.vector[1] += unit.vector[1]; michael@0: v.vector[2] += unit.vector[2]; michael@0: } michael@0: } michael@0: michael@0: iter->y++; michael@0: return iter->buffer; michael@0: } michael@0: michael@0: static uint32_t * michael@0: radial_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask) michael@0: { michael@0: /* michael@0: * Implementation of radial gradients following the PDF specification. michael@0: * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference michael@0: * Manual (PDF 32000-1:2008 at the time of this writing). michael@0: * michael@0: * In the radial gradient problem we are given two circles (c₁,r₁) and michael@0: * (c₂,r₂) that define the gradient itself. michael@0: * michael@0: * Mathematically the gradient can be defined as the family of circles michael@0: * michael@0: * ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂) michael@0: * michael@0: * excluding those circles whose radius would be < 0. When a point michael@0: * belongs to more than one circle, the one with a bigger t is the only michael@0: * one that contributes to its color. When a point does not belong michael@0: * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0). michael@0: * Further limitations on the range of values for t are imposed when michael@0: * the gradient is not repeated, namely t must belong to [0,1]. michael@0: * michael@0: * The graphical result is the same as drawing the valid (radius > 0) michael@0: * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient michael@0: * is not repeated) using SOURCE operator composition. michael@0: * michael@0: * It looks like a cone pointing towards the viewer if the ending circle michael@0: * is smaller than the starting one, a cone pointing inside the page if michael@0: * the starting circle is the smaller one and like a cylinder if they michael@0: * have the same radius. michael@0: * michael@0: * What we actually do is, given the point whose color we are interested michael@0: * in, compute the t values for that point, solving for t in: michael@0: * michael@0: * length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂ michael@0: * michael@0: * Let's rewrite it in a simpler way, by defining some auxiliary michael@0: * variables: michael@0: * michael@0: * cd = c₂ - c₁ michael@0: * pd = p - c₁ michael@0: * dr = r₂ - r₁ michael@0: * length(t·cd - pd) = r₁ + t·dr michael@0: * michael@0: * which actually means michael@0: * michael@0: * hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr michael@0: * michael@0: * or michael@0: * michael@0: * ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr. michael@0: * michael@0: * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes: michael@0: * michael@0: * (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)² michael@0: * michael@0: * where we can actually expand the squares and solve for t: michael@0: * michael@0: * t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² = michael@0: * = r₁² + 2·r₁·t·dr + t²·dr² michael@0: * michael@0: * (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t + michael@0: * (pdx² + pdy² - r₁²) = 0 michael@0: * michael@0: * A = cdx² + cdy² - dr² michael@0: * B = pdx·cdx + pdy·cdy + r₁·dr michael@0: * C = pdx² + pdy² - r₁² michael@0: * At² - 2Bt + C = 0 michael@0: * michael@0: * The solutions (unless the equation degenerates because of A = 0) are: michael@0: * michael@0: * t = (B ± ⎷(B² - A·C)) / A michael@0: * michael@0: * The solution we are going to prefer is the bigger one, unless the michael@0: * radius associated to it is negative (or it falls outside the valid t michael@0: * range). michael@0: * michael@0: * Additional observations (useful for optimizations): michael@0: * A does not depend on p michael@0: * michael@0: * A < 0 <=> one of the two circles completely contains the other one michael@0: * <=> for every p, the radiuses associated with the two t solutions michael@0: * have opposite sign michael@0: */ michael@0: pixman_image_t *image = iter->image; michael@0: int x = iter->x; michael@0: int y = iter->y; michael@0: int width = iter->width; michael@0: uint16_t *buffer = iter->buffer; michael@0: pixman_bool_t toggle = ((x ^ y) & 1); michael@0: michael@0: gradient_t *gradient = (gradient_t *)image; michael@0: radial_gradient_t *radial = (radial_gradient_t *)image; michael@0: uint16_t *end = buffer + width; michael@0: pixman_gradient_walker_t walker; michael@0: pixman_vector_t v, unit; michael@0: michael@0: /* reference point is the center of the pixel */ michael@0: v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; michael@0: v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; michael@0: v.vector[2] = pixman_fixed_1; michael@0: michael@0: _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); michael@0: michael@0: if (image->common.transform) michael@0: { michael@0: if (!pixman_transform_point_3d (image->common.transform, &v)) michael@0: return iter->buffer; michael@0: michael@0: unit.vector[0] = image->common.transform->matrix[0][0]; michael@0: unit.vector[1] = image->common.transform->matrix[1][0]; michael@0: unit.vector[2] = image->common.transform->matrix[2][0]; michael@0: } michael@0: else michael@0: { michael@0: unit.vector[0] = pixman_fixed_1; michael@0: unit.vector[1] = 0; michael@0: unit.vector[2] = 0; michael@0: } michael@0: michael@0: if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1) michael@0: { michael@0: /* michael@0: * Given: michael@0: * michael@0: * t = (B ± ⎷(B² - A·C)) / A michael@0: * michael@0: * where michael@0: * michael@0: * A = cdx² + cdy² - dr² michael@0: * B = pdx·cdx + pdy·cdy + r₁·dr michael@0: * C = pdx² + pdy² - r₁² michael@0: * det = B² - A·C michael@0: * michael@0: * Since we have an affine transformation, we know that (pdx, pdy) michael@0: * increase linearly with each pixel, michael@0: * michael@0: * pdx = pdx₀ + n·ux, michael@0: * pdy = pdy₀ + n·uy, michael@0: * michael@0: * we can then express B, C and det through multiple differentiation. michael@0: */ michael@0: pixman_fixed_32_32_t b, db, c, dc, ddc; michael@0: michael@0: /* warning: this computation may overflow */ michael@0: v.vector[0] -= radial->c1.x; michael@0: v.vector[1] -= radial->c1.y; michael@0: michael@0: /* michael@0: * B and C are computed and updated exactly. michael@0: * If fdot was used instead of dot, in the worst case it would michael@0: * lose 11 bits of precision in each of the multiplication and michael@0: * summing up would zero out all the bit that were preserved, michael@0: * thus making the result 0 instead of the correct one. michael@0: * This would mean a worst case of unbound relative error or michael@0: * about 2^10 absolute error michael@0: */ michael@0: b = dot (v.vector[0], v.vector[1], radial->c1.radius, michael@0: radial->delta.x, radial->delta.y, radial->delta.radius); michael@0: db = dot (unit.vector[0], unit.vector[1], 0, michael@0: radial->delta.x, radial->delta.y, 0); michael@0: michael@0: c = dot (v.vector[0], v.vector[1], michael@0: -((pixman_fixed_48_16_t) radial->c1.radius), michael@0: v.vector[0], v.vector[1], radial->c1.radius); michael@0: dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0], michael@0: 2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1], michael@0: 0, michael@0: unit.vector[0], unit.vector[1], 0); michael@0: ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, michael@0: unit.vector[0], unit.vector[1], 0); michael@0: michael@0: while (buffer < end) michael@0: { michael@0: if (!mask || *mask++) michael@0: { michael@0: *buffer = dither_8888_to_0565( michael@0: radial_compute_color (radial->a, b, c, michael@0: radial->inva, michael@0: radial->delta.radius, michael@0: radial->mindr, michael@0: &walker, michael@0: image->common.repeat), michael@0: toggle); michael@0: } michael@0: michael@0: toggle ^= 1; michael@0: b += db; michael@0: c += dc; michael@0: dc += ddc; michael@0: ++buffer; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* projective */ michael@0: /* Warning: michael@0: * error propagation guarantees are much looser than in the affine case michael@0: */ michael@0: while (buffer < end) michael@0: { michael@0: if (!mask || *mask++) michael@0: { michael@0: if (v.vector[2] != 0) michael@0: { michael@0: double pdx, pdy, invv2, b, c; michael@0: michael@0: invv2 = 1. * pixman_fixed_1 / v.vector[2]; michael@0: michael@0: pdx = v.vector[0] * invv2 - radial->c1.x; michael@0: /* / pixman_fixed_1 */ michael@0: michael@0: pdy = v.vector[1] * invv2 - radial->c1.y; michael@0: /* / pixman_fixed_1 */ michael@0: michael@0: b = fdot (pdx, pdy, radial->c1.radius, michael@0: radial->delta.x, radial->delta.y, michael@0: radial->delta.radius); michael@0: /* / pixman_fixed_1 / pixman_fixed_1 */ michael@0: michael@0: c = fdot (pdx, pdy, -radial->c1.radius, michael@0: pdx, pdy, radial->c1.radius); michael@0: /* / pixman_fixed_1 / pixman_fixed_1 */ michael@0: michael@0: *buffer = dither_8888_to_0565 ( michael@0: radial_compute_color (radial->a, b, c, michael@0: radial->inva, michael@0: radial->delta.radius, michael@0: radial->mindr, michael@0: &walker, michael@0: image->common.repeat), michael@0: toggle); michael@0: } michael@0: else michael@0: { michael@0: *buffer = 0; michael@0: } michael@0: } michael@0: michael@0: ++buffer; michael@0: toggle ^= 1; michael@0: michael@0: v.vector[0] += unit.vector[0]; michael@0: v.vector[1] += unit.vector[1]; michael@0: v.vector[2] += unit.vector[2]; michael@0: } michael@0: } michael@0: michael@0: iter->y++; michael@0: return iter->buffer; michael@0: } michael@0: static uint32_t * michael@0: radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) michael@0: { michael@0: uint32_t *buffer = radial_get_scanline_narrow (iter, NULL); michael@0: michael@0: pixman_expand_to_float ( michael@0: (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: void michael@0: _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) michael@0: { michael@0: if (iter->iter_flags & ITER_16) michael@0: iter->get_scanline = radial_get_scanline_16; michael@0: else if (iter->iter_flags & ITER_NARROW) michael@0: iter->get_scanline = radial_get_scanline_narrow; michael@0: else michael@0: iter->get_scanline = radial_get_scanline_wide; michael@0: } michael@0: michael@0: michael@0: PIXMAN_EXPORT pixman_image_t * michael@0: pixman_image_create_radial_gradient (const pixman_point_fixed_t * inner, michael@0: const pixman_point_fixed_t * outer, michael@0: pixman_fixed_t inner_radius, michael@0: pixman_fixed_t outer_radius, michael@0: const pixman_gradient_stop_t *stops, michael@0: int n_stops) michael@0: { michael@0: pixman_image_t *image; michael@0: radial_gradient_t *radial; michael@0: michael@0: image = _pixman_image_allocate (); michael@0: michael@0: if (!image) michael@0: return NULL; michael@0: michael@0: radial = &image->radial; michael@0: michael@0: if (!_pixman_init_gradient (&radial->common, stops, n_stops)) michael@0: { michael@0: free (image); michael@0: return NULL; michael@0: } michael@0: michael@0: image->type = RADIAL; michael@0: michael@0: radial->c1.x = inner->x; michael@0: radial->c1.y = inner->y; michael@0: radial->c1.radius = inner_radius; michael@0: radial->c2.x = outer->x; michael@0: radial->c2.y = outer->y; michael@0: radial->c2.radius = outer_radius; michael@0: michael@0: /* warning: this computations may overflow */ michael@0: radial->delta.x = radial->c2.x - radial->c1.x; michael@0: radial->delta.y = radial->c2.y - radial->c1.y; michael@0: radial->delta.radius = radial->c2.radius - radial->c1.radius; michael@0: michael@0: /* computed exactly, then cast to double -> every bit of the double michael@0: representation is correct (53 bits) */ michael@0: radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius, michael@0: radial->delta.x, radial->delta.y, radial->delta.radius); michael@0: if (radial->a != 0) michael@0: radial->inva = 1. * pixman_fixed_1 / radial->a; michael@0: michael@0: radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius; michael@0: michael@0: return image; michael@0: }