Gradient fixes

* Port fix for bug 7685 from pixman. Patch by Carl Worth

* Add projective version of radial gradient code.

* Make sure that all Pict*Gradient types have PictGradient as prefix,
  since code in various places relies on that.
This commit is contained in:
Soren Sandmann Pedersen 2007-04-23 13:19:54 -04:00
parent 38d14e8589
commit 84838268b3
3 changed files with 204 additions and 38 deletions

View File

@ -3148,13 +3148,128 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
} }
} }
} else { } else {
/*
* In the radial gradient problem we are given two circles (c,r) and
* (c,r) that define the gradient itself. Then, for any point p, we
* must compute the value(s) of t within [0.0, 1.0] representing the
* circle(s) that would color the point.
*
* There are potentially two values of t since the point p can be
* colored by both sides of the circle, (which happens whenever one
* circle is not entirely contained within the other).
*
* If we solve for a value of t that is outside of [0.0, 1.0] then we
* use the extend mode (NONE, REPEAT, REFLECT, or PAD) to map to a
* value within [0.0, 1.0].
*
* Here is an illustration of the problem:
*
* p
* p
*
* · r
* p ·
* θ
*
* r · c
* θ ·
*
* c
*
* Given (c,r), (c,r) and p, we must find an angle θ such that two
* points p and p on the two circles are collinear with p. Then, the
* desired value of t is the ratio of the length of pp to the length
* of pp.
*
* So, we have six unknown values: (px, py), (px, py), θ and t.
* We can also write six equations that constrain the problem:
*
* Point p is a distance r from c at an angle of θ:
*
* 1. px = cx + r·cos θ
* 2. py = cy + r·sin θ
*
* Point p is a distance r from c at an angle of θ:
*
* 3. px = cx + r2·cos θ
* 4. py = cy + r2·sin θ
*
* Point p lies at a fraction t along the line segment pp:
*
* 5. px = t·px + (1-t)·px
* 6. py = t·py + (1-t)·py
*
* To solve, first subtitute 1-4 into 5 and 6:
*
* px = t·(cx + r·cos θ) + (1-t)·(cx + r·cos θ)
* py = t·(cy + r·sin θ) + (1-t)·(cy + r·sin θ)
*
* Then solve each for cos θ and sin θ expressed as a function of t:
*
* cos θ = (-(cx - cx)·t + (px - cx)) / ((r-r)·t + r)
* sin θ = (-(cy - cy)·t + (py - cy)) / ((r-r)·t + r)
*
* To simplify this a bit, we define new variables for several of the
* common terms as shown below:
*
* p
* p
*
* · r
* p ·
* pdy
* c
* r ·
* · cdy
*
* c pdx cdx
*
* cdx = (cx - cx)
* cdy = (cy - cy)
* dr = r-r
* pdx = px - cx
* pdy = py - cy
*
* Note that cdx, cdy, and dr do not depend on point p at all, so can
* be pre-computed for the entire gradient. The simplifed equations
* are now:
*
* cos θ = (-cdx·t + pdx) / (dr·t + r)
* sin θ = (-cdy·t + pdy) / (dr·t + r)
*
* Finally, to get a single function of t and eliminate the last
* unknown θ, we use the identity sin²θ + cos²θ = 1. First, square
* each equation, (we knew a quadratic was coming since it must be
* possible to obtain two solutions in some cases):
*
* cos²θ = (cdx²t² - 2·cdx·pdx·t + pdx²) / (dr²·t² + 2·r·dr·t + r²)
* sin²θ = (cdy²t² - 2·cdy·pdy·t + pdy²) / (dr²·t² + 2·r·dr·t + r²)
*
* Then add both together, set the result equal to 1, and express as a
* standard quadratic equation in t of the form At² + Bt + C = 0
*
* (cdx² + cdy² - dr²)·t² - 2·(cdx·pdx + cdy·pdy + r·dr)·t + (pdx² + pdy² - r²) = 0
*
* In other words:
*
* A = cdx² + cdy² - dr²
* B = -2·(pdx·cdx + pdy·cdy + r·dr)
* C = pdx² + pdy² - r²
*
* And again, notice that A does not depend on p, so can be
* precomputed. From here we just use the quadratic formula to solve
* for t:
*
* t = (-2·B ± (B² - 4·A·C)) / 2·A
*/
/* radial or conical */ /* radial or conical */
Bool affine = TRUE; Bool affine = TRUE;
double cx = 1.; double cx = 1.;
double cy = 0.; double cy = 0.;
double cz = 0.; double cz = 0.;
double rx = x; double rx = x + 0.5;
double ry = y; double ry = y + 0.5;
double rz = 1.; double rz = 1.;
if (pict->transform) { if (pict->transform) {
@ -3176,23 +3291,36 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
} }
if (pGradient->type == SourcePictTypeRadial) { if (pGradient->type == SourcePictTypeRadial) {
PictRadialGradient *radial;
radial = &pGradient->radial;
if (affine) { if (affine) {
rx -= pGradient->radial.fx;
ry -= pGradient->radial.fy;
while (buffer < end) { while (buffer < end) {
double b, c, det, s;
if (!mask || *mask++ & maskBits) if (!mask || *mask++ & maskBits)
{ {
double pdx, pdy;
double B, C;
double det;
double c1x = radial->c1.x / 65536.0;
double c1y = radial->c1.y / 65536.0;
double r1 = radial->c1.radius / 65536.0;
xFixed_48_16 t; xFixed_48_16 t;
b = 2*(rx*pGradient->radial.dx + ry*pGradient->radial.dy); pdx = rx - c1x;
c = -(rx*rx + ry*ry); pdy = ry - c1y;
det = (b * b) - (4 * pGradient->radial.a * c);
s = (-b + sqrt(det))/(2. * pGradient->radial.a);
t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536); B = -2 * ( pdx * radial->cdx
+ pdy * radial->cdy
+ r1 * radial->dr);
C = (pdx * pdx + pdy * pdy - r1 * r1);
det = (B * B) - (4 * radial->A * C);
if (det < 0.0)
det = 0.0;
if (radial->A < 0)
t = (xFixed_48_16) ((- B - sqrt(det)) / (2.0 * radial->A) * 65536);
else
t = (xFixed_48_16) ((- B + sqrt(det)) / (2.0 * radial->A) * 65536);
WRITE(buffer, _gradient_walker_pixel (&walker, t)); WRITE(buffer, _gradient_walker_pixel (&walker, t));
} }
@ -3202,13 +3330,18 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
ry += cy; ry += cy;
} }
} else { } else {
/* projective */
while (buffer < end) { while (buffer < end) {
double x, y;
double b, c, det, s;
if (!mask || *mask++ & maskBits) if (!mask || *mask++ & maskBits)
{ {
double pdx, pdy;
double B, C;
double det;
double c1x = radial->c1.x / 65536.0;
double c1y = radial->c1.y / 65536.0;
double r1 = radial->c1.radius / 65536.0;
xFixed_48_16 t; xFixed_48_16 t;
double x, y;
if (rz != 0) { if (rz != 0) {
x = rx/rz; x = rx/rz;
@ -3216,13 +3349,23 @@ static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *
} else { } else {
x = y = 0.; x = y = 0.;
} }
x -= pGradient->radial.fx;
y -= pGradient->radial.fy; pdx = x - c1x;
b = 2*(x*pGradient->radial.dx + y*pGradient->radial.dy); pdy = y - c1y;
c = -(x*x + y*y);
det = (b * b) - (4 * pGradient->radial.a * c); B = -2 * ( pdx * radial->cdx
s = (-b + sqrt(det))/(2. * pGradient->radial.a); + pdy * radial->cdy
t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536); + r1 * radial->dr);
C = (pdx * pdx + pdy * pdy - r1 * r1);
det = (B * B) - (4 * radial->A * C);
if (det < 0.0)
det = 0.0;
if (radial->A < 0)
t = (xFixed_48_16) ((- B - sqrt(det)) / (2.0 * radial->A) * 65536);
else
t = (xFixed_48_16) ((- B + sqrt(det)) / (2.0 * radial->A) * 65536);
WRITE(buffer, _gradient_walker_pixel (&walker, t)); WRITE(buffer, _gradient_walker_pixel (&walker, t));
} }

View File

@ -1051,6 +1051,7 @@ CreateRadialGradientPicture (Picture pid, xPointFixed *inner, xPointFixed *outer
radial = &pPicture->pSourcePict->radial; radial = &pPicture->pSourcePict->radial;
radial->type = SourcePictTypeRadial; radial->type = SourcePictTypeRadial;
#if 0
{ {
double x = (double)innerRadius / (double)outerRadius; double x = (double)innerRadius / (double)outerRadius;
radial->dx = (outer->x - inner->x); radial->dx = (outer->x - inner->x);
@ -1066,6 +1067,19 @@ CreateRadialGradientPicture (Picture pid, xPointFixed *inner, xPointFixed *outer
x = outerRadius/65536.; x = outerRadius/65536.;
radial->a = x*x - radial->dx*radial->dx - radial->dy*radial->dy; radial->a = x*x - radial->dx*radial->dx - radial->dy*radial->dy;
} }
#endif
radial->c1.x = inner->x;
radial->c1.y = inner->y;
radial->c1.radius = innerRadius;
radial->c2.x = outer->x;
radial->c2.y = outer->y;
radial->c2.radius = outerRadius;
radial->cdx = (radial->c2.x - radial->c1.x) / 65536.;
radial->cdy = (radial->c2.y - radial->c1.y) / 65536.;
radial->dr = (radial->c2.radius - radial->c1.radius) / 65536.;
radial->A = ( radial->cdx * radial->cdx
+ radial->cdy * radial->cdy
- radial->dr * radial->dr);
initGradient(pPicture->pSourcePict, nStops, stops, colors, error); initGradient(pPicture->pSourcePict, nStops, stops, colors, error);
if (*error) { if (*error) {

View File

@ -105,17 +105,26 @@ typedef struct _PictLinearGradient {
xPointFixed p2; xPointFixed p2;
} PictLinearGradient, *PictLinearGradientPtr; } PictLinearGradient, *PictLinearGradientPtr;
typedef struct _PictCircle {
xFixed x;
xFixed y;
xFixed radius;
} PictCircle, *PictCirclePtr;
typedef struct _PictRadialGradient { typedef struct _PictRadialGradient {
unsigned int type; unsigned int type;
unsigned int class;
int nstops; int nstops;
PictGradientStopPtr stops; PictGradientStopPtr stops;
double fx; int stopRange;
double fy; CARD32 *colorTable;
double dx; int colorTableSize;
double dy; PictCircle c1;
double a; PictCircle c2;
double m; double cdx;
double b; double cdy;
double dr;
double A;
} PictRadialGradient, *PictRadialGradientPtr; } PictRadialGradient, *PictRadialGradientPtr;
typedef struct _PictConicalGradient { typedef struct _PictConicalGradient {