Source code: com/arranger/jarl/trait/base/GradientColor.java
1 package com.arranger.jarl.trait.base;
2
3 import com.jhlabs.image.Gradient;
4 import sun.awt.image.IntegerComponentRaster;
5
6 import java.awt.*;
7 import java.awt.geom.AffineTransform;
8 import java.awt.geom.NoninvertibleTransformException;
9 import java.awt.geom.Point2D;
10 import java.awt.geom.Rectangle2D;
11 import java.awt.image.ColorModel;
12 import java.awt.image.Raster;
13 import java.lang.ref.WeakReference;
14
15 /**
16 * GradientColor
17 * Note: This is heavily based on java.awt.GradientPaintContext & java.awt.GradientPaint
18 */
19 public class GradientColor implements Paint {
20
21 protected Gradient m_gradient;
22
23 public GradientColor(Gradient gradient) {
24 m_gradient = gradient;
25 }
26
27 public PaintContext createContext(ColorModel cm,
28 Rectangle deviceBounds,
29 Rectangle2D userBounds,
30 AffineTransform xform,
31 RenderingHints hints) {
32
33 Point2D point1 = new Point2D.Double(userBounds.getX(), userBounds.getY());
34 Point2D point2 = new Point2D.Double(userBounds.getX() + userBounds.getWidth(), userBounds.getY() + userBounds.getHeight());
35 return new GradientPaintContext(point1, point2, xform, m_gradient.getMap());
36 }
37
38 public int getTransparency() {
39 return Transparency.OPAQUE;
40 }
41
42 public Gradient getGradient() {
43 return m_gradient;
44 }
45
46 static ColorModel cachedModel;
47 static WeakReference cached;
48
49 static synchronized Raster getCachedRaster(ColorModel cm, int w, int h) {
50 if (cm == cachedModel) {
51 if (cached != null) {
52 Raster ras = (Raster) cached.get();
53 if (ras != null &&
54 ras.getWidth() >= w &&
55 ras.getHeight() >= h) {
56 cached = null;
57 return ras;
58 }
59 }
60 }
61 return cm.createCompatibleWritableRaster(w, h);
62 }
63
64 static synchronized void putCachedRaster(ColorModel cm, Raster ras) {
65 if (cached != null) {
66 Raster cras = (Raster) cached.get();
67 if (cras != null) {
68 int cw = cras.getWidth();
69 int ch = cras.getHeight();
70 int iw = ras.getWidth();
71 int ih = ras.getHeight();
72 if (cw >= iw && ch >= ih) {
73 return;
74 }
75 if (cw * ch >= iw * ih) {
76 return;
77 }
78 }
79 }
80 cachedModel = cm;
81 cached = new WeakReference(ras);
82 }
83
84 protected static class GradientPaintContext implements PaintContext {
85
86 double x1;
87 double y1;
88 double dx;
89 double dy;
90 int interp[];
91 Raster saved;
92 ColorModel model;
93
94 public GradientPaintContext(Point2D p1, Point2D p2, AffineTransform xform,
95 int[] map) {
96 // First calculate the distance moved in user space when
97 // we move a single unit along the X & Y axes in device space.
98 Point2D xvec = new Point2D.Double(1, 0);
99 Point2D yvec = new Point2D.Double(0, 1);
100 try {
101 AffineTransform inverse = xform.createInverse();
102 inverse.deltaTransform(xvec, xvec);
103 inverse.deltaTransform(yvec, yvec);
104 } catch (NoninvertibleTransformException e) {
105 xvec.setLocation(0, 0);
106 yvec.setLocation(0, 0);
107 }
108
109 // Now calculate the (square of the) user space distance
110 // between the anchor points. This value equals:
111 // (UserVec . UserVec)
112 double udx = p2.getX() - p1.getX();
113 double udy = p2.getY() - p1.getY();
114 double ulenSq = udx * udx + udy * udy;
115
116 if (ulenSq <= Double.MIN_VALUE) {
117 dx = 0;
118 dy = 0;
119 } else {
120 // Now calculate the proportional distance moved along the
121 // vector from p1 to p2 when we move a unit along X & Y in
122 // device space.
123 //
124 // The length of the projection of the Device Axis Vector is
125 // its dot product with the Unit User Vector:
126 // (DevAxisVec . (UserVec / Len(UserVec))
127 //
128 // The "proportional" length is that length divided again
129 // by the length of the User Vector:
130 // (DevAxisVec . (UserVec / Len(UserVec))) / Len(UserVec)
131 // which simplifies to:
132 // ((DevAxisVec . UserVec) / Len(UserVec)) / Len(UserVec)
133 // which simplifies to:
134 // (DevAxisVec . UserVec) / LenSquared(UserVec)
135 dx = (xvec.getX() * udx + xvec.getY() * udy) / ulenSq;
136 dy = (yvec.getX() * udx + yvec.getY() * udy) / ulenSq;
137
138
139 // We are acyclic
140 if (dx < 0) {
141 // If we are using the acyclic form below, we need
142 // dx to be non-negative for simplicity of scanning
143 // across the scan lines for the transition points.
144 // To ensure that constraint, we negate the dx/dy
145 // values and swap the points and colors.
146 Point2D p = p1;
147 p1 = p2;
148 p2 = p;
149 dx = -dx;
150 dy = -dy;
151 }
152 }
153
154 Point2D dp1 = xform.transform(p1, null);
155 this.x1 = dp1.getX();
156 this.y1 = dp1.getY();
157
158 model = ColorModel.getRGBdefault();
159 interp = map;
160 }
161
162 /**
163 * Release the resources allocated for the operation.
164 */
165 public void dispose() {
166 if (saved != null) {
167 putCachedRaster(model, saved);
168 saved = null;
169 }
170 }
171
172 /**
173 * Return the ColorModel of the output.
174 */
175 public ColorModel getColorModel() {
176 return model;
177 }
178
179 /**
180 * Return a Raster containing the colors generated for the graphics
181 * operation.
182 */
183 public Raster getRaster(int x, int y, int w, int h) {
184 double rowrel = (x - x1) * dx + (y - y1) * dy;
185
186 Raster rast = saved;
187 if (rast == null || rast.getWidth() < w || rast.getHeight() < h) {
188 rast = getCachedRaster(model, w, h);
189 saved = rast;
190 }
191 IntegerComponentRaster irast = (IntegerComponentRaster) rast;
192 int off = irast.getDataOffset(0);
193 int adjust = irast.getScanlineStride() - w;
194 int[] pixels = irast.getDataStorage();
195
196 clipFillRaster(pixels, off, adjust, w, h, rowrel, dx, dy);
197
198 return rast;
199 }
200
201 void cycleFillRaster(int[] pixels, int off, int adjust, int w, int h,
202 double rowrel, double dx, double dy) {
203 rowrel = rowrel % 2.0;
204 int irowrel = ((int) (rowrel * (1 << 30))) << 1;
205 int idx = (int) (-dx * (1 << 31));
206 int idy = (int) (-dy * (1 << 31));
207 while (--h >= 0) {
208 int icolrel = irowrel;
209 for (int j = w; j > 0; j--) {
210 pixels[off++] = interp[icolrel >>> 23];
211 icolrel += idx;
212 }
213
214 off += adjust;
215 irowrel += idy;
216 }
217 }
218
219 void clipFillRaster(int[] pixels, int off, int adjust, int w, int h,
220 double rowrel, double dx, double dy) {
221 while (--h >= 0) {
222 double colrel = rowrel;
223 int j = w;
224 if (colrel <= 0.0) {
225 int rgb = interp[0];
226 do {
227 pixels[off++] = rgb;
228 colrel += dx;
229 } while (--j > 0 && colrel <= 0.0);
230 }
231 while (colrel < 1.0 && --j >= 0) {
232 pixels[off++] = interp[(int) (colrel * (interp.length - 1))];
233 colrel += dx;
234 }
235 if (j > 0) {
236 int rgb = interp[(interp.length - 1)];
237 do {
238 pixels[off++] = rgb;
239 } while (--j > 0);
240 }
241
242 off += adjust;
243 rowrel += dy;
244 }
245 }
246 };
247 }