/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geotools.process.raster;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.jai.BorderExtender;
import javax.media.jai.BorderExtenderConstant;
import javax.media.jai.GeometricOpImage;
import javax.media.jai.ImageLayout;
import javax.media.jai.Interpolation;
import javax.media.jai.InterpolationNearest;
import javax.media.jai.PlanarImage;
import javax.media.jai.RasterAccessor;
import javax.media.jai.RasterFormatTag;
import javax.media.jai.iterator.RandomIter;
import javax.media.jai.iterator.RandomIterFactory;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.referencing.CRS;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import com.sun.media.jai.util.ImageUtil;
/**
*
* A RenderedImage that provides values coming from a source GridCoverage2D, with a backing grid
* addressable as the target GridCoverage2D.
* <P>
* The exposed Layout will be the same as the target, and each Point in the target grid can be used
* in the resulting RenderedImage,
*
*
* @author ETj <etj at geo-solutions.it>
*
* @source $URL$
*/
public class GridCoverage2DRIA extends GeometricOpImage {
private final static Logger LOGGER = Logger.getLogger(GridCoverage2DRIA.class.getName());
private final GridCoverage2D src;
private final GridCoverage2D dst;
private final MathTransform g2wd;
private final MathTransform g2ws;
private final MathTransform w2gd;
private final MathTransform w2gs;
private final MathTransform src2dstCRSTransform;
private final MathTransform dst2srcCRSTransform;
/** Color table representing source's IndexColorModel. */
private byte[][] ctable = null; // ETj: just for keeping compiler quiet: let's see if we really
// need it
/**
* Wrap the src coverage in the dst layout. <BR>
* The resulting RenderedImage will contain the data in src, and will be accessible via the grid
* specs of dst,
*
* @param src
* the data coverage to be remapped on dst grid
* @param dst
* the provider of the final grid
* @param nodata
* the nodata value to set for cells not covered by src but included in dst. All
* bands will share the same nodata value.
* @return an instance of Coverage2RenderedImageAdapter
*/
public static GridCoverage2DRIA create(final GridCoverage2D src, final GridCoverage2D dst,
final double nodata) {
// === Create Layout
final ImageLayout imageLayout = new ImageLayout(dst.getRenderedImage());
//
// SampleModel and ColorModel are related to data itself, so we
// copy them from the source
imageLayout.setColorModel(src.getRenderedImage().getColorModel());
imageLayout.setSampleModel(src.getRenderedImage().getSampleModel());
// === BorderExtender
//
// We have yet to check for it usefulness: it might be more convenient
// to check for region overlapping and return a nodata value by hand,
// so to avoid problems with interpolation at source raster borders.
//
BorderExtender extender = new BorderExtenderConstant(new double[] { nodata });
return new GridCoverage2DRIA(src, dst, vectorize(src.getRenderedImage()), imageLayout,
null, false, extender, Interpolation.getInstance(Interpolation.INTERP_NEAREST),
new double[] { nodata });
}
protected GridCoverage2DRIA(final GridCoverage2D src, final GridCoverage2D dst,
final Vector sources, final ImageLayout layout, final Map configuration,
final boolean cobbleSources, final BorderExtender extender, final Interpolation interp,
final double[] nodata) {
super(sources, layout, configuration, cobbleSources, extender, interp, nodata);
this.src = src;
this.dst = dst;
// === Take one for all all the transformation we need to pass from
// model, sample, src, target and viceversa.
g2wd = dst.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT);
try {
w2gd = g2wd.inverse();
} catch (org.opengis.referencing.operation.NoninvertibleTransformException e) {
throw new IllegalArgumentException("Can't compute destination W2G", e);
}
g2ws = src.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT);
try {
w2gs = g2ws.inverse();
} catch (org.opengis.referencing.operation.NoninvertibleTransformException e) {
throw new IllegalArgumentException("Can't compute source W2G", e);
}
try {
CoordinateReferenceSystem sourceCRS = src.getCoordinateReferenceSystem2D();
CoordinateReferenceSystem targetCRS = dst.getCoordinateReferenceSystem2D();
src2dstCRSTransform = CRS.findMathTransform(sourceCRS, targetCRS, true);
dst2srcCRSTransform = src2dstCRSTransform.inverse();
} catch (FactoryException e) {
throw new IllegalArgumentException("Can't create a transform between CRS", e);
} catch (NoninvertibleTransformException e) {
throw new IllegalArgumentException("Can't create a transform between CRS", e);
}
}
@Override
public Point2D mapSourcePoint(Point2D srcPt, int sourceIndex) {
if (srcPt == null) {
throw new IllegalArgumentException("Bad dest pt");// JaiI18N.getString("Generic0"));
} else if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
throw new IndexOutOfBoundsException("Bad src");// JaiI18N.getString("Generic1"));
}
double coords[] = new double[] { srcPt.getX(), srcPt.getY() };
try {
mapSrcPoint(coords);
} catch (TransformException e) {
LOGGER.log(Level.WARNING, "Error transforming coords", e);
return null;
}
Point2D ret = ((Point2D) srcPt.clone());
ret.setLocation(coords[0], coords[1]);
if (inside(ret, src.getRenderedImage()))
return ret;
else {
LOGGER.log(Level.WARNING, "{0} mapped to {1} lies outside {2},{3}+{4}x{5}",
new Object[] { srcPt, ret, src.getRenderedImage().getMinX(),
src.getRenderedImage().getMinX(), src.getRenderedImage().getWidth(),
src.getRenderedImage().getHeight() });
return null;
}
}
private static boolean inside(Point2D point, RenderedImage ri) {
double x = point.getX();
double y = point.getY();
return x >= ri.getMinX() && x <= ri.getMinX() + ri.getWidth() && y >= ri.getMinY()
&& y <= ri.getMinY() + ri.getHeight();
}
/**
* Returns the minimum bounding box of the region of the destination to which a particular
* <code>Rectangle</code> of the specified source will be mapped.
*
* <p>
* The integral source rectangle coordinates should be considered pixel indices. The "energy" of
* each pixel is defined to be concentrated in the continuous plane of pixels at an offset of
* (0.5, 0.5) from the index of the pixel. Forward mappings must take this (0.5, 0.5)
* pixel center into account. Thus given integral source pixel indices as input, the fractional
* destination location, as calculated by functions Xf(xSrc, ySrc), Yf(xSrc, ySrc), is
* given by:
*
* <pre>
*
* xDst = Xf(xSrc+0.5, ySrc+0.5) - 0.5
* yDst = Yf(xSrc+0.5, ySrc+0.5) - 0.5
*
* </pre>
*
* @param sourceRect
* the <code>Rectangle</code> in source coordinates.
* @param sourceIndex
* the index of the source image.
*
* @return a <code>Rectangle</code> indicating the destination bounding box, or
* <code>null</code> if the bounding box is unknown.
*
* @throws IllegalArgumentException
* if <code>sourceIndex</code> is negative or greater than the index of the last
* source.
* @throws IllegalArgumentException
* if <code>sourceRect</code> is <code>null</code>.
*/
@Override
protected Rectangle forwardMapRect(Rectangle pxRect, int i) {
// transformation from out target coverage toward the source one.
// note that source/target names from OpImage are reversed with respect to our
// definitions
// i is not used, only one source raster
float[] pts = rect2PointArr(pxRect);
try {
g2wd.transform(pts, 0, pts, 0, 4);
dst2srcCRSTransform.transform(pts, 0, pts, 0, 4);
w2gs.transform(pts, 0, pts, 0, 4);
} catch (TransformException e) {
LOGGER.log(Level.WARNING, "Error transforming coords", e);
return null;
}
Rectangle srcRect = pointArr2Rect(pts);
return srcRect; // .intersection(src.getGridGeometry().getGridRange2D());
}
@Override
public Point2D mapDestPoint(Point2D destPt, int sourceIndex) {
if (destPt == null) {
throw new IllegalArgumentException("Bad dest pt");// JaiI18N.getString("Generic0"));
} else if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
throw new IndexOutOfBoundsException("Bad src");// JaiI18N.getString("Generic1"));
}
double coords[] = new double[] { destPt.getX(), destPt.getY() };
try {
mapDestPoint(coords);
} catch (TransformException e) {
LOGGER.log(Level.WARNING, "Error transforming coords", e);
return null;
}
Point2D ret = ((Point2D) destPt.clone());
ret.setLocation(coords[0], coords[1]);
if (dst.getGridGeometry().getEnvelope2D().contains(ret))
return ret;
else
return null;
}
private void mapDestPoint(double[] coords) throws TransformException {
final int npoints = coords.length / 2;
g2ws.transform(coords, 0, coords, 0, npoints);
src2dstCRSTransform.transform(coords, 0, coords, 0, npoints);
w2gd.transform(coords, 0, coords, 0, npoints);
}
private void mapSrcPoint(double[] coords) throws TransformException {
final int npoints = coords.length / 2;
// StringBuilder sb = new StringBuilder();
// sb.append("SRC[").append(coords[0]).append(',').append(coords[1]).append(']').append("--g2wd->");
g2wd.transform(coords, 0, coords, 0, npoints);
// sb.append("[").append(coords[0]).append(',').append(coords[1]).append(']').append("--d2sCRS->");
dst2srcCRSTransform.transform(coords, 0, coords, 0, npoints);
// sb.append('[').append(coords[0]).append(',').append(coords[1]).append(']').append("--w2gs->");
w2gs.transform(coords, 0, coords, 0, npoints);
// sb.append('[').append(coords[0]).append(',').append(coords[1]).append(']');
// System.out.println(sb);
}
/**
* Returns the minimum bounding box of the region of the specified source to which a particular
* <code>Rectangle</code> of the destination will be mapped.
*
* <p>
* The integral destination rectangle coordinates should be considered pixel indices. The
* "energy" of each pixel is defined to be concentrated in the continuous plane of pixels at an
* offset of (0.5, 0.5) from the index of the pixel. Backward mappings must take this
* (0.5, 0.5) pixel center into account. Thus given integral destination pixel indices as
* input, the fractional source location, as calculated by functions Xb(xDst, yDst),
* Yb(xDst, yDst), is given by:
*
* <pre>
*
* xSrc = Xb(xDst+0.5, yDst+0.5) - 0.5
* ySrc = Yb(xDst+0.5, yDst+0.5) - 0.5
*
* </pre>
*
* @param destRect
* the <code>Rectangle</code> in destination coordinates.
* @param sourceIndex
* the index of the source image.
*
* @return a <code>Rectangle</code> indicating the source bounding box, or <code>null</code> if
* the bounding box is unknown.
*
* @throws IllegalArgumentException
* if <code>sourceIndex</code> is negative or greater than the index of the last
* source.
* @throws IllegalArgumentException
* if <code>destRect</code> is <code>null</code>.
*/
@Override
protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) {
float[] pts = rect2PointArr(destRect);
try {
g2ws.transform(pts, 0, pts, 0, 4);
src2dstCRSTransform.transform(pts, 0, pts, 0, 4);
w2gd.transform(pts, 0, pts, 0, 4);
} catch (TransformException e) {
LOGGER.log(Level.WARNING, "Error transforming coords", e);
return null;
}
Rectangle pxRect = pointArr2Rect(pts);
return pxRect;
}
private static float[] rect2PointArr(Rectangle rect) {
float dx0 = (float) rect.x;
float dy0 = (float) rect.y;
float dw = (float) (rect.width);
float dh = (float) (rect.height);
return new float[] { dx0, dy0, (dx0 + dw), dy0, (dx0 + dw), (dy0 + dh), dx0, (dy0 + dh) };
}
private Rectangle pointArr2Rect(float[] points) {
float f_sx0 = Float.MAX_VALUE;
float f_sy0 = Float.MAX_VALUE;
float f_sx1 = -Float.MAX_VALUE;
float f_sy1 = -Float.MAX_VALUE;
for (int i = 0; i < 4; i++) {
float px = points[i * 2];
float py = points[i * 2 + 1];
f_sx0 = Math.min(f_sx0, px);
f_sy0 = Math.min(f_sy0, py);
f_sx1 = Math.max(f_sx1, px);
f_sy1 = Math.max(f_sy1, py);
}
int s_x0 = 0, s_y0 = 0, s_x1 = 0, s_y1 = 0;
// Find the bounding box of the source rectangle
if (interp instanceof InterpolationNearest) {
s_x0 = (int) Math.floor(f_sx0);
s_y0 = (int) Math.floor(f_sy0);
// Fix for bug 4485920 was to add " + 0.05" to the following
// two lines. It should be noted that the fix was made based
// on empirical evidence and tested thoroughly, but it is not
// known whether this is the root cause.
s_x1 = (int) Math.ceil(f_sx1 + 0.5);
s_y1 = (int) Math.ceil(f_sy1 + 0.5);
} else {
s_x0 = (int) Math.floor(f_sx0 - 0.5);
s_y0 = (int) Math.floor(f_sy0 - 0.5);
s_x1 = (int) Math.ceil(f_sx1);
s_y1 = (int) Math.ceil(f_sy1);
}
//
// Return the new rectangle
//
return new Rectangle(s_x0, s_y0, s_x1 - s_x0, s_y1 - s_y0);
}
/**
* Warps a rectangle.
*
* Copied and adapted from WarpGeneralOpImage
*/
@Override
protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) {
// Retrieve format tags.
RasterFormatTag[] formatTags = getFormatTags();
RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel());
switch (d.getDataType()) {
case DataBuffer.TYPE_BYTE:
computeRectByte(sources[0], d);
break;
case DataBuffer.TYPE_USHORT:
computeRectUShort(sources[0], d);
break;
case DataBuffer.TYPE_SHORT:
computeRectShort(sources[0], d);
break;
case DataBuffer.TYPE_INT:
computeRectInt(sources[0], d);
break;
case DataBuffer.TYPE_FLOAT:
computeRectFloat(sources[0], d);
break;
case DataBuffer.TYPE_DOUBLE:
computeRectDouble(sources[0], d);
break;
}
if (d.isDataCopy()) {
d.clampDataArrays();
d.copyDataToRaster();
}
}
private void computeRectByte(PlanarImage src, RasterAccessor dst) {
int lpad, rpad, tpad, bpad;
if (interp != null) {
lpad = interp.getLeftPadding();
rpad = interp.getRightPadding();
tpad = interp.getTopPadding();
bpad = interp.getBottomPadding();
} else {
lpad = rpad = tpad = bpad = 0;
}
int minX, maxX, minY, maxY;
RandomIter iter;
if (extender != null) {
minX = src.getMinX();
maxX = src.getMaxX();
minY = src.getMinY();
maxY = src.getMaxY();
Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src
.getWidth()
+ lpad + rpad, src.getHeight() + tpad + bpad);
iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds);
} else {
minX = src.getMinX() + lpad;
maxX = src.getMaxX() - rpad;
minY = src.getMinY() + tpad;
maxY = src.getMaxY() - bpad;
iter = RandomIterFactory.create(src, src.getBounds());
}
int kwidth = interp.getWidth();
int kheight = interp.getHeight();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int lineStride = dst.getScanlineStride();
int pixelStride = dst.getPixelStride();
int[] bandOffsets = dst.getBandOffsets();
byte[][] data = dst.getByteDataArrays();
int precH = 1 << interp.getSubsampleBitsH();
int precV = 1 << interp.getSubsampleBitsV();
float[] warpData = new float[2 * dstWidth];
int[][] samples = new int[kheight][kwidth];
int lineOffset = 0;
byte[] backgroundByte = new byte[dstBands];
for (int i = 0; i < dstBands; i++) {
backgroundByte[i] = (byte) backgroundValues[i];
}
if (ctable == null) { // source does not have IndexColorModel
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
int xfrac = (int) ((sx - xint) * precH);
int yfrac = (int) ((sy - yint) * precV);
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundByte[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = iter.getSample(xint + i, yint + j, b) & 0xFF;
}
}
data[b][pixelOffset + bandOffsets[b]] = ImageUtil.clampByte(interp
.interpolate(samples, xfrac, yfrac));
}
}
pixelOffset += pixelStride;
}
}
} else { // source has IndexColorModel
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
int xfrac = (int) ((sx - xint) * precH);
int yfrac = (int) ((sy - yint) * precV);
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundByte[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
byte[] t = ctable[b];
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = t[iter.getSample(xint + i, yint + j, 0) & 0xFF] & 0xFF;
}
}
data[b][pixelOffset + bandOffsets[b]] = ImageUtil.clampByte(interp
.interpolate(samples, xfrac, yfrac));
}
}
pixelOffset += pixelStride;
}
}
}
}
private void computeRectUShort(PlanarImage src, RasterAccessor dst) {
int lpad, rpad, tpad, bpad;
if (interp != null) {
lpad = interp.getLeftPadding();
rpad = interp.getRightPadding();
tpad = interp.getTopPadding();
bpad = interp.getBottomPadding();
} else {
lpad = rpad = tpad = bpad = 0;
}
int minX, maxX, minY, maxY;
RandomIter iter;
if (extender != null) {
minX = src.getMinX();
maxX = src.getMaxX();
minY = src.getMinY();
maxY = src.getMaxY();
Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src
.getWidth()
+ lpad + rpad, src.getHeight() + tpad + bpad);
iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds);
} else {
minX = src.getMinX() + lpad;
maxX = src.getMaxX() - rpad;
minY = src.getMinY() + tpad;
maxY = src.getMaxY() - bpad;
iter = RandomIterFactory.create(src, src.getBounds());
}
int kwidth = interp.getWidth();
int kheight = interp.getHeight();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int lineStride = dst.getScanlineStride();
int pixelStride = dst.getPixelStride();
int[] bandOffsets = dst.getBandOffsets();
short[][] data = dst.getShortDataArrays();
int precH = 1 << interp.getSubsampleBitsH();
int precV = 1 << interp.getSubsampleBitsV();
float[] warpData = new float[2 * dstWidth];
int[][] samples = new int[kheight][kwidth];
int lineOffset = 0;
short[] backgroundUShort = new short[dstBands];
for (int i = 0; i < dstBands; i++) {
backgroundUShort[i] = (short) backgroundValues[i];
}
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
int xfrac = (int) ((sx - xint) * precH);
int yfrac = (int) ((sy - yint) * precV);
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundUShort[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = iter.getSample(xint + i, yint + j, b) & 0xFFFF;
}
}
data[b][pixelOffset + bandOffsets[b]] = ImageUtil.clampUShort(interp
.interpolate(samples, xfrac, yfrac));
}
}
pixelOffset += pixelStride;
}
}
}
private void computeRectShort(PlanarImage src, RasterAccessor dst) {
int lpad, rpad, tpad, bpad;
if (interp != null) {
lpad = interp.getLeftPadding();
rpad = interp.getRightPadding();
tpad = interp.getTopPadding();
bpad = interp.getBottomPadding();
} else {
lpad = rpad = tpad = bpad = 0;
}
int minX, maxX, minY, maxY;
RandomIter iter;
if (extender != null) {
minX = src.getMinX();
maxX = src.getMaxX();
minY = src.getMinY();
maxY = src.getMaxY();
Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src
.getWidth()
+ lpad + rpad, src.getHeight() + tpad + bpad);
iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds);
} else {
minX = src.getMinX() + lpad;
maxX = src.getMaxX() - rpad;
minY = src.getMinY() + tpad;
maxY = src.getMaxY() - bpad;
iter = RandomIterFactory.create(src, src.getBounds());
}
int kwidth = interp.getWidth();
int kheight = interp.getHeight();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int lineStride = dst.getScanlineStride();
int pixelStride = dst.getPixelStride();
int[] bandOffsets = dst.getBandOffsets();
short[][] data = dst.getShortDataArrays();
int precH = 1 << interp.getSubsampleBitsH();
int precV = 1 << interp.getSubsampleBitsV();
float[] warpData = new float[2 * dstWidth];
int[][] samples = new int[kheight][kwidth];
int lineOffset = 0;
short[] backgroundShort = new short[dstBands];
for (int i = 0; i < dstBands; i++) {
backgroundShort[i] = (short) backgroundValues[i];
}
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
int xfrac = (int) ((sx - xint) * precH);
int yfrac = (int) ((sy - yint) * precV);
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundShort[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = iter.getSample(xint + i, yint + j, b);
}
}
data[b][pixelOffset + bandOffsets[b]] = ImageUtil.clampShort(interp
.interpolate(samples, xfrac, yfrac));
}
}
pixelOffset += pixelStride;
}
}
}
private void computeRectInt(PlanarImage src, RasterAccessor dst) {
int lpad, rpad, tpad, bpad;
if (interp != null) {
lpad = interp.getLeftPadding();
rpad = interp.getRightPadding();
tpad = interp.getTopPadding();
bpad = interp.getBottomPadding();
} else {
lpad = rpad = tpad = bpad = 0;
}
int minX, maxX, minY, maxY;
RandomIter iter;
if (extender != null) {
minX = src.getMinX();
maxX = src.getMaxX();
minY = src.getMinY();
maxY = src.getMaxY();
Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src
.getWidth()
+ lpad + rpad, src.getHeight() + tpad + bpad);
iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds);
} else {
minX = src.getMinX() + lpad;
maxX = src.getMaxX() - rpad;
minY = src.getMinY() + tpad;
maxY = src.getMaxY() - bpad;
iter = RandomIterFactory.create(src, src.getBounds());
}
int kwidth = interp.getWidth();
int kheight = interp.getHeight();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int lineStride = dst.getScanlineStride();
int pixelStride = dst.getPixelStride();
int[] bandOffsets = dst.getBandOffsets();
int[][] data = dst.getIntDataArrays();
int precH = 1 << interp.getSubsampleBitsH();
int precV = 1 << interp.getSubsampleBitsV();
float[] warpData = new float[2 * dstWidth];
int[][] samples = new int[kheight][kwidth];
int lineOffset = 0;
int[] backgroundInt = new int[dstBands];
for (int i = 0; i < dstBands; i++) {
backgroundInt[i] = (int) backgroundValues[i];
}
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
int xfrac = (int) ((sx - xint) * precH);
int yfrac = (int) ((sy - yint) * precV);
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundInt[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = iter.getSample(xint + i, yint + j, b);
}
}
data[b][pixelOffset + bandOffsets[b]] = interp.interpolate(samples, xfrac,
yfrac);
}
}
pixelOffset += pixelStride;
}
}
}
private void computeRectFloat(PlanarImage src, RasterAccessor dst) {
int lpad, rpad, tpad, bpad;
if (interp != null) {
lpad = interp.getLeftPadding();
rpad = interp.getRightPadding();
tpad = interp.getTopPadding();
bpad = interp.getBottomPadding();
} else {
lpad = rpad = tpad = bpad = 0;
}
int minX, maxX, minY, maxY;
RandomIter iter;
if (extender != null) {
minX = src.getMinX();
maxX = src.getMaxX();
minY = src.getMinY();
maxY = src.getMaxY();
Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src
.getWidth()
+ lpad + rpad, src.getHeight() + tpad + bpad);
iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds);
} else {
minX = src.getMinX() + lpad;
maxX = src.getMaxX() - rpad;
minY = src.getMinY() + tpad;
maxY = src.getMaxY() - bpad;
iter = RandomIterFactory.create(src, src.getBounds());
}
int kwidth = interp.getWidth();
int kheight = interp.getHeight();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int lineStride = dst.getScanlineStride();
int pixelStride = dst.getPixelStride();
int[] bandOffsets = dst.getBandOffsets();
float[][] data = dst.getFloatDataArrays();
float[] warpData = new float[2 * dstWidth];
float[][] samples = new float[kheight][kwidth];
int lineOffset = 0;
float[] backgroundFloat = new float[dstBands];
for (int i = 0; i < dstBands; i++) {
backgroundFloat[i] = (float) backgroundValues[i];
}
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
float xfrac = sx - xint;
float yfrac = sy - yint;
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundFloat[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = iter.getSampleFloat(xint + i, yint + j, b);
}
}
data[b][pixelOffset + bandOffsets[b]] = interp.interpolate(samples, xfrac,
yfrac);
}
}
pixelOffset += pixelStride;
}
}
}
private void computeRectDouble(PlanarImage src, RasterAccessor dst) {
int lpad, rpad, tpad, bpad;
if (interp != null) {
lpad = interp.getLeftPadding();
rpad = interp.getRightPadding();
tpad = interp.getTopPadding();
bpad = interp.getBottomPadding();
} else {
lpad = rpad = tpad = bpad = 0;
}
int minX, maxX, minY, maxY;
RandomIter iter;
if (extender != null) {
minX = src.getMinX();
maxX = src.getMaxX();
minY = src.getMinY();
maxY = src.getMaxY();
Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src
.getWidth()
+ lpad + rpad, src.getHeight() + tpad + bpad);
iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds);
} else {
minX = src.getMinX() + lpad;
maxX = src.getMaxX() - rpad;
minY = src.getMinY() + tpad;
maxY = src.getMaxY() - bpad;
iter = RandomIterFactory.create(src, src.getBounds());
}
int kwidth = interp.getWidth();
int kheight = interp.getHeight();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int lineStride = dst.getScanlineStride();
int pixelStride = dst.getPixelStride();
int[] bandOffsets = dst.getBandOffsets();
double[][] data = dst.getDoubleDataArrays();
float[] warpData = new float[2 * dstWidth];
double[][] samples = new double[kheight][kwidth];
int lineOffset = 0;
for (int h = 0; h < dstHeight; h++) {
int pixelOffset = lineOffset;
lineOffset += lineStride;
// warp.warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
warpRect(dst.getX(), dst.getY() + h, dstWidth, 1, warpData);
int count = 0;
for (int w = 0; w < dstWidth; w++) {
float sx = warpData[count++];
float sy = warpData[count++];
int xint = floor(sx);
int yint = floor(sy);
float xfrac = sx - xint;
float yfrac = sy - yint;
if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) {
/* Fill with a background color. */
if (setBackground) {
for (int b = 0; b < dstBands; b++) {
data[b][pixelOffset + bandOffsets[b]] = backgroundValues[b];
}
}
} else {
xint -= lpad;
yint -= tpad;
for (int b = 0; b < dstBands; b++) {
for (int j = 0; j < kheight; j++) {
for (int i = 0; i < kwidth; i++) {
samples[j][i] = iter.getSampleDouble(xint + i, yint + j, b);
}
}
data[b][pixelOffset + bandOffsets[b]] = interp.interpolate(samples, xfrac,
yfrac);
}
}
pixelOffset += pixelStride;
}
}
}
/** Returns the "floor" value of a float. */
private static final int floor(float f) {
return f >= 0 ? (int) f : (int) f - 1;
}
public float[] warpRect(int x, int y, int width, int height, float[] destRect) {
if (destRect != null && destRect.length < (width * height * 2)) {
throw new IllegalArgumentException("warpRect: bad destRect");// JaiI18N.getString("Warp0"));
}
return warpSparseRect(x, y, width, height, 1, 1, destRect);
}
/**
* @param x0
* The minimum X coordinate of the destination region.
* @param y0
* The minimum Y coordinate of the destination region.
* @param width
* The width of the destination region.
* @param height
* The height of the destination region.
* @param periodX
* The horizontal sampling period.
* @param periodY
* The vertical sampling period.
*
* @param destRect
* A <code>float</code> array containing at least
* <code>2*((width+periodX-1)/periodX)*
* ((height+periodY-1)/periodY)</code> elements, or <code>null</code>. If
* <code>null</code>, a new array will be constructed.
*
* @return A reference to the <code>destRect</code> parameter if it is non-<code>null</code>, or
* a new <code>float</code> array otherwise.
*/
public float[] warpSparseRect(int x0, int y0, int width, int height, int periodX, int periodY,
float[] destRect) {
// XXX: This method should do its calculations in doubles
if (destRect == null) {
destRect = new float[((width + periodX - 1) / periodX)
* ((height + periodY - 1) / periodY) * 2];
}
width += x0;
height += y0;
int index = 0; // destRect index
double xy[] = new double[2];
for (int y = y0; y < height; y += periodY) {
for (int x = x0; x < width; x += periodX) {
xy[0] = x;
xy[1] = y;
try {
mapSrcPoint(xy);
destRect[index++] = (float) xy[0];
destRect[index++] = (float) xy[1];
} catch (TransformException e) {
LOGGER.log(Level.WARNING, "Error transforming {0}", xy);
destRect[index++] = Float.NaN; // ???
destRect[index++] = Float.NaN; // ???
}
}
}
return destRect;
}
}