KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > RadialGradientPaintContext


1 /*
2  * @(#)RadialGradientPaintContext.java 1.4 06/04/28
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.awt;
9
10 import java.awt.MultipleGradientPaint.CycleMethod JavaDoc;
11 import java.awt.MultipleGradientPaint.ColorSpaceType JavaDoc;
12 import java.awt.geom.AffineTransform JavaDoc;
13 import java.awt.geom.Rectangle2D JavaDoc;
14 import java.awt.image.ColorModel JavaDoc;
15
16 /**
17  * Provides the actual implementation for the RadialGradientPaint.
18  * This is where the pixel processing is done. A RadialGradienPaint
19  * only supports circular gradients, but it should be possible to scale
20  * the circle to look approximately elliptical, by means of a
21  * gradient transform passed into the RadialGradientPaint constructor.
22  *
23  * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
24  */

25 final class RadialGradientPaintContext extends MultipleGradientPaintContext JavaDoc {
26     
27     /** True when (focus == center). */
28     private boolean isSimpleFocus = false;
29
30     /** True when (cycleMethod == NO_CYCLE). */
31     private boolean isNonCyclic = false;
32        
33     /** Radius of the outermost circle defining the 100% gradient stop. */
34     private float radius;
35     
36     /** Variables representing center and focus points. */
37     private float centerX, centerY, focusX, focusY;
38
39     /** Radius of the gradient circle squared. */
40     private float radiusSq;
41
42     /** Constant part of X, Y user space coordinates. */
43     private float constA, constB;
44
45     /** Constant second order delta for simple loop. */
46     private float gDeltaDelta;
47
48     /**
49      * This value represents the solution when focusX == X. It is called
50      * trivial because it is easier to calculate than the general case.
51      */

52     private float trivial;
53     
54     /** Amount for offset when clamping focus. */
55     private static final float SCALEBACK = .99f;
56     
57     /**
58      * Constructor for RadialGradientPaintContext.
59      *
60      * @param paint the {@code RadialGradientPaint} from which this context
61      * is created
62      * @param cm the {@code ColorModel} that receives
63      * the {@code Paint} data (this is used only as a hint)
64      * @param deviceBounds the device space bounding box of the
65      * graphics primitive being rendered
66      * @param userBounds the user space bounding box of the
67      * graphics primitive being rendered
68      * @param t the {@code AffineTransform} from user
69      * space into device space (gradientTransform should be
70      * concatenated with this)
71      * @param hints the hints that the context object uses to choose
72      * between rendering alternatives
73      * @param cx the center X coordinate in user space of the circle defining
74      * the gradient. The last color of the gradient is mapped to
75      * the perimeter of this circle.
76      * @param cy the center Y coordinate in user space of the circle defining
77      * the gradient. The last color of the gradient is mapped to
78      * the perimeter of this circle.
79      * @param r the radius of the circle defining the extents of the
80      * color gradient
81      * @param fx the X coordinate in user space to which the first color
82      * is mapped
83      * @param fy the Y coordinate in user space to which the first color
84      * is mapped
85      * @param fractions the fractions specifying the gradient distribution
86      * @param colors the gradient colors
87      * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
88      * @param colorSpace which colorspace to use for interpolation,
89      * either SRGB or LINEAR_RGB
90      */

91     RadialGradientPaintContext(RadialGradientPaint JavaDoc paint,
92                                ColorModel JavaDoc cm,
93                                Rectangle JavaDoc deviceBounds,
94                                Rectangle2D JavaDoc userBounds,
95                                AffineTransform JavaDoc t,
96                                RenderingHints JavaDoc hints,
97                                float cx, float cy,
98                                float r,
99                                float fx, float fy,
100                                float[] fractions,
101                                Color JavaDoc[] colors,
102                                CycleMethod cycleMethod,
103                                ColorSpaceType colorSpace)
104     {
105         super(paint, cm, deviceBounds, userBounds, t, hints,
106               fractions, colors, cycleMethod, colorSpace);
107
108         // copy some parameters
109
centerX = cx;
110         centerY = cy;
111         focusX = fx;
112         focusY = fy;
113         radius = r;
114         
115         this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
116         this.isNonCyclic = (cycleMethod == CycleMethod.NO_CYCLE);
117         
118         // for use in the quadractic equation
119
radiusSq = radius * radius;
120
121         float dX = focusX - centerX;
122         float dY = focusY - centerY;
123
124         double distSq = (dX * dX) + (dY * dY);
125
126         // test if distance from focus to center is greater than the radius
127
if (distSq > radiusSq * SCALEBACK) {
128             // clamp focus to radius
129
float scalefactor = (float)Math.sqrt(radiusSq * SCALEBACK / distSq);
130             dX = dX * scalefactor;
131             dY = dY * scalefactor;
132             focusX = centerX + dX;
133             focusY = centerY + dY;
134         }
135
136         // calculate the solution to be used in the case where X == focusX
137
// in cyclicCircularGradientFillRaster()
138
trivial = (float)Math.sqrt(radiusSq - (dX * dX));
139
140         // constant parts of X, Y user space coordinates
141
constA = a02 - centerX;
142         constB = a12 - centerY;
143
144         // constant second order delta for simple loop
145
gDeltaDelta = 2 * ( a00 * a00 + a10 * a10) / radiusSq;
146     }
147     
148     /**
149      * Return a Raster containing the colors generated for the graphics
150      * operation.
151      *
152      * @param x,y,w,h the area in device space for which colors are
153      * generated.
154      */

155     protected void fillRaster(int pixels[], int off, int adjust,
156                               int x, int y, int w, int h)
157     {
158         if (isSimpleFocus && isNonCyclic && isSimpleLookup) {
159             simpleNonCyclicFillRaster(pixels, off, adjust, x, y, w, h);
160         } else {
161             cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
162         }
163     }
164     
165     /**
166      * This code works in the simplest of cases, where the focus == center
167      * point, the gradient is noncyclic, and the gradient lookup method is
168      * fast (single array index, no conversion necessary).
169      */

170     private void simpleNonCyclicFillRaster(int pixels[], int off, int adjust,
171                                            int x, int y, int w, int h)
172     {
173         /* We calculate sqrt(X^2 + Y^2) relative to the radius
174          * size to get the fraction for the color to use.
175          *
176          * Each step along the scanline adds (a00, a10) to (X, Y).
177          * If we precalculate:
178          * gRel = X^2+Y^2
179          * for the start of the row, then for each step we need to
180          * calculate:
181          * gRel' = (X+a00)^2 + (Y+a10)^2
182          * = X^2 + 2*X*a00 + a00^2 + Y^2 + 2*Y*a10 + a10^2
183          * = (X^2+Y^2) + 2*(X*a00+Y*a10) + (a00^2+a10^2)
184          * = gRel + 2*(X*a00+Y*a10) + (a00^2+a10^2)
185          * = gRel + 2*DP + SD
186          * (where DP = dot product between X,Y and a00,a10
187          * and SD = dot product square of the delta vector)
188          * For the step after that we get:
189          * gRel'' = (X+2*a00)^2 + (Y+2*a10)^2
190          * = X^2 + 4*X*a00 + 4*a00^2 + Y^2 + 4*Y*a10 + 4*a10^2
191          * = (X^2+Y^2) + 4*(X*a00+Y*a10) + 4*(a00^2+a10^2)
192          * = gRel + 4*DP + 4*SD
193          * = gRel' + 2*DP + 3*SD
194          * The increment changed by:
195          * (gRel'' - gRel') - (gRel' - gRel)
196          * = (2*DP + 3*SD) - (2*DP + SD)
197          * = 2*SD
198          * Note that this value depends only on the (inverse of the)
199          * transformation matrix and so is a constant for the loop.
200          * To make this all relative to the unit circle, we need to
201          * divide all values as follows:
202          * [XY] /= radius
203          * gRel /= radiusSq
204          * DP /= radiusSq
205          * SD /= radiusSq
206          */

207         // coordinates of UL corner in "user space" relative to center
208
float rowX = (a00*x) + (a01*y) + constA;
209         float rowY = (a10*x) + (a11*y) + constB;
210
211         // second order delta calculated in constructor
212
float gDeltaDelta = this.gDeltaDelta;
213
214         // adjust is (scan-w) of pixels array, we need (scan)
215
adjust += w;
216
217         // rgb of the 1.0 color used when the distance exceeds gradient radius
218
int rgbclip = gradient[fastGradientArraySize];
219
220         for (int j = 0; j < h; j++) {
221             // these values depend on the coordinates of the start of the row
222
float gRel = (rowX * rowX + rowY * rowY) / radiusSq;
223             float gDelta = (2 * ( a00 * rowX + a10 * rowY) / radiusSq +
224                             gDeltaDelta/2);
225
226             /* Use optimized loops for any cases where gRel >= 1.
227              * We do not need to calculate sqrt(gRel) for these
228              * values since sqrt(N>=1) == (M>=1).
229              * Note that gRel follows a parabola which can only be < 1
230              * for a small region around the center on each scanline. In
231              * particular:
232              * gDeltaDelta is always positive
233              * gDelta is <0 until it crosses the midpoint, then >0
234              * To the left and right of that region, it will always be
235              * >=1 out to infinity, so we can process the line in 3
236              * regions:
237              * out to the left - quick fill until gRel < 1, updating gRel
238              * in the heart - slow fraction=sqrt fill while gRel < 1
239              * out to the right - quick fill rest of scanline, ignore gRel
240              */

241             int i = 0;
242             // Quick fill for "out to the left"
243
while (i < w && gRel >= 1.0f) {
244                 pixels[off + i] = rgbclip;
245                 gRel += gDelta;
246                 gDelta += gDeltaDelta;
247                 i++;
248             }
249             // Slow fill for "in the heart"
250
while (i < w && gRel < 1.0f) {
251                 int gIndex;
252
253                 if (gRel <= 0) {
254                     gIndex = 0;
255                 } else {
256                     float fIndex = gRel * SQRT_LUT_SIZE;
257                     int iIndex = (int) (fIndex);
258                     float s0 = sqrtLut[iIndex];
259                     float s1 = sqrtLut[iIndex+1] - s0;
260                     fIndex = s0 + (fIndex - iIndex) * s1;
261                     gIndex = (int) (fIndex * fastGradientArraySize);
262                 }
263
264                 // store the color at this point
265
pixels[off + i] = gradient[gIndex];
266
267                 // incremental calculation
268
gRel += gDelta;
269                 gDelta += gDeltaDelta;
270                 i++;
271             }
272             // Quick fill to end of line for "out to the right"
273
while (i < w) {
274                 pixels[off + i] = rgbclip;
275                 i++;
276             }
277
278             off += adjust;
279             rowX += a01;
280             rowY += a11;
281         }
282     }
283
284     // SQRT_LUT_SIZE must be a power of 2 for the test above to work.
285
private static final int SQRT_LUT_SIZE = (1 << 11);
286     private static float sqrtLut[] = new float[SQRT_LUT_SIZE+1];
287     static {
288         for (int i = 0; i < sqrtLut.length; i++) {
289             sqrtLut[i] = (float) Math.sqrt(i / ((float) SQRT_LUT_SIZE));
290         }
291     }
292
293     /**
294      * Fill the raster, cycling the gradient colors when a point falls outside
295      * of the perimeter of the 100% stop circle.
296      *
297      * This calculation first computes the intersection point of the line
298      * from the focus through the current point in the raster, and the
299      * perimeter of the gradient circle.
300      *
301      * Then it determines the percentage distance of the current point along
302      * that line (focus is 0%, perimeter is 100%).
303      *
304      * Equation of a circle centered at (a,b) with radius r:
305      * (x-a)^2 + (y-b)^2 = r^2
306      * Equation of a line with slope m and y-intercept b:
307      * y = mx + b
308      * Replacing y in the circle equation and solving using the quadratic
309      * formula produces the following set of equations. Constant factors have
310      * been extracted out of the inner loop.
311      */

312     private void cyclicCircularGradientFillRaster(int pixels[], int off,
313                                                   int adjust,
314                                                   int x, int y,
315                                                   int w, int h)
316     {
317         // constant part of the C factor of the quadratic equation
318
final double constC =
319             -radiusSq + (centerX * centerX) + (centerY * centerY);
320
321         // coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
322
double A, B, C;
323
324         // slope and y-intercept of the focus-perimeter line
325
double slope, yintcpt;
326
327         // intersection with circle X,Y coordinate
328
double solutionX, solutionY;
329
330         // constant parts of X, Y coordinates
331
final float constX = (a00*x) + (a01*y) + a02;
332         final float constY = (a10*x) + (a11*y) + a12;
333
334         // constants in inner loop quadratic formula
335
final float precalc2 = 2 * centerY;
336         final float precalc3 = -2 * centerX;
337
338         // value between 0 and 1 specifying position in the gradient
339
float g;
340
341         // determinant of quadratic formula (should always be > 0)
342
float det;
343
344         // sq distance from the current point to focus
345
float currentToFocusSq;
346
347         // sq distance from the intersect point to focus
348
float intersectToFocusSq;
349
350         // temp variables for change in X,Y squared
351
float deltaXSq, deltaYSq;
352
353         // used to index pixels array
354
int indexer = off;
355
356         // incremental index change for pixels array
357
int pixInc = w+adjust;
358         
359         // for every row
360
for (int j = 0; j < h; j++) {
361
362             // user space point; these are constant from column to column
363
float X = (a01*j) + constX;
364             float Y = (a11*j) + constY;
365
366             // for every column (inner loop begins here)
367
for (int i = 0; i < w; i++) {
368         
369                 if (X == focusX) {
370                     // special case to avoid divide by zero
371
solutionX = focusX;
372                     solutionY = centerY;
373                     solutionY += (Y > focusY) ? trivial : -trivial;
374                 } else {
375                     // slope and y-intercept of the focus-perimeter line
376
slope = (Y - focusY) / (X - focusX);
377                     yintcpt = Y - (slope * X);
378                     
379                     // use the quadratic formula to calculate the
380
// intersection point
381
A = (slope * slope) + 1;
382                     B = precalc3 + (-2 * slope * (centerY - yintcpt));
383                     C = constC + (yintcpt* (yintcpt - precalc2));
384                     
385                     det = (float)Math.sqrt((B * B) - (4 * A * C));
386                     solutionX = -B;
387                     
388                     // choose the positive or negative root depending
389
// on where the X coord lies with respect to the focus
390
solutionX += (X < focusX)? -det : det;
391                     solutionX = solutionX / (2 * A); // divisor
392
solutionY = (slope * solutionX) + yintcpt;
393                 }
394
395                 // Calculate the square of the distance from the current point
396
// to the focus and the square of the distance from the
397
// intersection point to the focus. Want the squares so we can
398
// do 1 square root after division instead of 2 before.
399

400                 deltaXSq = X - focusX;
401                 deltaXSq = deltaXSq * deltaXSq;
402
403                 deltaYSq = Y - focusY;
404                 deltaYSq = deltaYSq * deltaYSq;
405
406                 currentToFocusSq = deltaXSq + deltaYSq;
407
408                 deltaXSq = (float)solutionX - focusX;
409                 deltaXSq = deltaXSq * deltaXSq;
410
411                 deltaYSq = (float)solutionY - focusY;
412                 deltaYSq = deltaYSq * deltaYSq;
413
414                 intersectToFocusSq = deltaXSq + deltaYSq;
415
416                 // get the percentage (0-1) of the current point along the
417
// focus-circumference line
418
g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);
419                                               
420                 // store the color at this point
421
pixels[indexer + i] = indexIntoGradientsArrays(g);
422
423                 // incremental change in X, Y
424
X += a00;
425                 Y += a10;
426             } //end inner loop
427

428             indexer += pixInc;
429         } //end outer loop
430
}
431 }
432
Popular Tags