1 /* 2 * Copyright (c) 1997, 2014, 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.image.Raster; 29 import sun.awt.image.IntegerComponentRaster; 30 import java.awt.image.ColorModel; 31 import java.awt.image.DirectColorModel; 32 import java.awt.geom.Point2D; 33 import java.awt.geom.AffineTransform; 34 import java.awt.geom.NoninvertibleTransformException; 35 import java.lang.ref.WeakReference; 36 37 class GradientPaintContext implements PaintContext { 38 static ColorModel xrgbmodel = 39 new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff); 40 static ColorModel xbgrmodel = 41 new DirectColorModel(24, 0x000000ff, 0x0000ff00, 0x00ff0000); 42 43 static ColorModel cachedModel; 44 static WeakReference<Raster> cached; 45 46 static synchronized Raster getCachedRaster(ColorModel cm, int w, int h) { 47 if (cm == cachedModel) { 48 if (cached != null) { 49 Raster ras = cached.get(); 50 if (ras != null && 51 ras.getWidth() >= w && 52 ras.getHeight() >= h) 53 { 54 cached = null; 55 return ras; 56 } 57 } 58 } 59 return cm.createCompatibleWritableRaster(w, h); 60 } 61 62 static synchronized void putCachedRaster(ColorModel cm, Raster ras) { 63 if (cached != null) { 64 Raster cras = cached.get(); 65 if (cras != null) { 66 int cw = cras.getWidth(); 67 int ch = cras.getHeight(); 68 int iw = ras.getWidth(); 69 int ih = ras.getHeight(); 70 if (cw >= iw && ch >= ih) { 71 return; 72 } 73 if (cw * ch >= iw * ih) { 74 return; 75 } 76 } 77 } 78 cachedModel = cm; 79 cached = new WeakReference<>(ras); 80 } 81 82 double x1; 83 double y1; 84 double dx; 85 double dy; 86 boolean cyclic; 87 int interp[]; 88 Raster saved; 89 ColorModel model; 90 91 public GradientPaintContext(ColorModel cm, 92 Point2D p1, Point2D p2, AffineTransform xform, 93 Color c1, Color c2, boolean cyclic) { 94 // First calculate the distance moved in user space when 95 // we move a single unit along the X & Y axes in device space. 96 Point2D xvec = new Point2D.Double(1, 0); 97 Point2D yvec = new Point2D.Double(0, 1); 98 try { 99 AffineTransform inverse = xform.createInverse(); 100 inverse.deltaTransform(xvec, xvec); 101 inverse.deltaTransform(yvec, yvec); 102 } catch (NoninvertibleTransformException e) { 103 xvec.setLocation(0, 0); 104 yvec.setLocation(0, 0); 105 } 106 107 // Now calculate the (square of the) user space distance 108 // between the anchor points. This value equals: 109 // (UserVec . UserVec) 110 double udx = p2.getX() - p1.getX(); 111 double udy = p2.getY() - p1.getY(); 112 double ulenSq = udx * udx + udy * udy; 113 114 if (ulenSq <= Double.MIN_VALUE) { 115 dx = 0; 116 dy = 0; 117 } else { 118 // Now calculate the proportional distance moved along the 119 // vector from p1 to p2 when we move a unit along X & Y in 120 // device space. 121 // 122 // The length of the projection of the Device Axis Vector is 123 // its dot product with the Unit User Vector: 124 // (DevAxisVec . (UserVec / Len(UserVec)) 125 // 126 // The "proportional" length is that length divided again 127 // by the length of the User Vector: 128 // (DevAxisVec . (UserVec / Len(UserVec))) / Len(UserVec) 129 // which simplifies to: 130 // ((DevAxisVec . UserVec) / Len(UserVec)) / Len(UserVec) 131 // which simplifies to: 132 // (DevAxisVec . UserVec) / LenSquared(UserVec) 133 dx = (xvec.getX() * udx + xvec.getY() * udy) / ulenSq; 134 dy = (yvec.getX() * udx + yvec.getY() * udy) / ulenSq; 135 136 if (cyclic) { 137 dx = dx % 1.0; 138 dy = dy % 1.0; 139 } else { 140 // We are acyclic 141 if (dx < 0) { 142 // If we are using the acyclic form below, we need 143 // dx to be non-negative for simplicity of scanning 144 // across the scan lines for the transition points. 145 // To ensure that constraint, we negate the dx/dy 146 // values and swap the points and colors. 147 Point2D p = p1; p1 = p2; p2 = p; 148 Color c = c1; c1 = c2; c2 = c; 149 dx = -dx; 150 dy = -dy; 151 } 152 } 153 } 154 155 Point2D dp1 = xform.transform(p1, null); 156 this.x1 = dp1.getX(); 157 this.y1 = dp1.getY(); 158 159 this.cyclic = cyclic; 160 int rgb1 = c1.getRGB(); 161 int rgb2 = c2.getRGB(); 162 int a1 = (rgb1 >> 24) & 0xff; 163 int r1 = (rgb1 >> 16) & 0xff; 164 int g1 = (rgb1 >> 8) & 0xff; 165 int b1 = (rgb1 ) & 0xff; 166 int da = ((rgb2 >> 24) & 0xff) - a1; 167 int dr = ((rgb2 >> 16) & 0xff) - r1; 168 int dg = ((rgb2 >> 8) & 0xff) - g1; 169 int db = ((rgb2 ) & 0xff) - b1; 170 if (a1 == 0xff && da == 0) { 171 model = xrgbmodel; 172 if (cm instanceof DirectColorModel) { 173 DirectColorModel dcm = (DirectColorModel) cm; 174 int tmp = dcm.getAlphaMask(); 175 if ((tmp == 0 || tmp == 0xff) && 176 dcm.getRedMask() == 0xff && 177 dcm.getGreenMask() == 0xff00 && 178 dcm.getBlueMask() == 0xff0000) 179 { 180 model = xbgrmodel; 181 tmp = r1; r1 = b1; b1 = tmp; 182 tmp = dr; dr = db; db = tmp; 183 } 184 } 185 } else { 186 model = ColorModel.getRGBdefault(); 187 } 188 interp = new int[cyclic ? 513 : 257]; 189 for (int i = 0; i <= 256; i++) { 190 float rel = i / 256.0f; 191 int rgb = 192 (((int) (a1 + da * rel)) << 24) | 193 (((int) (r1 + dr * rel)) << 16) | 194 (((int) (g1 + dg * rel)) << 8) | 195 (((int) (b1 + db * rel)) ); 196 interp[i] = rgb; 197 if (cyclic) { 198 interp[512 - i] = rgb; 199 } 200 } 201 } 202 203 /** 204 * Release the resources allocated for the operation. 205 */ 206 public void dispose() { 207 if (saved != null) { 208 putCachedRaster(model, saved); 209 saved = null; 210 } 211 } 212 213 /** 214 * Return the ColorModel of the output. 215 */ 216 public ColorModel getColorModel() { 217 return model; 218 } 219 220 /** 221 * Return a Raster containing the colors generated for the graphics 222 * operation. 223 * @param x,y,w,h The area in device space for which colors are 224 * generated. 225 */ 226 public Raster getRaster(int x, int y, int w, int h) { 227 double rowrel = (x - x1) * dx + (y - y1) * dy; 228 229 Raster rast = saved; 230 if (rast == null || rast.getWidth() < w || rast.getHeight() < h) { 231 rast = getCachedRaster(model, w, h); 232 saved = rast; 233 } 234 IntegerComponentRaster irast = (IntegerComponentRaster) rast; 235 int off = irast.getDataOffset(0); 236 int adjust = irast.getScanlineStride() - w; 237 int[] pixels = irast.getDataStorage(); 238 239 if (cyclic) { 240 cycleFillRaster(pixels, off, adjust, w, h, rowrel, dx, dy); 241 } else { 242 clipFillRaster(pixels, off, adjust, w, h, rowrel, dx, dy); 243 } 244 245 irast.markDirty(); 246 247 return rast; 248 } 249 250 void cycleFillRaster(int[] pixels, int off, int adjust, int w, int h, 251 double rowrel, double dx, double dy) { 252 rowrel = rowrel % 2.0; 253 int irowrel = ((int) (rowrel * (1 << 30))) << 1; 254 int idx = (int) (-dx * (1 << 31)); 255 int idy = (int) (-dy * (1 << 31)); 256 while (--h >= 0) { 257 int icolrel = irowrel; 258 for (int j = w; j > 0; j--) { 259 pixels[off++] = interp[icolrel >>> 23]; 260 icolrel += idx; 261 } 262 263 off += adjust; 264 irowrel += idy; 265 } 266 } 267 268 void clipFillRaster(int[] pixels, int off, int adjust, int w, int h, 269 double rowrel, double dx, double dy) { 270 while (--h >= 0) { 271 double colrel = rowrel; 272 int j = w; 273 if (colrel <= 0.0) { 274 int rgb = interp[0]; 275 do { 276 pixels[off++] = rgb; 277 colrel += dx; 278 } while (--j > 0 && colrel <= 0.0); 279 } 280 while (colrel < 1.0 && --j >= 0) { 281 pixels[off++] = interp[(int) (colrel * 256)]; 282 colrel += dx; 283 } 284 if (j > 0) { 285 int rgb = interp[256]; 286 do { 287 pixels[off++] = rgb; 288 } while (--j > 0); 289 } 290 291 off += adjust; 292 rowrel += dy; 293 } 294 } 295 }