History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/awt/RadialGradientPaintContext.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/awt/RadialGradientPaintContext.java+449−0
@@ -0,0 +1,449 @@
1+/*
2+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package java.awt;
27+
28+import java.awt.MultipleGradientPaint.CycleMethod;
29+import java.awt.MultipleGradientPaint.ColorSpaceType;
30+import java.awt.geom.AffineTransform;
31+import java.awt.geom.Rectangle2D;
32+import java.awt.image.ColorModel;
33+
34+/**
35+ * Provides the actual implementation for the RadialGradientPaint.
36+ * This is where the pixel processing is done. A RadialGradienPaint
37+ * only supports circular gradients, but it should be possible to scale
38+ * the circle to look approximately elliptical, by means of a
39+ * gradient transform passed into the RadialGradientPaint constructor.
40+ *
41+ * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
42+ */
43+final class RadialGradientPaintContext extends MultipleGradientPaintContext {
44+
45+ /** True when (focus == center). */
46+ private boolean isSimpleFocus = false;
47+
48+ /** True when (cycleMethod == NO_CYCLE). */
49+ private boolean isNonCyclic = false;
50+
51+ /** Radius of the outermost circle defining the 100% gradient stop. */
52+ private float radius;
53+
54+ /** Variables representing center and focus points. */
55+ private float centerX, centerY, focusX, focusY;
56+
57+ /** Radius of the gradient circle squared. */
58+ private float radiusSq;
59+
60+ /** Constant part of X, Y user space coordinates. */
61+ private float constA, constB;
62+
63+ /** Constant second order delta for simple loop. */
64+ private float gDeltaDelta;
65+
66+ /**
67+ * This value represents the solution when focusX == X. It is called
68+ * trivial because it is easier to calculate than the general case.
69+ */
70+ private float trivial;
71+
72+ /** Amount for offset when clamping focus. */
73+ private static final float SCALEBACK = .99f;
74+
75+ /**
76+ * Constructor for RadialGradientPaintContext.
77+ *
78+ * @param paint the {@code RadialGradientPaint} from which this context
79+ * is created
80+ * @param cm the {@code ColorModel} that receives
81+ * the {@code Paint} data (this is used only as a hint)
82+ * @param deviceBounds the device space bounding box of the
83+ * graphics primitive being rendered
84+ * @param userBounds the user space bounding box of the
85+ * graphics primitive being rendered
86+ * @param t the {@code AffineTransform} from user
87+ * space into device space (gradientTransform should be
88+ * concatenated with this)
89+ * @param hints the hints that the context object uses to choose
90+ * between rendering alternatives
91+ * @param cx the center X coordinate in user space of the circle defining
92+ * the gradient. The last color of the gradient is mapped to
93+ * the perimeter of this circle.
94+ * @param cy the center Y coordinate in user space of the circle defining
95+ * the gradient. The last color of the gradient is mapped to
96+ * the perimeter of this circle.
97+ * @param r the radius of the circle defining the extents of the
98+ * color gradient
99+ * @param fx the X coordinate in user space to which the first color
100+ * is mapped
101+ * @param fy the Y coordinate in user space to which the first color
102+ * is mapped
103+ * @param fractions the fractions specifying the gradient distribution
104+ * @param colors the gradient colors
105+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
106+ * @param colorSpace which colorspace to use for interpolation,
107+ * either SRGB or LINEAR_RGB
108+ */
109+ RadialGradientPaintContext(RadialGradientPaint paint,
110+ ColorModel cm,
111+ Rectangle deviceBounds,
112+ Rectangle2D userBounds,
113+ AffineTransform t,
114+ RenderingHints hints,
115+ float cx, float cy,
116+ float r,
117+ float fx, float fy,
118+ float[] fractions,
119+ Color[] colors,
120+ CycleMethod cycleMethod,
121+ ColorSpaceType colorSpace)
122+ {
123+ super(paint, cm, deviceBounds, userBounds, t, hints,
124+ fractions, colors, cycleMethod, colorSpace);
125+
126+ // copy some parameters
127+ centerX = cx;
128+ centerY = cy;
129+ focusX = fx;
130+ focusY = fy;
131+ radius = r;
132+
133+ this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
134+ this.isNonCyclic = (cycleMethod == CycleMethod.NO_CYCLE);
135+
136+ // for use in the quadractic equation
137+ radiusSq = radius * radius;
138+
139+ float dX = focusX - centerX;
140+ float dY = focusY - centerY;
141+
142+ double distSq = (dX * dX) + (dY * dY);
143+
144+ // test if distance from focus to center is greater than the radius
145+ if (distSq > radiusSq * SCALEBACK) {
146+ // clamp focus to radius
147+ float scalefactor = (float)Math.sqrt(radiusSq * SCALEBACK / distSq);
148+ dX = dX * scalefactor;
149+ dY = dY * scalefactor;
150+ focusX = centerX + dX;
151+ focusY = centerY + dY;
152+ }
153+
154+ // calculate the solution to be used in the case where X == focusX
155+ // in cyclicCircularGradientFillRaster()
156+ trivial = (float)Math.sqrt(radiusSq - (dX * dX));
157+
158+ // constant parts of X, Y user space coordinates
159+ constA = a02 - centerX;
160+ constB = a12 - centerY;
161+
162+ // constant second order delta for simple loop
163+ gDeltaDelta = 2 * ( a00 * a00 + a10 * a10) / radiusSq;
164+ }
165+
166+ /**
167+ * Return a Raster containing the colors generated for the graphics
168+ * operation.
169+ *
170+ * @param x,y,w,h the area in device space for which colors are
171+ * generated.
172+ */
173+ protected void fillRaster(int pixels[], int off, int adjust,
174+ int x, int y, int w, int h)
175+ {
176+ if (isSimpleFocus && isNonCyclic && isSimpleLookup) {
177+ simpleNonCyclicFillRaster(pixels, off, adjust, x, y, w, h);
178+ } else {
179+ cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
180+ }
181+ }
182+
183+ /**
184+ * This code works in the simplest of cases, where the focus == center
185+ * point, the gradient is noncyclic, and the gradient lookup method is
186+ * fast (single array index, no conversion necessary).
187+ */
188+ private void simpleNonCyclicFillRaster(int pixels[], int off, int adjust,
189+ int x, int y, int w, int h)
190+ {
191+ /* We calculate sqrt(X^2 + Y^2) relative to the radius
192+ * size to get the fraction for the color to use.
193+ *
194+ * Each step along the scanline adds (a00, a10) to (X, Y).
195+ * If we precalculate:
196+ * gRel = X^2+Y^2
197+ * for the start of the row, then for each step we need to
198+ * calculate:
199+ * gRel' = (X+a00)^2 + (Y+a10)^2
200+ * = X^2 + 2*X*a00 + a00^2 + Y^2 + 2*Y*a10 + a10^2
201+ * = (X^2+Y^2) + 2*(X*a00+Y*a10) + (a00^2+a10^2)
202+ * = gRel + 2*(X*a00+Y*a10) + (a00^2+a10^2)
203+ * = gRel + 2*DP + SD
204+ * (where DP = dot product between X,Y and a00,a10
205+ * and SD = dot product square of the delta vector)
206+ * For the step after that we get:
207+ * gRel'' = (X+2*a00)^2 + (Y+2*a10)^2
208+ * = X^2 + 4*X*a00 + 4*a00^2 + Y^2 + 4*Y*a10 + 4*a10^2
209+ * = (X^2+Y^2) + 4*(X*a00+Y*a10) + 4*(a00^2+a10^2)
210+ * = gRel + 4*DP + 4*SD
211+ * = gRel' + 2*DP + 3*SD
212+ * The increment changed by:
213+ * (gRel'' - gRel') - (gRel' - gRel)
214+ * = (2*DP + 3*SD) - (2*DP + SD)
215+ * = 2*SD
216+ * Note that this value depends only on the (inverse of the)
217+ * transformation matrix and so is a constant for the loop.
218+ * To make this all relative to the unit circle, we need to
219+ * divide all values as follows:
220+ * [XY] /= radius
221+ * gRel /= radiusSq
222+ * DP /= radiusSq
223+ * SD /= radiusSq
224+ */
225+ // coordinates of UL corner in "user space" relative to center
226+ float rowX = (a00*x) + (a01*y) + constA;
227+ float rowY = (a10*x) + (a11*y) + constB;
228+
229+ // second order delta calculated in constructor
230+ float gDeltaDelta = this.gDeltaDelta;
231+
232+ // adjust is (scan-w) of pixels array, we need (scan)
233+ adjust += w;
234+
235+ // rgb of the 1.0 color used when the distance exceeds gradient radius
236+ int rgbclip = gradient[fastGradientArraySize];
237+
238+ for (int j = 0; j < h; j++) {
239+ // these values depend on the coordinates of the start of the row
240+ float gRel = (rowX * rowX + rowY * rowY) / radiusSq;
241+ float gDelta = (2 * ( a00 * rowX + a10 * rowY) / radiusSq +
242+ gDeltaDelta/2);
243+
244+ /* Use optimized loops for any cases where gRel >= 1.
245+ * We do not need to calculate sqrt(gRel) for these
246+ * values since sqrt(N>=1) == (M>=1).
247+ * Note that gRel follows a parabola which can only be < 1
248+ * for a small region around the center on each scanline. In
249+ * particular:
250+ * gDeltaDelta is always positive
251+ * gDelta is <0 until it crosses the midpoint, then >0
252+ * To the left and right of that region, it will always be
253+ * >=1 out to infinity, so we can process the line in 3
254+ * regions:
255+ * out to the left - quick fill until gRel < 1, updating gRel
256+ * in the heart - slow fraction=sqrt fill while gRel < 1
257+ * out to the right - quick fill rest of scanline, ignore gRel
258+ */
259+ int i = 0;
260+ // Quick fill for "out to the left"
261+ while (i < w && gRel >= 1.0f) {
262+ pixels[off + i] = rgbclip;
263+ gRel += gDelta;
264+ gDelta += gDeltaDelta;
265+ i++;
266+ }
267+ // Slow fill for "in the heart"
268+ while (i < w && gRel < 1.0f) {
269+ int gIndex;
270+
271+ if (gRel <= 0) {
272+ gIndex = 0;
273+ } else {
274+ float fIndex = gRel * SQRT_LUT_SIZE;
275+ int iIndex = (int) (fIndex);
276+ float s0 = sqrtLut[iIndex];
277+ float s1 = sqrtLut[iIndex+1] - s0;
278+ fIndex = s0 + (fIndex - iIndex) * s1;
279+ gIndex = (int) (fIndex * fastGradientArraySize);
280+ }
281+
282+ // store the color at this point
283+ pixels[off + i] = gradient[gIndex];
284+
285+ // incremental calculation
286+ gRel += gDelta;
287+ gDelta += gDeltaDelta;
288+ i++;
289+ }
290+ // Quick fill to end of line for "out to the right"
291+ while (i < w) {
292+ pixels[off + i] = rgbclip;
293+ i++;
294+ }
295+
296+ off += adjust;
297+ rowX += a01;
298+ rowY += a11;
299+ }
300+ }
301+
302+ // SQRT_LUT_SIZE must be a power of 2 for the test above to work.
303+ private static final int SQRT_LUT_SIZE = (1 << 11);
304+ private static float sqrtLut[] = new float[SQRT_LUT_SIZE+1];
305+ static {
306+ for (int i = 0; i < sqrtLut.length; i++) {
307+ sqrtLut[i] = (float) Math.sqrt(i / ((float) SQRT_LUT_SIZE));
308+ }
309+ }
310+
311+ /**
312+ * Fill the raster, cycling the gradient colors when a point falls outside
313+ * of the perimeter of the 100% stop circle.
314+ *
315+ * This calculation first computes the intersection point of the line
316+ * from the focus through the current point in the raster, and the
317+ * perimeter of the gradient circle.
318+ *
319+ * Then it determines the percentage distance of the current point along
320+ * that line (focus is 0%, perimeter is 100%).
321+ *
322+ * Equation of a circle centered at (a,b) with radius r:
323+ * (x-a)^2 + (y-b)^2 = r^2
324+ * Equation of a line with slope m and y-intercept b:
325+ * y = mx + b
326+ * Replacing y in the circle equation and solving using the quadratic
327+ * formula produces the following set of equations. Constant factors have
328+ * been extracted out of the inner loop.
329+ */
330+ private void cyclicCircularGradientFillRaster(int pixels[], int off,
331+ int adjust,
332+ int x, int y,
333+ int w, int h)
334+ {
335+ // constant part of the C factor of the quadratic equation
336+ final double constC =
337+ -radiusSq + (centerX * centerX) + (centerY * centerY);
338+
339+ // coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
340+ double A, B, C;
341+
342+ // slope and y-intercept of the focus-perimeter line
343+ double slope, yintcpt;
344+
345+ // intersection with circle X,Y coordinate
346+ double solutionX, solutionY;
347+
348+ // constant parts of X, Y coordinates
349+ final float constX = (a00*x) + (a01*y) + a02;
350+ final float constY = (a10*x) + (a11*y) + a12;
351+
352+ // constants in inner loop quadratic formula
353+ final float precalc2 = 2 * centerY;
354+ final float precalc3 = -2 * centerX;
355+
356+ // value between 0 and 1 specifying position in the gradient
357+ float g;
358+
359+ // determinant of quadratic formula (should always be > 0)
360+ float det;
361+
362+ // sq distance from the current point to focus
363+ float currentToFocusSq;
364+
365+ // sq distance from the intersect point to focus
366+ float intersectToFocusSq;
367+
368+ // temp variables for change in X,Y squared
369+ float deltaXSq, deltaYSq;
370+
371+ // used to index pixels array
372+ int indexer = off;
373+
374+ // incremental index change for pixels array
375+ int pixInc = w+adjust;
376+
377+ // for every row
378+ for (int j = 0; j < h; j++) {
379+
380+ // user space point; these are constant from column to column
381+ float X = (a01*j) + constX;
382+ float Y = (a11*j) + constY;
383+
384+ // for every column (inner loop begins here)
385+ for (int i = 0; i < w; i++) {
386+
387+ if (X == focusX) {
388+ // special case to avoid divide by zero
389+ solutionX = focusX;
390+ solutionY = centerY;
391+ solutionY += (Y > focusY) ? trivial : -trivial;
392+ } else {
393+ // slope and y-intercept of the focus-perimeter line
394+ slope = (Y - focusY) / (X - focusX);
395+ yintcpt = Y - (slope * X);
396+
397+ // use the quadratic formula to calculate the
398+ // intersection point
399+ A = (slope * slope) + 1;
400+ B = precalc3 + (-2 * slope * (centerY - yintcpt));
401+ C = constC + (yintcpt* (yintcpt - precalc2));
402+
403+ det = (float)Math.sqrt((B * B) - (4 * A * C));
404+ solutionX = -B;
405+
406+ // choose the positive or negative root depending
407+ // on where the X coord lies with respect to the focus
408+ solutionX += (X < focusX)? -det : det;
409+ solutionX = solutionX / (2 * A); // divisor
410+ solutionY = (slope * solutionX) + yintcpt;
411+ }
412+
413+ // Calculate the square of the distance from the current point
414+ // to the focus and the square of the distance from the
415+ // intersection point to the focus. Want the squares so we can
416+ // do 1 square root after division instead of 2 before.
417+
418+ deltaXSq = X - focusX;
419+ deltaXSq = deltaXSq * deltaXSq;
420+
421+ deltaYSq = Y - focusY;
422+ deltaYSq = deltaYSq * deltaYSq;
423+
424+ currentToFocusSq = deltaXSq + deltaYSq;
425+
426+ deltaXSq = (float)solutionX - focusX;
427+ deltaXSq = deltaXSq * deltaXSq;
428+
429+ deltaYSq = (float)solutionY - focusY;
430+ deltaYSq = deltaYSq * deltaYSq;
431+
432+ intersectToFocusSq = deltaXSq + deltaYSq;
433+
434+ // get the percentage (0-1) of the current point along the
435+ // focus-circumference line
436+ g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);
437+
438+ // store the color at this point
439+ pixels[indexer + i] = indexIntoGradientsArrays(g);
440+
441+ // incremental change in X, Y
442+ X += a00;
443+ Y += a10;
444+ } //end inner loop
445+
446+ indexer += pixInc;
447+ } //end outer loop
448+ }
449+}