summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/java/awt/image/AffineTransformOp.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/awt/image/AffineTransformOp.java')
-rw-r--r--libjava/classpath/java/awt/image/AffineTransformOp.java375
1 files changed, 375 insertions, 0 deletions
diff --git a/libjava/classpath/java/awt/image/AffineTransformOp.java b/libjava/classpath/java/awt/image/AffineTransformOp.java
new file mode 100644
index 00000000000..f11066e4e3d
--- /dev/null
+++ b/libjava/classpath/java/awt/image/AffineTransformOp.java
@@ -0,0 +1,375 @@
+/* AffineTransformOp.java -- This class performs affine
+ transformation between two images or rasters in 2 dimensions.
+ Copyright (C) 2004 Free Software Foundation
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.awt.image;
+
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Arrays;
+
+/**
+ * This class performs affine transformation between two images or
+ * rasters in 2 dimensions.
+ *
+ * @author Olga Rodimina (rodimina@redhat.com)
+ */
+public class AffineTransformOp implements BufferedImageOp, RasterOp
+{
+ public static final int TYPE_NEAREST_NEIGHBOR = 1;
+
+ public static final int TYPE_BILINEAR = 2;
+
+ /**
+ * @since 1.5.0
+ */
+ public static final int TYPE_BICUBIC = 3;
+
+ private AffineTransform transform;
+ private RenderingHints hints;
+
+ /**
+ * Construct AffineTransformOp with the given xform and interpolationType.
+ * Interpolation type can be TYPE_BILINEAR, TYPE_BICUBIC or
+ * TYPE_NEAREST_NEIGHBOR.
+ *
+ * @param xform AffineTransform that will applied to the source image
+ * @param interpolationType type of interpolation used
+ */
+ public AffineTransformOp (AffineTransform xform, int interpolationType)
+ {
+ this.transform = xform;
+ if (xform.getDeterminant() == 0)
+ throw new ImagingOpException(null);
+
+ switch (interpolationType)
+ {
+ case TYPE_BILINEAR:
+ hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ break;
+ case TYPE_BICUBIC:
+ hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ break;
+ default:
+ hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+ }
+ }
+
+ /**
+ * Construct AffineTransformOp with the given xform and rendering hints.
+ *
+ * @param xform AffineTransform that will applied to the source image
+ * @param hints rendering hints that will be used during transformation
+ */
+ public AffineTransformOp (AffineTransform xform, RenderingHints hints)
+ {
+ this.transform = xform;
+ this.hints = hints;
+ if (xform.getDeterminant() == 0)
+ throw new ImagingOpException(null);
+ }
+
+ /**
+ * Creates empty BufferedImage with the size equal to that of the
+ * transformed image and correct number of bands. The newly created
+ * image is created with the specified ColorModel.
+ * If the ColorModel is equal to null, then image is created
+ * with the ColorModel of the source image.
+ *
+ * @param src source image
+ * @param destCM color model for the destination image
+ * @return new compatible destination image
+ */
+ public BufferedImage createCompatibleDestImage (BufferedImage src,
+ ColorModel destCM)
+ {
+
+ // if destCm is not specified, use color model of the source image
+
+ if (destCM == null)
+ destCM = src.getColorModel ();
+
+ return new BufferedImage (destCM,
+ createCompatibleDestRaster (src.getRaster ()),
+ src.isAlphaPremultiplied (),
+ null);
+
+ }
+
+ /**
+ * Creates empty WritableRaster with the size equal to the transformed
+ * source raster and correct number of bands
+ *
+ * @param src source raster
+ * @throws RasterFormatException if resulting width or height of raster is 0
+ * @return new compatible raster
+ */
+ public WritableRaster createCompatibleDestRaster (Raster src)
+ {
+ Rectangle rect = (Rectangle) getBounds2D (src);
+
+ // throw RasterFormatException if resulting width or height of the
+ // transformed raster is 0
+
+ if (rect.getWidth () == 0 || rect.getHeight () == 0)
+ throw new RasterFormatException("width or height is 0");
+
+ return src.createCompatibleWritableRaster ((int) rect.getWidth (),
+ (int) rect.getHeight ());
+ }
+
+ /**
+ * Transforms source image using transform specified at the constructor.
+ * The resulting transformed image is stored in the destination image.
+ *
+ * @param src source image
+ * @param dst destination image
+ * @return transformed source image
+ */
+ public final BufferedImage filter (BufferedImage src, BufferedImage dst)
+ {
+
+ if (dst == src)
+ throw new IllegalArgumentException ("src image cannot be the same as the dst image");
+
+ // If the destination image is null, then BufferedImage is
+ // created with ColorModel of the source image
+
+ if (dst == null)
+ dst = createCompatibleDestImage(src, src.getColorModel ());
+
+ // FIXME: Must check if color models of src and dst images are the same.
+ // If it is not, then source image should be converted to color model
+ // of the destination image
+
+ Graphics2D gr = (Graphics2D) dst.createGraphics ();
+ gr.setRenderingHints (hints);
+ gr.drawImage (src, transform, null);
+ return dst;
+
+ }
+
+ /**
+ * Transforms source raster using transform specified at the constructor.
+ * The resulting raster is stored in the destination raster.
+ *
+ * @param src source raster
+ * @param dst destination raster
+ * @return transformed raster
+ */
+ public final WritableRaster filter (Raster src, WritableRaster dst)
+ {
+ if (dst == src)
+ throw new IllegalArgumentException("src image cannot be the same as"
+ + " the dst image");
+
+ if (dst == null)
+ dst = createCompatibleDestRaster(src);
+
+ if (src.getNumBands() != dst.getNumBands())
+ throw new IllegalArgumentException("src and dst must have same number"
+ + " of bands");
+
+ double[] dpts = new double[dst.getWidth() * 2];
+ double[] pts = new double[dst.getWidth() * 2];
+ for (int x = 0; x < dst.getWidth(); x++)
+ {
+ dpts[2 * x] = x + dst.getMinX();
+ dpts[2 * x + 1] = x;
+ }
+ Rectangle srcbounds = src.getBounds();
+ if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
+ {
+ for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
+ {
+ try {
+ transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
+ } catch (NoninvertibleTransformException e) {
+ // Can't happen since the constructor traps this
+ e.printStackTrace();
+ }
+
+ for (int x = 0; x < dst.getWidth(); x++)
+ {
+ if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
+ continue;
+ dst.setDataElements(x + dst.getMinX(), y,
+ src.getDataElements((int)pts[2 * x],
+ (int)pts[2 * x + 1],
+ null));
+ }
+ }
+ }
+ else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
+ {
+ double[] tmp = new double[4 * src.getNumBands()];
+ for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
+ {
+ try {
+ transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
+ } catch (NoninvertibleTransformException e) {
+ // Can't happen since the constructor traps this
+ e.printStackTrace();
+ }
+
+ for (int x = 0; x < dst.getWidth(); x++)
+ {
+ if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
+ continue;
+ int xx = (int)pts[2 * x];
+ int yy = (int)pts[2 * x + 1];
+ double dx = (pts[2 * x] - xx);
+ double dy = (pts[2 * x + 1] - yy);
+
+ // TODO write this more intelligently
+ if (xx == src.getMinX() + src.getWidth() - 1 ||
+ yy == src.getMinY() + src.getHeight() - 1)
+ {
+ // bottom or right edge
+ Arrays.fill(tmp, 0);
+ src.getPixel(xx, yy, tmp);
+ }
+ else
+ {
+ // Normal case
+ src.getPixels(xx, yy, 2, 2, tmp);
+ for (int b = 0; b < src.getNumBands(); b++)
+ tmp[b] = dx * dy * tmp[b]
+ + (1 - dx) * dy * tmp[b + src.getNumBands()]
+ + dx * (1 - dy) * tmp[b + 2 * src.getNumBands()]
+ + (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()];
+ }
+ dst.setPixel(x, y, tmp);
+ }
+ }
+ }
+ else
+ {
+ // Bicubic
+ throw new UnsupportedOperationException("not implemented yet");
+ }
+
+ return dst;
+ }
+
+ /**
+ * Transforms source image using transform specified at the constructor and
+ * returns bounds of the transformed image.
+ *
+ * @param src image to be transformed
+ * @return bounds of the transformed image.
+ */
+ public final Rectangle2D getBounds2D (BufferedImage src)
+ {
+ return getBounds2D (src.getRaster());
+ }
+
+ /**
+ * Returns bounds of the transformed raster.
+ *
+ * @param src raster to be transformed
+ * @return bounds of the transformed raster.
+ */
+ public final Rectangle2D getBounds2D (Raster src)
+ {
+ // determine new size for the transformed raster.
+ // Need to calculate transformed coordinates of the lower right
+ // corner of the raster. The upper left corner is always (0,0)
+
+ double x2 = (double) src.getWidth () + src.getMinX ();
+ double y2 = (double) src.getHeight () + src.getMinY ();
+ Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null);
+
+ Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ());
+ return rect.getBounds ();
+ }
+
+ /**
+ * Returns interpolation type used during transformations
+ *
+ * @return interpolation type
+ */
+ public final int getInterpolationType ()
+ {
+ if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR))
+ return TYPE_BILINEAR;
+ else
+ return TYPE_NEAREST_NEIGHBOR;
+ }
+
+ /**
+ * Returns location of the transformed source point. The resulting point
+ * is stored in the dstPt if one is specified.
+ *
+ * @param srcPt point to be transformed
+ * @param dstPt destination point
+ * @return the location of the transformed source point.
+ */
+ public Point2D getPoint2D (Point2D srcPt, Point2D dstPt)
+ {
+ return transform.transform (srcPt, dstPt);
+ }
+
+ /**
+ * Returns rendering hints that are used during transformation.
+ *
+ * @return rendering hints
+ */
+ public final RenderingHints getRenderingHints ()
+ {
+ return hints;
+ }
+
+ /**
+ * Returns transform used in transformation between source and destination
+ * image.
+ *
+ * @return transform
+ */
+ public final AffineTransform getTransform ()
+ {
+ return transform;
+ }
+}
OpenPOWER on IntegriCloud