001 /*
002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt;
027
028 import java.awt.image.Raster;
029 import sun.awt.image.IntegerComponentRaster;
030 import java.awt.image.ColorModel;
031 import java.awt.image.DirectColorModel;
032 import java.awt.geom.Point2D;
033 import java.awt.geom.AffineTransform;
034 import java.awt.geom.NoninvertibleTransformException;
035 import java.lang.ref.WeakReference;
036
037 class GradientPaintContext implements PaintContext {
038 static ColorModel xrgbmodel = new DirectColorModel(24, 0x00ff0000,
039 0x0000ff00, 0x000000ff);
040 static ColorModel xbgrmodel = new DirectColorModel(24, 0x000000ff,
041 0x0000ff00, 0x00ff0000);
042
043 static ColorModel cachedModel;
044 static WeakReference cached;
045
046 static synchronized Raster getCachedRaster(ColorModel cm, int w,
047 int h) {
048 if (cm == cachedModel) {
049 if (cached != null) {
050 Raster ras = (Raster) cached.get();
051 if (ras != null && ras.getWidth() >= w
052 && ras.getHeight() >= h) {
053 cached = null;
054 return ras;
055 }
056 }
057 }
058 return cm.createCompatibleWritableRaster(w, h);
059 }
060
061 static synchronized void putCachedRaster(ColorModel cm, Raster ras) {
062 if (cached != null) {
063 Raster cras = (Raster) cached.get();
064 if (cras != null) {
065 int cw = cras.getWidth();
066 int ch = cras.getHeight();
067 int iw = ras.getWidth();
068 int ih = ras.getHeight();
069 if (cw >= iw && ch >= ih) {
070 return;
071 }
072 if (cw * ch >= iw * ih) {
073 return;
074 }
075 }
076 }
077 cachedModel = cm;
078 cached = new WeakReference(ras);
079 }
080
081 double x1;
082 double y1;
083 double dx;
084 double dy;
085 boolean cyclic;
086 int interp[];
087 Raster saved;
088 ColorModel model;
089
090 public GradientPaintContext(ColorModel cm, Point2D p1, Point2D p2,
091 AffineTransform xform, Color c1, Color c2, boolean cyclic) {
092 // First calculate the distance moved in user space when
093 // we move a single unit along the X & Y axes in device space.
094 Point2D xvec = new Point2D.Double(1, 0);
095 Point2D yvec = new Point2D.Double(0, 1);
096 try {
097 AffineTransform inverse = xform.createInverse();
098 inverse.deltaTransform(xvec, xvec);
099 inverse.deltaTransform(yvec, yvec);
100 } catch (NoninvertibleTransformException e) {
101 xvec.setLocation(0, 0);
102 yvec.setLocation(0, 0);
103 }
104
105 // Now calculate the (square of the) user space distance
106 // between the anchor points. This value equals:
107 // (UserVec . UserVec)
108 double udx = p2.getX() - p1.getX();
109 double udy = p2.getY() - p1.getY();
110 double ulenSq = udx * udx + udy * udy;
111
112 if (ulenSq <= Double.MIN_VALUE) {
113 dx = 0;
114 dy = 0;
115 } else {
116 // Now calculate the proportional distance moved along the
117 // vector from p1 to p2 when we move a unit along X & Y in
118 // device space.
119 //
120 // The length of the projection of the Device Axis Vector is
121 // its dot product with the Unit User Vector:
122 // (DevAxisVec . (UserVec / Len(UserVec))
123 //
124 // The "proportional" length is that length divided again
125 // by the length of the User Vector:
126 // (DevAxisVec . (UserVec / Len(UserVec))) / Len(UserVec)
127 // which simplifies to:
128 // ((DevAxisVec . UserVec) / Len(UserVec)) / Len(UserVec)
129 // which simplifies to:
130 // (DevAxisVec . UserVec) / LenSquared(UserVec)
131 dx = (xvec.getX() * udx + xvec.getY() * udy) / ulenSq;
132 dy = (yvec.getX() * udx + yvec.getY() * udy) / ulenSq;
133
134 if (cyclic) {
135 dx = dx % 1.0;
136 dy = dy % 1.0;
137 } else {
138 // We are acyclic
139 if (dx < 0) {
140 // If we are using the acyclic form below, we need
141 // dx to be non-negative for simplicity of scanning
142 // across the scan lines for the transition points.
143 // To ensure that constraint, we negate the dx/dy
144 // values and swap the points and colors.
145 Point2D p = p1;
146 p1 = p2;
147 p2 = p;
148 Color c = c1;
149 c1 = c2;
150 c2 = c;
151 dx = -dx;
152 dy = -dy;
153 }
154 }
155 }
156
157 Point2D dp1 = xform.transform(p1, null);
158 this .x1 = dp1.getX();
159 this .y1 = dp1.getY();
160
161 this .cyclic = cyclic;
162 int rgb1 = c1.getRGB();
163 int rgb2 = c2.getRGB();
164 int a1 = (rgb1 >> 24) & 0xff;
165 int r1 = (rgb1 >> 16) & 0xff;
166 int g1 = (rgb1 >> 8) & 0xff;
167 int b1 = (rgb1) & 0xff;
168 int da = ((rgb2 >> 24) & 0xff) - a1;
169 int dr = ((rgb2 >> 16) & 0xff) - r1;
170 int dg = ((rgb2 >> 8) & 0xff) - g1;
171 int db = ((rgb2) & 0xff) - b1;
172 if (a1 == 0xff && da == 0) {
173 model = xrgbmodel;
174 if (cm instanceof DirectColorModel) {
175 DirectColorModel dcm = (DirectColorModel) cm;
176 int tmp = dcm.getAlphaMask();
177 if ((tmp == 0 || tmp == 0xff)
178 && dcm.getRedMask() == 0xff
179 && dcm.getGreenMask() == 0xff00
180 && dcm.getBlueMask() == 0xff0000) {
181 model = xbgrmodel;
182 tmp = r1;
183 r1 = b1;
184 b1 = tmp;
185 tmp = dr;
186 dr = db;
187 db = tmp;
188 }
189 }
190 } else {
191 model = ColorModel.getRGBdefault();
192 }
193 interp = new int[cyclic ? 513 : 257];
194 for (int i = 0; i <= 256; i++) {
195 float rel = i / 256.0f;
196 int rgb = (((int) (a1 + da * rel)) << 24)
197 | (((int) (r1 + dr * rel)) << 16)
198 | (((int) (g1 + dg * rel)) << 8)
199 | (((int) (b1 + db * rel)));
200 interp[i] = rgb;
201 if (cyclic) {
202 interp[512 - i] = rgb;
203 }
204 }
205 }
206
207 /**
208 * Release the resources allocated for the operation.
209 */
210 public void dispose() {
211 if (saved != null) {
212 putCachedRaster(model, saved);
213 saved = null;
214 }
215 }
216
217 /**
218 * Return the ColorModel of the output.
219 */
220 public ColorModel getColorModel() {
221 return model;
222 }
223
224 /**
225 * Return a Raster containing the colors generated for the graphics
226 * operation.
227 * @param x,y,w,h The area in device space for which colors are
228 * generated.
229 */
230 public Raster getRaster(int x, int y, int w, int h) {
231 double rowrel = (x - x1) * dx + (y - y1) * dy;
232
233 Raster rast = saved;
234 if (rast == null || rast.getWidth() < w || rast.getHeight() < h) {
235 rast = getCachedRaster(model, w, h);
236 saved = rast;
237 }
238 IntegerComponentRaster irast = (IntegerComponentRaster) rast;
239 int off = irast.getDataOffset(0);
240 int adjust = irast.getScanlineStride() - w;
241 int[] pixels = irast.getDataStorage();
242
243 if (cyclic) {
244 cycleFillRaster(pixels, off, adjust, w, h, rowrel, dx, dy);
245 } else {
246 clipFillRaster(pixels, off, adjust, w, h, rowrel, dx, dy);
247 }
248
249 irast.markDirty();
250
251 return rast;
252 }
253
254 void cycleFillRaster(int[] pixels, int off, int adjust, int w,
255 int h, double rowrel, double dx, double dy) {
256 rowrel = rowrel % 2.0;
257 int irowrel = ((int) (rowrel * (1 << 30))) << 1;
258 int idx = (int) (-dx * (1 << 31));
259 int idy = (int) (-dy * (1 << 31));
260 while (--h >= 0) {
261 int icolrel = irowrel;
262 for (int j = w; j > 0; j--) {
263 pixels[off++] = interp[icolrel >>> 23];
264 icolrel += idx;
265 }
266
267 off += adjust;
268 irowrel += idy;
269 }
270 }
271
272 void clipFillRaster(int[] pixels, int off, int adjust, int w,
273 int h, double rowrel, double dx, double dy) {
274 while (--h >= 0) {
275 double colrel = rowrel;
276 int j = w;
277 if (colrel <= 0.0) {
278 int rgb = interp[0];
279 do {
280 pixels[off++] = rgb;
281 colrel += dx;
282 } while (--j > 0 && colrel <= 0.0);
283 }
284 while (colrel < 1.0 && --j >= 0) {
285 pixels[off++] = interp[(int) (colrel * 256)];
286 colrel += dx;
287 }
288 if (j > 0) {
289 int rgb = interp[256];
290 do {
291 pixels[off++] = rgb;
292 } while (--j > 0);
293 }
294
295 off += adjust;
296 rowrel += dy;
297 }
298 }
299 }
|