/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2012, Geomatys * * 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; * version 2.1 of the License. * * 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 org.geotoolkit.image.interpolation; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.WritableRenderedImage; import org.apache.sis.geometry.Envelope2D; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.geometry.Envelopes; import org.geotoolkit.image.io.large.WritableLargeRenderedImage; import org.geotoolkit.image.iterator.PixelIterator; import org.geotoolkit.image.iterator.PixelIteratorFactory; import org.geotoolkit.math.XMath; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransform2D; import org.opengis.referencing.operation.NoninvertibleTransformException; import org.opengis.referencing.operation.TransformException; /** * Fill target image from source image pixels interpolation at coordinate define * by transformation of target pixel coordinate by {@code MathTransform}. * * @author RĂ©mi Marechal (Geomatys). * @author Martin Desruisseaux (Geomatys). */ public class Resample { private static final double[] CLAMP_BYTE = new double[]{0, 255}; private static final double[] CLAMP_SHORT = new double[]{Short.MIN_VALUE, Short.MAX_VALUE}; private static final double[] CLAMP_USHORT = new double[]{0, 0xFFFF}; private static final double[] CLAMP_INT = new double[]{Integer.MIN_VALUE, Integer.MAX_VALUE}; /** * Transform multi-dimensional point (in our case pixel coordinate) from target image * {@code CoordinateReferenceSystem} to source image {@code CoordinateReferenceSystem}. */ private MathTransform destToSourceMathTransform; /** * Image in which image source pixel interpolation result is push. */ private final WritableRenderedImage imageDest; /** * Sort of interpolation use to interpolate image source pixels. */ private final Interpolation interpol; /** * Image number bands.<br/> * Note : source and target image have same bands number. */ private final int numBands; /** * Table which contain x, y coordinates after {@link MathTransform} transformation. */ private final double[] srcCoords; /** * Table which contain x, y coordinates after {@link MathTransform} transformation. */ private final double[] destCoords; /** * Iterator use to fill destination image from interpolation of source image pixel value. */ final PixelIterator destIterator; /** * Minimum and maximum values authorized for pixels. All interpolated value outside this interval will be clamped. */ protected final double[] clamp; /** * Contains value use when destination pixel coordinates transformation * are out of source image boundary. */ private double[] fillValue; /** * On the border of the destination image, projection of destination border * pixel coordinates should be out of source image boundary. * This enum explain the expected comportement define by user. * By default is {@link ResampleBorderComportement#EXTRAPOLATION}, * which allow interpolation outside of source pixel validity area. */ ResampleBorderComportement rbc; /** * Grid use to resample image if exist. * @see GridFactory#create(org.opengis.referencing.operation.MathTransform2D, java.awt.Rectangle) */ ResampleGrid theGrid; /** * <p>Fill destination image from interpolation of source pixels.<br/> * Source pixel coordinate is obtained from invert transformation of destination pixel coordinates.<br/> * The default border comportement is {@link ResampleBorderComportement#EXTRAPOLATION}.<br/><br/> * * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider with {@link PixelInCell#CELL_CENTER} configuration.</strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param interpol Interpolation use to interpolate source image pixels. * @param fillValue contains value use when pixel transformation is out of source image boundary. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. */ @Deprecated public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, Interpolation interpol, double[] fillValue) throws NoninvertibleTransformException, TransformException { this(mathTransform, imageDest, null, interpol, fillValue); } /** * <p>Fill destination image area from interpolation of source pixels.<br/> * Source pixel coordinate is obtained from invert transformation of destination pixel coordinates.<br/> * The default border comportement is {@link ResampleBorderComportement#EXTRAPOLATION}.<br/><br/> * * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider with {@link PixelInCell#CELL_CENTER} configuration.</strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param resampleArea destination image area within pixels are resample. * @param interpol Interpolation use to interpolate source image pixels. * @param fillValue contains value use when pixel transformation is out of source image boundary. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. */ @Deprecated public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, Rectangle resampleArea, Interpolation interpol, double[] fillValue) throws NoninvertibleTransformException, TransformException { this(mathTransform, imageDest, resampleArea, interpol, fillValue, ResampleBorderComportement.EXTRAPOLATION); } /** * <p>Fill destination image area from interpolation of source pixels from imageSrc.<br/> * Source pixel coordinates is obtained from invert transformation of destination pixel coordinates.<br/><br/> * - In case where interpolation equals {@linkplain InterpolationCase#LANCZOS lanczos interpolation} the default * choosen lanczos window is initialized by value 2.<br/> * - The default border comportement is define as {@link ResampleBorderComportement#FILL_VALUE}<br/> * - In case where pixel transformation is out of source image boundary the default choosen fill value is {@link Double#NaN}.<br/><br/> * * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider as {@link PixelInCell#CELL_CENTER} configuration.</strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param imageSrc source image which contain pixel values which will be interpolate. * @param interpolation case of interpolation. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. * @see ResampleBorderComportement * @see LanczosInterpolation#LanczosInterpolation(org.geotoolkit.image.iterator.PixelIterator, int) */ public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, RenderedImage imageSrc, InterpolationCase interpolation) throws TransformException { this(mathTransform, imageDest, null, imageSrc, interpolation, 2, ResampleBorderComportement.FILL_VALUE, new double[imageDest.getSampleModel().getNumBands()]); } /** * <p>Fill destination image area from interpolation of source pixels from imageSrc.<br/> * Source pixel coordinates is obtained from invert transformation of destination pixel coordinates.<br/><br/> * - In case where interpolation equals {@linkplain InterpolationCase#LANCZOS lanczos interpolation} the default * choosen lanczos window is initialized by value 2.<br/><br/> * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider as {@link PixelInCell#CELL_CENTER} configuration.</strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param imageSrc source image which contain pixel values which will be interpolate. * @param interpolation case of interpolation. * @param rbc comportement of the destination image border. * @param fillValue contains value use when pixel transformation is out of source image boundary. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. * @see ResampleBorderComportement */ public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, RenderedImage imageSrc, InterpolationCase interpolation, ResampleBorderComportement rbc, double[] fillValue) throws TransformException { this(mathTransform, imageDest, null, imageSrc, interpolation, 2, rbc, fillValue); } /** * <p>Fill destination image area from interpolation of source pixels from imageSrc.<br/> * Source pixel coordinates is obtained from invert transformation of destination pixel coordinates.<br/><br/> * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider as {@link PixelInCell#CELL_CENTER} configuration.</strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param imageSrc source image which contain pixel values which will be interpolate. * @param interpolation case of interpolation. * @param lanczosWindow only use about Lanczos interpolation. * @param rbc comportement of the destination image border. * @param fillValue contains value use when pixel transformation is out of source image boundary, or {@code null}. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. * @see ResampleBorderComportement */ public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, RenderedImage imageSrc, InterpolationCase interpolation, int lanczosWindow, ResampleBorderComportement rbc, double[] fillValue) throws TransformException { this(mathTransform, imageDest, null, imageSrc, interpolation, lanczosWindow, rbc, fillValue); } /** * <p>Fill destination image area from interpolation of source pixels from imageSrc.<br/> * Source pixel coordinates is obtained from invert transformation of destination pixel coordinates.<br/><br/> * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider as {@link PixelInCell#CELL_CENTER} configuration.</strong> * <strong> * fillValue parameter should be {@code null}, in case where fillValue is null, when destination * coordinates pixel transformation is out of source boundary the destination pixel has no setted sample values.<br/> * In other words resampling has no impact on destination pixel samples values when transformation is outside source image boundary.<br/></strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param resampleArea destination image area within pixels are resample. * @param imageSrc source image which contain pixel values which will be interpolate. * @param interpolation case of interpolation. * @param lanczosWindow only use about Lanczos interpolation. * @param rbc comportement of the destination image border. * @param fillValue contains value use when pixel transformation is out of source image boundary. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. * @see ResampleBorderComportement */ public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, Rectangle resampleArea, RenderedImage imageSrc, InterpolationCase interpolation, int lanczosWindow, ResampleBorderComportement rbc, double[] fillValue) throws TransformException { ArgumentChecks.ensureNonNull("mathTransform", mathTransform); ArgumentChecks.ensureNonNull("imageSrc", imageSrc); ArgumentChecks.ensureNonNull("ResampleBorderComportement", rbc); if (imageDest == null) { final Envelope2D srcGrid = new Envelope2D(); srcGrid.setFrame(imageSrc.getMinX() + 0.5, imageSrc.getMinY() + 0.5, imageSrc.getWidth() - 1, imageSrc.getHeight() - 1); final GeneralEnvelope dest = Envelopes.transform(mathTransform.inverse(), srcGrid); final int minx = (int) dest.getLower(0); final int miny = (int) dest.getLower(1); final int w = (int) dest.getSpan(0); final int h = (int) dest.getSpan(1); this.imageDest = new WritableLargeRenderedImage(minx, miny, w, h, null, 0, 0, imageSrc.getColorModel(), imageSrc.getSampleModel()); } else { /* * If a user give a destination image he should hope that his image boundary stay unchanged. */ if (rbc == ResampleBorderComportement.CROP) throw new IllegalArgumentException("It is impossible to define appropriate border comportement with a given image and crop request."); this.imageDest = imageDest; } this.numBands = imageSrc.getSampleModel().getNumBands(); this.fillValue = fillValue; if (fillValue != null) if (fillValue.length != numBands) throw new IllegalArgumentException("fillValue table length and numbands are different : "+fillValue.length+" numbands = "+this.numBands); assert(numBands == imageDest.getWritableTile(imageDest.getMinTileX(), imageDest.getMinTileY()).getNumBands()) : "destination image numbands different from source image numbands"; this.destIterator = PixelIteratorFactory.createDefaultWriteableIterator(this.imageDest, this.imageDest, resampleArea); this.destToSourceMathTransform = mathTransform; srcCoords = new double[2]; destCoords = new double[2]; //-- interpolation creation --// PixelIterator pix = PixelIteratorFactory.createDefaultIterator(imageSrc); interpol = Interpolation.create(pix, interpolation, lanczosWindow, rbc, fillValue); this.rbc = rbc; this.clamp = getClamp(imageDest.getSampleModel().getDataType()); } /** * <p>Fill destination image area from interpolation of source pixels.<br/> * Source pixel coordinate is obtained from invert transformation of destination pixel coordinates.<br/><br/> * <strong> * Moreover : the specified MathTransform should be from CENTER of target image point to CENTER of source image point.<br/> * The used MathTransform is consider with {@link PixelInCell#CELL_CENTER} configuration.</strong></p> * * @param mathTransform Transformation use to transform target point to source point. * @param imageDest image will be fill by image source pixel interpolation. * @param resampleArea destination image area within pixels are resample. * @param interpol Interpolation use to interpolate source image pixels. * @param fillValue contains value use when pixel transformation is out of source image boundary. * @param rbc comportement of the destination image border. * @throws NoninvertibleTransformException if it is impossible to invert {@code MathTransform} parameter. * @see ResampleBorderComportement */ @Deprecated public Resample(MathTransform mathTransform, WritableRenderedImage imageDest, Rectangle resampleArea, Interpolation interpol, double[] fillValue, ResampleBorderComportement rbc) throws NoninvertibleTransformException, TransformException { ArgumentChecks.ensureNonNull("mathTransform", mathTransform); ArgumentChecks.ensureNonNull("interpolation", interpol); final Rectangle bound = interpol.getBoundary(); if (imageDest == null) { final Envelope2D srcGrid = new Envelope2D(); srcGrid.setFrame(bound.x + 0.5, bound.y + 0.5, bound.width - 1, bound.height - 1); final GeneralEnvelope dest = Envelopes.transform(mathTransform.inverse(), srcGrid); final int minx = (int) dest.getLower(0); final int miny = (int) dest.getLower(1); final int w = (int) dest.getSpan(0); final int h = (int) dest.getSpan(1); this.imageDest = new WritableLargeRenderedImage(minx, miny, w, h, null, 0, 0, interpol.pixelIterator.getRenderedImage().getColorModel(), interpol.pixelIterator.getRenderedImage().getSampleModel()); } else { /* * If a user give a destination image he should hope that his image boundary stay unchanged. */ if (rbc == ResampleBorderComportement.CROP) throw new IllegalArgumentException("It is impossible to define appropriate border comportement with a given image and crop request."); this.imageDest = imageDest; } this.numBands = interpol.getNumBands(); this.fillValue = fillValue; if (fillValue != null) if (fillValue.length != numBands) throw new IllegalArgumentException("fillValue table length and numbands are different : "+fillValue.length+" numbands = "+this.numBands); assert(numBands == imageDest.getWritableTile(imageDest.getMinTileX(), imageDest.getMinTileY()).getNumBands()) : "destination image numbands different from source image numbands"; this.destIterator = PixelIteratorFactory.createDefaultWriteableIterator(this.imageDest, this.imageDest, resampleArea); this.destToSourceMathTransform = mathTransform; this.interpol = interpol; srcCoords = new double[2]; destCoords = new double[2]; this.rbc = rbc; this.clamp = getClamp(imageDest.getSampleModel().getDataType()); } private static double[] getClamp(int dataType) { switch (dataType) { /* Because DataBuffer.TYPE_BYTE is define as UByte. */ case DataBuffer.TYPE_BYTE : return CLAMP_BYTE; case DataBuffer.TYPE_SHORT : return CLAMP_SHORT; case DataBuffer.TYPE_USHORT : return CLAMP_USHORT; case DataBuffer.TYPE_INT : return CLAMP_INT; default : return null; } } /** * Fill destination image from pre-computed grid. * * @throws TransformException */ private void fillImageByGrid() throws TransformException { final double[] theGridArray = theGrid.getGrid(); final int stepX = theGrid.getStepX(); final int stepY = theGrid.getStepY(); final int minGridX = theGrid.getMinGridX(); final int minGridY = theGrid.getMinGridY(); final int minGridXIndex = theGrid.getMinGridXIndex(); final int minGridYIndex = theGrid.getMinGridYIndex(); //-- destination area traveled by destination iterator. Rectangle rectBound = destIterator.getBoundary(true); //-- destination raster dimensions. final int tileWidth = imageDest.getTileWidth(); final int tileHeight = imageDest.getTileHeight(); //-- destination raster index which will be traveled. final int destMinRastXIndex = imageDest.getMinTileX() + (rectBound.x - imageDest.getMinX()) / tileWidth; final int destMinRastYIndex = imageDest.getMinTileY() + (rectBound.y - imageDest.getMinY()) / tileHeight; final int destMaxRastX = imageDest.getMinTileX() + (rectBound.x + rectBound.width + tileWidth - 1) / tileWidth; final int destMaxRastY = imageDest.getMinTileY() + (rectBound.y + rectBound.height + tileHeight - 1) / tileHeight; //-- grid dimensions. final int gridWidth = theGrid.getGridWidth(); final int gridHeight = theGrid.getGridHeight(); final int gridLineStride = gridWidth << 1; //-- current raster coordinate in Y direction. int rY = destMinRastYIndex * tileHeight; int rMaxY = rY + tileHeight; for (int idRy = destMinRastYIndex; idRy < destMaxRastY; idRy++) { //-- define intersection between current raster and area traveled by destination iterator in Y direction. final int interMinRastY = StrictMath.max(rY, rectBound.y); final int interMaxRastY = StrictMath.min(rMaxY, rectBound.y + rectBound.height); //-- define minimum and maximum needed grid index in Y direction. final int gCMinY = (int) ((interMinRastY - minGridY) / stepY) + minGridYIndex; /* * Max grid index in Y direction equal Math.ceil(intersectionY / stepY) + 1. * With + 1 because gridHeight = sub-division on Y axis + 1; */ final int gCMaxY = (int) ((interMaxRastY - minGridY + stepY - 1) / stepY) + minGridYIndex + 1; assert gCMaxY <= gridHeight : "Computed max grid index should be lesser or equal than grid height. Expected max grid index : "+gridHeight+", found : "+gCMaxY; //-- current raster coordinate in X direction. int rX = destMinRastXIndex * tileWidth; int rMaxX = rX + tileWidth; for (int idRx = destMinRastXIndex; idRx < destMaxRastX; idRx++) { //-- define intersection between current raster and area traveled by destination iterator in X direction. final int interMinRastX = StrictMath.max(rX, rectBound.x); final int interMaxRastX = StrictMath.min(rMaxX, rectBound.x + rectBound.width); //-- define minimum and maximum needed grid index in X direction. final int gCMinX = (int) (interMinRastX - minGridY / stepX) + minGridXIndex; /* * Max grid index in X direction equal Math.ceil(intersectionX / stepX) + 1. * With + 1 because gridWidth = sub-division on X axis + 1; */ final int gCMaxX = (int) ((interMaxRastX - minGridX + stepX - 1) / stepX) + minGridXIndex + 1; assert gCMaxX <= gridWidth : "Computed max grid index in X direction should be lesser or equal than grid width. Expected max grid index : "+gridWidth+", found : "+gCMaxX; //------------------------- grid working -------------------------// //-- index des points a interpoler dans la grille int id10, id11; double v00X, v10X, v01X, v11X; double v00Y, v10Y, v01Y, v11Y; //-- travel destination pixel coordinates. //-- intersection between area iterated on raster and current grid cell projected into raster space. final int interMinY = StrictMath.max(interMinRastY, gCMinY * stepY); final int interMinX = StrictMath.max(interMinRastX, gCMinX * stepX); final int interMaxY = StrictMath.min(interMaxRastY, gCMaxY * stepY); final int interMaxX = StrictMath.min(interMaxRastX, gCMaxX * stepX); //-- define grid array index. int rowId0 = gCMinY * gridLineStride + (gCMinX << 1); int maxRowId0 = (gCMaxY - 2) * gridLineStride + (gCMinX << 1); int rowId1 = rowId0 + gridLineStride; int py = interMinY; //-- Define pixel coordinate in Y direction to pass at next grid cell. int nextGIdY = (gCMinY + 1) * stepY;// + (stepY >>> 1); //-- current grid index in X direction. int gx = gCMinX; //-- current grid index in Y direction. int gy = gCMinY; while (py < interMaxY) { if (py == nextGIdY) { rowId0 += gridLineStride; rowId0 = StrictMath.min(rowId0, maxRowId0); rowId1 = rowId0 + gridLineStride; gy = StrictMath.min(++gy, gridHeight - 2); nextGIdY += stepY; } id10 = rowId0 + 2; id11 = rowId1 + 2; gx = gCMinX; int px = interMinX; //-- Define pixel coordinate in X direction to pass at next grid cell. int nextGIdX = (gCMinX + 1) * stepX;// + (stepX >>> 1); /* * To define source image coordinate we use bilinear interpolation * from precedently computed values from grid. * Bilinear interpolation is computing like follow in 3 steps. * First we compute "A" value from v00 and v01 (grid values), * t0y which is v00 position in grid and ty the destination * pixel coordinate projected into grid space by followed formula : A = (ty - t0y) * (v10 - v00) + v00. * * Secondly, with the same formula we compute B. * * And finally, we compute coordinates by same precedently formula * with precedently computing results A and B but on X axis. * * P = (tX - t0X) * (B - A) + A * * t0X tX * | | | * ____ | ____ * t0y ------|v00|--------- C ---------|v10|---- * | | | * | | | * | | | * | | | * ty ---- A ---------- P ---------- B----- * | | | * | | | * | | | * | | | * ____ | ____ * ------|v01|--------- D ---------|v11|---- * | | | * * During iteration in X direction into the grid we note that v00 become v10 and v01 -> v11, and thereby A = B. * To avoid some of unneccessary computing we affect B to A and * we just re-compute B value and only interpolation in X direction (C and D values). */ //-- vX v00X = theGridArray[rowId0]; v10X = theGridArray[id10]; v01X = theGridArray[rowId1]; v11X = theGridArray[id11]; //-- vY v00Y = theGridArray[rowId0 | 1]; v10Y = theGridArray[id10 | 1]; v01Y = theGridArray[rowId1 | 1]; v11Y = theGridArray[id11 | 1]; //-- destination image pixel coordinate into grid space in Y direction. final double destY = (py + imageDest.getMinY()) / ((double) stepY); final double ty_t0y = (destY - gy); //-- ty - toy //-- constant value on X source coordinate. double coeff0X = ty_t0y * (v01X - v00X); double coeff1X = ty_t0y * (v11X - v10X); //-- constant value on Y source coordinate. double coeff0Y = ty_t0y * (v01Y - v00Y); double coeff1Y = ty_t0y * (v11Y - v10Y); while (px < interMaxX) { if (px == nextGIdX) { nextGIdX += stepX; if (++gx <= gridWidth - 2) { id10 += 2; id11 += 2; v00X = v10X; v01X = v11X; v10X = theGridArray[id10]; v11X = theGridArray[id11]; v00Y = v10Y; v01Y = v11Y; v10Y = theGridArray[id10 | 1]; v11Y = theGridArray[id11 | 1]; //-- coefficient exchange //-- on destination X axis coordinate coeff0X = coeff1X; coeff1X = ty_t0y * (v11X - v10X); //-- on destination Y axis coordinate coeff0Y = coeff1Y; coeff1Y = ty_t0y * (v11Y - v10Y); } else { gx--; } } //-- coordinate interpolation final double destX = (px + imageDest.getMinX()) / ((double) stepX); //-- remonter cette addition pour eviter n * final double tx_t0x = (destX - gx);//-- tx - t0x //-- Compute interpolation from destination image pixel coordinate and //-- computed source coordinates from grid. //-- interpolation on X coordinates final double srcX = tx_t0x * (coeff1X + v10X - v00X) + (1 - tx_t0x) * coeff0X + v00X; //-- interpolation on Y coordinates final double srcY = tx_t0x * (coeff1Y + v10Y - v00Y) + (1 - tx_t0x) * coeff0Y + v00Y; int band = 0; //-- pixel value interpolation //-- if destination coordinate transformation is out of source boundary. if (!interpol.checkInterpolate(srcX, srcY)) { while (band < numBands && destIterator.next()) { if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); band++; } } else { while (band < numBands && destIterator.next()) { double sample = interpol.interpolate(srcX, srcY, band++); if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); } } px++; } py++; } //------------------------- grid working -------------------------// rX += tileWidth; rMaxX += tileWidth; } rY += tileHeight; rMaxY += tileHeight; } } /** * Fill image without any grid, all pixels coordinates are transform by given {@link MathTransform}. * * @throws TransformException */ private void fillImageByAffineTransform(AffineTransform destCoordToSource) throws TransformException { int band; while (destIterator.next()) { band = 0; //-- Compute source coordinate from destination coordinate and mathtransform. destCoords[0] = destIterator.getX(); destCoords[1] = destIterator.getY(); destCoordToSource.transform(destCoords, 0, srcCoords, 0, 1); //-- if destination coordinate transformation is out of source boundary. if (!interpol.checkInterpolate(srcCoords[0], srcCoords[1])) { if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); //Todo : find a way to avoid code duplication while (++band < numBands) { destIterator.next(); if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); } } else { double sample = interpol.interpolate(srcCoords[0], srcCoords[1], band);//Todo : find a way to avoid code duplication if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); while (++band < numBands) { destIterator.next(); sample = interpol.interpolate(srcCoords[0], srcCoords[1], band); if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); } } } } /** * Fill image without any grid, all pixels coordinates are transform by given {@link MathTransform}. * * @throws TransformException */ private void fillImageByTransform() throws TransformException { int band; while (destIterator.next()) { band = 0; //-- Compute source coordinate from destination coordinate and mathtransform. destCoords[0] = destIterator.getX(); destCoords[1] = destIterator.getY(); destToSourceMathTransform.transform(destCoords, 0, srcCoords, 0, 1); //-- if destination coordinate transformation is out of source boundary. if (!interpol.checkInterpolate(srcCoords[0], srcCoords[1])) { if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); //Todo : find a way to avoid code duplication while (++band < numBands) { destIterator.next(); if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); } } else { double sample = interpol.interpolate(srcCoords[0], srcCoords[1], band); //Todo : find a way to avoid code duplication if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); while (++band < numBands) { destIterator.next(); sample = interpol.interpolate(srcCoords[0], srcCoords[1], band); if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); } } } } /** * Fill destination image from source image pixel interpolation. */ public void fillImage() throws TransformException { if (destToSourceMathTransform instanceof MathTransform2D) { try { final GridFactory gridFact = new GridFactory(0.125); final Object object = gridFact.create((MathTransform2D) destToSourceMathTransform, destIterator.getBoundary(false)); if (object instanceof AffineTransform) { fillImageByAffineTransform((AffineTransform) object); } else { theGrid = (ResampleGrid) object; fillImageByGrid(); } return; } catch (TransformException ex) { //-- leave to fall back } catch (ArithmeticException e) { //-- leave to fall back } } fillImageByTransform(); } /** * Please use {@link #fillImageByTransform() } method. * * @throws TransformException * @deprecated replace by {@link #fillImageByTransform() }. */ @Deprecated public void fillImagePx() throws TransformException { int band; while (destIterator.next()) { band = 0; //-- Compute source coordinate from destination coordinate and mathtransform. destCoords[0] = destIterator.getX(); destCoords[1] = destIterator.getY(); destToSourceMathTransform.transform(destCoords, 0, srcCoords, 0, 1); //-- if destination coordinate transformation is out of source boundary. if (!interpol.checkInterpolate(srcCoords[0], srcCoords[1])) { if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); while (++band < numBands) { destIterator.next(); if (fillValue != null) destIterator.setSampleDouble(fillValue[band]); } } else { double sample = interpol.interpolate(srcCoords[0], srcCoords[1], band); if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); while (++band < numBands) { destIterator.next(); sample = interpol.interpolate(srcCoords[0], srcCoords[1], band); if (clamp != null) sample = XMath.clamp(sample, clamp[0], clamp[1]); destIterator.setSampleDouble(sample); } } } } /** * Returns {@link Interpolation} object use to resample. * * @return {@link Interpolation} object use to resample. */ public Interpolation getInterpol() { return interpol; } /** * Returns the needed grid use to resample if exist or {@code null} if none. * * @return needed grid use to resample if exist or {@code null} if none. */ ResampleGrid getGrid() { return theGrid; } }