/*
* ImageI/O-Ext - OpenSource Java Image translation Library
* http://www.geo-solutions.it/
* http://java.net/projects/imageio-ext/
* (C) 2007 - 2009, GeoSolutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*/
package it.geosolutions.imageio.matfile5.sas;
import it.geosolutions.imageio.utilities.Utilities;
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.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RasterFormatException;
import java.awt.image.RasterOp;
import java.awt.image.WritableRaster;
class SASAffineTransformOp implements BufferedImageOp, RasterOp {
AffineTransform xform;
AffineTransform inv_xform;
private RenderingHints hints = null;
public SASAffineTransformOp(AffineTransform xform,
final RenderingHints hints) {
this.hints = hints;
this.xform = (AffineTransform)xform.clone();
try {
inv_xform = xform.createInverse();
} catch (NoninvertibleTransformException e) {
}
}
public BufferedImage filter (BufferedImage src, BufferedImage dst){
// create destination image if needed
if (dst == null) {
dst = createCompatibleDestImage(src, null);
}
WritableRaster wsrc = src.getRaster();
WritableRaster wdst = dst.getRaster();
filter(wsrc, wdst);
return dst;
}
public Point2D mapSourcePoint(Point2D sourcePt, Point2D destPt) {
Utilities.checkNotNull(sourcePt, "The provided source point is null");
Utilities.checkNotNull(destPt, "The provided destination point is null");
sourcePt.setLocation(sourcePt.getX() + 0.5, sourcePt.getY() + 0.5);
Point2D dpt = xform.transform(sourcePt, destPt);
sourcePt.setLocation(sourcePt.getX() - 0.5, sourcePt.getY() - 0.5);
dpt.setLocation(dpt.getX() - 0.5, dpt.getY() - 0.5);
return dpt;
}
/**
* Computes the source point corresponding to the supplied point.
*
* @param destPt the position in destination image coordinates
* to map to source image coordinates.
*
* @return a <code>Point2D</code> of the same class as
* <code>destPt</code>.
*
* @throws IllegalArgumentException if <code>destPt</code> is
* <code>null</code>.
*
* @since JAI 1.1.2
*/
public Point2D mapDestPoint(Point2D destPt, Point2D sourcePt) {
Utilities.checkNotNull(sourcePt, "The provided source point is null");
Utilities.checkNotNull(destPt, "The provided destination point is null");
destPt.setLocation(destPt.getX() + 0.5, destPt.getY() + 0.5);
Point2D spt = inv_xform.transform(destPt, sourcePt);
destPt.setLocation(destPt.getX() - 0.5, destPt.getY() - 0.5);
spt.setLocation(spt.getX() - 0.5, spt.getY() - 0.5);
return spt;
}
public BufferedImage createCompatibleDestImage (BufferedImage src,
ColorModel destCM, final Rectangle dstRegion) {
BufferedImage image;
Rectangle r = getBounds2D(src).getBounds();
// If r.x (or r.y) is < 0, then we want to only create an image
// that is in the positive range.
// If r.x (or r.y) is > 0, then we need to create an image that
// includes the translation.
int w = r.x + r.width;
int h = r.y + r.height;
if (w <= 0) {
throw new RasterFormatException("Transformed width ("+w+
") is less than or equal to 0.");
}
if (h <= 0) {
throw new RasterFormatException("Transformed height ("+h+
") is less than or equal to 0.");
}
if (dstRegion != null){
w = dstRegion.width;
h = dstRegion.height;
}
if (destCM == null) {
ColorModel cm = src.getColorModel();
image = new BufferedImage(cm,
src.getRaster().createCompatibleWritableRaster(w,h),
cm.isAlphaPremultiplied(), null);
}
else {
image = new BufferedImage(destCM,
destCM.createCompatibleWritableRaster(w,h),
destCM.isAlphaPremultiplied(), null);
}
return image;
}
/**
* Creates a zeroed destination image with the correct size and number of
* bands. A <CODE>RasterFormatException</CODE> may be thrown if the
* transformed width or height is equal to 0.
* <p>
* If <CODE>destCM</CODE> is null,
* an appropriate <CODE>ColorModel</CODE> is used; this
* <CODE>ColorModel</CODE> may have
* an alpha channel even if the source <CODE>ColorModel</CODE> is opaque.
*
* @param src The <CODE>BufferedImage</CODE> to be transformed.
* @param destCM <CODE>ColorModel</CODE> of the destination. If null,
* an appropriate <CODE>ColorModel</CODE> is used.
*
* @return The zeroed destination image.
*/
public BufferedImage createCompatibleDestImage (BufferedImage src,
ColorModel destCM) {
return createCompatibleDestImage(src, destCM, null);
}
public Rectangle2D getBounds2D (BufferedImage src) {
return getBounds2D(src.getRaster());
}
public Rectangle2D getBounds2D (Raster src) {
int w = src.getWidth();
int h = src.getHeight();
// Get the bounding box of the src and transform the corners
float[] pts = {0, 0, w, 0, w, h, 0, h};
xform.transform(pts, 0, pts, 0, 4);
// Get the min, max of the dst
float fmaxX = pts[0];
float fmaxY = pts[1];
float fminX = pts[0];
float fminY = pts[1];
for (int i=2; i < 8; i+=2) {
if (pts[i] > fmaxX) {
fmaxX = pts[i];
}
else if (pts[i] < fminX) {
fminX = pts[i];
}
if (pts[i+1] > fmaxY) {
fmaxY = pts[i+1];
}
else if (pts[i+1] < fminY) {
fminY = pts[i+1];
}
}
return new Rectangle2D.Float(fminX, fminY, fmaxX-fminX, fmaxY-fminY);
}
public Point2D getPoint2D(Point2D src, Point2D dst) {
if (dst == null)
dst = (Point2D) src.clone();
else
dst.setLocation(src);
return dst;
}
public RenderingHints getRenderingHints() {
return hints;
}
public WritableRaster createCompatibleDestRaster(Raster src) {
return src.createCompatibleWritableRaster();
}
public WritableRaster filter(Raster src, WritableRaster dst) {
if (dst == null)
dst = src.createCompatibleWritableRaster();
// Required sanity checks
if (src.getNumBands() != 2)
throw new IllegalArgumentException();
if (dst.getNumBands() != 2)
throw new IllegalArgumentException();
final int minX = src.getMinX();
final int minY = src.getMinY();
final int width = src.getWidth();
final int height = src.getHeight();
final int maxX = minX + width -1;
final int maxY = minY + height -1;
final int minXD = dst.getMinX();
final int minYD = dst.getMinY();
final int widthD = dst.getWidth();
final int heightD = dst.getHeight();
final int maxXD = minXD + widthD -1;
final int maxYD = minYD + heightD -1;
Object pixel = null;
final int dataType = src.getSampleModel().getDataType();
if (dataType == DataBuffer.TYPE_DOUBLE)
pixel = new double[2];
else if (dataType == DataBuffer.TYPE_FLOAT)
pixel = new float[2];
else if (dataType == DataBuffer.TYPE_INT)
pixel = new int[2];
else
throw new IllegalArgumentException("Unsupported datatype");
Point2D srcPt = new Point2D.Float(0, 0);
Point2D destPt = new Point2D.Float(0, 0);
// for (int i=minY;i<=maxY;i++){
// for (int j=minX;j<=maxX;j++){
// src.getDataElements(j, i, pixel);
// srcPt.setLocation(j, i);
// destPt = mapSourcePoint(srcPt, destPt);
// final int nearestX = findNearest(destPt.getX(), minXD, maxXD);
// final int nearestY = findNearest(destPt.getY(), minYD, maxYD);
// dst.setDataElements(nearestX, nearestY, pixel);
// }
// }
for (int i=minYD;i<=maxYD;i++){
for (int j=minXD;j<=maxXD;j++){
destPt.setLocation(j,i);
srcPt = mapDestPoint(destPt,srcPt);
final int nearestX = findNearest(srcPt.getX(), minX, maxX);
final int nearestY = findNearest(srcPt.getY(), minY, maxY);
src.getDataElements(nearestX, nearestY, pixel);
dst.setDataElements(j, i, pixel);
}
}
return dst;
}
private int findNearest(double a, int minA, int maxA) {
int nearestA = (int) Math.round(a);
if (nearestA < minA)
nearestA = minA;
else if (nearestA > maxA)
nearestA = maxA;
return nearestA;
}
}