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)
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., 51 Franklin Street, Fifth Floor, Boston, MA
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
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
;
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;
65 public static final int TYPE_BICUBIC
= 3;
67 private AffineTransform transform
;
68 private RenderingHints hints
;
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
)
87 hints
= new RenderingHints (RenderingHints
.KEY_INTERPOLATION
,
88 RenderingHints
.VALUE_INTERPOLATION_BILINEAR
);
91 hints
= new RenderingHints (RenderingHints
.KEY_INTERPOLATION
,
92 RenderingHints
.VALUE_INTERPOLATION_BICUBIC
);
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
;
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
,
129 // if destCm is not specified, use color model of the source image
132 destCM
= src
.getColorModel ();
134 return new BufferedImage (destCM
,
135 createCompatibleDestRaster (src
.getRaster ()),
136 src
.isAlphaPremultiplied (),
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
)
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
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);
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
)
205 throw new IllegalArgumentException("src image cannot be the same as"
209 dst
= createCompatibleDestRaster(src
);
211 if (src
.getNumBands() != dst
.getNumBands())
212 throw new IllegalArgumentException("src and dst must have same number"
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();
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
++)
228 transform
.inverseTransform(dpts
, 0, pts
, 0, dst
.getWidth() * 2);
229 } catch (NoninvertibleTransformException e
) {
230 // Can't happen since the constructor traps this
234 for (int x
= 0; x
< dst
.getWidth(); x
++)
236 if (!srcbounds
.contains(pts
[2 * x
], pts
[2 * x
+ 1]))
238 dst
.setDataElements(x
+ dst
.getMinX(), y
,
239 src
.getDataElements((int)pts
[2 * x
],
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
++)
251 transform
.inverseTransform(dpts
, 0, pts
, 0, dst
.getWidth() * 2);
252 } catch (NoninvertibleTransformException e
) {
253 // Can't happen since the constructor traps this
257 for (int x
= 0; x
< dst
.getWidth(); x
++)
259 if (!srcbounds
.contains(pts
[2 * x
], pts
[2 * x
+ 1]))
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
272 src
.getPixel(xx
, yy
, tmp
);
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
);
291 throw new UnsupportedOperationException("not implemented yet");
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
;
339 return TYPE_NEAREST_NEIGHBOR
;
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 final 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 ()
366 * Returns transform used in transformation between source and destination
371 public final AffineTransform
getTransform ()