Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / awt / image / AffineTransformOp.java
blob1326a61c043d3c6c32d41958a1913d87f9392308
1 /* AffineTransformOp.java -- This class performs affine
2 transformation between two images or rasters in 2 dimensions.
3 Copyright (C) 2004 Free Software Foundation
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA.
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
39 package java.awt.image;
41 import java.awt.Graphics2D;
42 import java.awt.Rectangle;
43 import java.awt.RenderingHints;
44 import java.awt.geom.AffineTransform;
45 import java.awt.geom.NoninvertibleTransformException;
46 import java.awt.geom.Point2D;
47 import java.awt.geom.Rectangle2D;
48 import java.util.Arrays;
50 /**
51 * This class performs affine transformation between two images or
52 * rasters in 2 dimensions.
54 * @author Olga Rodimina (rodimina@redhat.com)
56 public class AffineTransformOp implements BufferedImageOp, RasterOp
58 public static final int TYPE_NEAREST_NEIGHBOR = 1;
60 public static final int TYPE_BILINEAR = 2;
62 /**
63 * @since 1.5.0
65 public static final int TYPE_BICUBIC = 3;
67 private AffineTransform transform;
68 private RenderingHints hints;
70 /**
71 * Construct AffineTransformOp with the given xform and interpolationType.
72 * Interpolation type can be TYPE_BILINEAR, TYPE_BICUBIC or
73 * TYPE_NEAREST_NEIGHBOR.
75 * @param xform AffineTransform that will applied to the source image
76 * @param interpolationType type of interpolation used
78 public AffineTransformOp (AffineTransform xform, int interpolationType)
80 this.transform = xform;
81 if (xform.getDeterminant() == 0)
82 throw new ImagingOpException(null);
84 switch (interpolationType)
86 case TYPE_BILINEAR:
87 hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
88 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
89 break;
90 case TYPE_BICUBIC:
91 hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
92 RenderingHints.VALUE_INTERPOLATION_BICUBIC);
93 break;
94 default:
95 hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
96 RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
101 * Construct AffineTransformOp with the given xform and rendering hints.
103 * @param xform AffineTransform that will applied to the source image
104 * @param hints rendering hints that will be used during transformation
106 public AffineTransformOp (AffineTransform xform, RenderingHints hints)
108 this.transform = xform;
109 this.hints = hints;
110 if (xform.getDeterminant() == 0)
111 throw new ImagingOpException(null);
115 * Creates empty BufferedImage with the size equal to that of the
116 * transformed image and correct number of bands. The newly created
117 * image is created with the specified ColorModel.
118 * If the ColorModel is equal to null, then image is created
119 * with the ColorModel of the source image.
121 * @param src source image
122 * @param destCM color model for the destination image
123 * @return new compatible destination image
125 public BufferedImage createCompatibleDestImage (BufferedImage src,
126 ColorModel destCM)
129 // if destCm is not specified, use color model of the source image
131 if (destCM == null)
132 destCM = src.getColorModel ();
134 return new BufferedImage (destCM,
135 createCompatibleDestRaster (src.getRaster ()),
136 src.isAlphaPremultiplied (),
137 null);
142 * Creates empty WritableRaster with the size equal to the transformed
143 * source raster and correct number of bands
145 * @param src source raster
146 * @throws RasterFormatException if resulting width or height of raster is 0
147 * @return new compatible raster
149 public WritableRaster createCompatibleDestRaster (Raster src)
151 Rectangle rect = (Rectangle) getBounds2D (src);
153 // throw RasterFormatException if resulting width or height of the
154 // transformed raster is 0
156 if (rect.getWidth () == 0 || rect.getHeight () == 0)
157 throw new RasterFormatException("width or height is 0");
159 return src.createCompatibleWritableRaster ((int) rect.getWidth (),
160 (int) rect.getHeight ());
164 * Transforms source image using transform specified at the constructor.
165 * The resulting transformed image is stored in the destination image.
167 * @param src source image
168 * @param dst destination image
169 * @return transformed source image
171 public final BufferedImage filter (BufferedImage src, BufferedImage dst)
174 if (dst == src)
175 throw new IllegalArgumentException ("src image cannot be the same as the dst image");
177 // If the destination image is null, then BufferedImage is
178 // created with ColorModel of the source image
180 if (dst == null)
181 dst = createCompatibleDestImage(src, src.getColorModel ());
183 // FIXME: Must check if color models of src and dst images are the same.
184 // If it is not, then source image should be converted to color model
185 // of the destination image
187 Graphics2D gr = (Graphics2D) dst.createGraphics ();
188 gr.setRenderingHints (hints);
189 gr.drawImage (src, transform, null);
190 return dst;
195 * Transforms source raster using transform specified at the constructor.
196 * The resulting raster is stored in the destination raster.
198 * @param src source raster
199 * @param dst destination raster
200 * @return transformed raster
202 public final WritableRaster filter (Raster src, WritableRaster dst)
204 if (dst == src)
205 throw new IllegalArgumentException("src image cannot be the same as"
206 + " the dst image");
208 if (dst == null)
209 dst = createCompatibleDestRaster(src);
211 if (src.getNumBands() != dst.getNumBands())
212 throw new IllegalArgumentException("src and dst must have same number"
213 + " of bands");
215 double[] dpts = new double[dst.getWidth() * 2];
216 double[] pts = new double[dst.getWidth() * 2];
217 for (int x = 0; x < dst.getWidth(); x++)
219 dpts[2 * x] = x + dst.getMinX();
220 dpts[2 * x + 1] = x;
222 Rectangle srcbounds = src.getBounds();
223 if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
225 for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
227 try {
228 transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
229 } catch (NoninvertibleTransformException e) {
230 // Can't happen since the constructor traps this
231 e.printStackTrace();
234 for (int x = 0; x < dst.getWidth(); x++)
236 if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
237 continue;
238 dst.setDataElements(x + dst.getMinX(), y,
239 src.getDataElements((int)pts[2 * x],
240 (int)pts[2 * x + 1],
241 null));
245 else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
247 double[] tmp = new double[4 * src.getNumBands()];
248 for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
250 try {
251 transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
252 } catch (NoninvertibleTransformException e) {
253 // Can't happen since the constructor traps this
254 e.printStackTrace();
257 for (int x = 0; x < dst.getWidth(); x++)
259 if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
260 continue;
261 int xx = (int)pts[2 * x];
262 int yy = (int)pts[2 * x + 1];
263 double dx = (pts[2 * x] - xx);
264 double dy = (pts[2 * x + 1] - yy);
266 // TODO write this more intelligently
267 if (xx == src.getMinX() + src.getWidth() - 1 ||
268 yy == src.getMinY() + src.getHeight() - 1)
270 // bottom or right edge
271 Arrays.fill(tmp, 0);
272 src.getPixel(xx, yy, tmp);
274 else
276 // Normal case
277 src.getPixels(xx, yy, 2, 2, tmp);
278 for (int b = 0; b < src.getNumBands(); b++)
279 tmp[b] = dx * dy * tmp[b]
280 + (1 - dx) * dy * tmp[b + src.getNumBands()]
281 + dx * (1 - dy) * tmp[b + 2 * src.getNumBands()]
282 + (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()];
284 dst.setPixel(x, y, tmp);
288 else
290 // Bicubic
291 throw new UnsupportedOperationException("not implemented yet");
294 return dst;
298 * Transforms source image using transform specified at the constructor and
299 * returns bounds of the transformed image.
301 * @param src image to be transformed
302 * @return bounds of the transformed image.
304 public final Rectangle2D getBounds2D (BufferedImage src)
306 return getBounds2D (src.getRaster());
310 * Returns bounds of the transformed raster.
312 * @param src raster to be transformed
313 * @return bounds of the transformed raster.
315 public final Rectangle2D getBounds2D (Raster src)
317 // determine new size for the transformed raster.
318 // Need to calculate transformed coordinates of the lower right
319 // corner of the raster. The upper left corner is always (0,0)
321 double x2 = (double) src.getWidth () + src.getMinX ();
322 double y2 = (double) src.getHeight () + src.getMinY ();
323 Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null);
325 Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ());
326 return rect.getBounds ();
330 * Returns interpolation type used during transformations
332 * @return interpolation type
334 public final int getInterpolationType ()
336 if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR))
337 return TYPE_BILINEAR;
338 else
339 return TYPE_NEAREST_NEIGHBOR;
342 /**
343 * Returns location of the transformed source point. The resulting point
344 * is stored in the dstPt if one is specified.
346 * @param srcPt point to be transformed
347 * @param dstPt destination point
348 * @return the location of the transformed source point.
350 public Point2D getPoint2D (Point2D srcPt, Point2D dstPt)
352 return transform.transform (srcPt, dstPt);
356 * Returns rendering hints that are used during transformation.
358 * @return rendering hints
360 public final RenderingHints getRenderingHints ()
362 return hints;
366 * Returns transform used in transformation between source and destination
367 * image.
369 * @return transform
371 public final AffineTransform getTransform ()
373 return transform;