/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.border; import it.geosolutions.jaiext.range.Range; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.BorderExtenderZero; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import com.sun.media.jai.util.ImageUtil; import java.util.Map; /** * An <code>OpImage</code> implementing the "border" operation. * * <p> * It adds a border around a source image. The size of the border is specified by the left, right, top, and bottom padding parameters. The border may * be filled in a variety of ways specified by the border type parameter: * <ul> * <li>it may be extended with zeros (BORDER_ZERO_FILL); * <li>it may be extended with a constant set of values (BORDER_CONST_FILL); * <li) it may be created by copying the edge and corner pixels (BORDER_EXTEND); * <li>it may be created by reflection about the edges of the image (BORDER_REFLECT); or, * <li>it may be extended by "wrapping" the image plane toroidally, that is, joining opposite edges of the image. * </ul> * * <p> * When choosing the <code>BORDER_CONST_FILL</code> option, an array of constants must be supplied to the extender. The array must have at least one * element, in which case this same constant is applied to all image bands. Or, it may have a different constant entry for each corresponding band. * For all other border types, this <code>constants</code> parameter may be <code>null</code>. * * <p> * The layout information for this image may be specified via the <code>layout</code> parameter. However, due to the nature of this operation, the * <code>minX</code>, <code>minY</code>, <code>width</code>, and <code>height</code>, if specified, will be ignored. They will be calculated based on * the source's dimensions and the padding values. Likewise, the <code>SampleModel</code> and </code>ColorModel</code> hints will be ignored. * * If No Data are present, an optional No Data Range and a double value for the output No Data can be provided for avoiding to fill the Borders with * No Data. * */ public class BorderOpImage extends OpImage { /** * The <code>BorderExtender</code> object used to extend the source data. */ protected BorderExtender extender; /** No Data Range */ private Range noData; /** Boolean indicating if the No Data are present */ private final boolean hasNoData; /** Output No Data for Byte images */ private byte destNoDataByte; /** Output No Data for Short/UShort images */ private short destNoDataShort; /** Output No Data for Integer images */ private int destNoDataInt; /** Output No Data for Float images */ private float destNoDataFloat; /** Output No Data for Double images */ private double destNoDataDouble; /** LookupTable used for handling No Data in Byte images */ private byte[] byteLookupTable; /** Boolean indicating if the Borders must be checked for No Data */ private final boolean checkBorders; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param leftPad The amount of padding to the left of the source. * @param rightPad The amount of padding to the right of the source. * @param topPad The amount of padding to the top of the source. * @param bottomPad The amount of padding to the bottom of the source. * @param type The border type. * @param noData optional NoData Range. * @param destinationNoData value for replacing input No Data values */ public BorderOpImage(RenderedImage source, Map config, ImageLayout layout, int leftPad, int rightPad, int topPad, int bottomPad, BorderExtender extender, Range noData, double destinationNoData) { super(vectorize(source), layoutHelper(layout, source, leftPad, rightPad, topPad, bottomPad), config, true); // Destination Image data Type int dataType = getSampleModel().getDataType(); // If No Data are present if (noData != null) { this.noData = noData; // No Data are present, so associated flag is set to true this.hasNoData = true; // Destination No Data value is clamped to the image data type switch (dataType) { case DataBuffer.TYPE_BYTE: // Destination NoData clamping this.destNoDataByte = ImageUtil.clampRoundByte(destinationNoData); // Creation of the Lookup Table for No Data byteLookupTable = new byte[256]; int lookupTableLenght = byteLookupTable.length; for (int i = 0; i < lookupTableLenght; i++) { byte value = (byte) i; if (noData.contains(value)) { byteLookupTable[i] = destNoDataByte; } else { byteLookupTable[i] = value; } } break; case DataBuffer.TYPE_USHORT: // Destination NoData clamping this.destNoDataShort = ImageUtil.clampRoundUShort(destinationNoData); break; case DataBuffer.TYPE_SHORT: // Destination NoData clamping this.destNoDataShort = ImageUtil.clampRoundShort(destinationNoData); break; case DataBuffer.TYPE_INT: // Destination NoData clamping this.destNoDataInt = ImageUtil.clampRoundInt(destinationNoData); break; case DataBuffer.TYPE_FLOAT: // Destination NoData clamping this.destNoDataFloat = ImageUtil.clampFloat(destinationNoData); break; case DataBuffer.TYPE_DOUBLE: // Destination NoData clamping this.destNoDataDouble = destinationNoData; break; default: throw new IllegalArgumentException("Wrong image data type"); } } else { this.noData = null; this.hasNoData = false; } // Border Extender used this.extender = extender; boolean notZeroExtender = !(extender instanceof BorderExtenderZero); // Check if the No Data check on the borders must be done checkBorders = hasNoData && notZeroExtender; } /** * Sets up the image layout information for this Operation. The minX, minY, width, and height are calculated based on the source's dimension and * padding values. Any of these values specified in the layout parameter is ignored. All other variables are taken from the layout parameter or * inherited from the source. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, int leftPad, int rightPad, int topPad, int bottomPad) { ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout) layout.clone(); // Set the image bounds according to the padding. il.setMinX(source.getMinX() - leftPad); il.setMinY(source.getMinY() - topPad); il.setWidth(source.getWidth() + leftPad + rightPad); il.setHeight(source.getHeight() + topPad + bottomPad); // Set tile grid offset to minimize the probability that a // tile's bounds does not intersect the source image bounds. if (!il.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) { il.setTileGridXOffset(il.getMinX(null)); } if (!il.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) { il.setTileGridYOffset(il.getMinY(null)); } // Force inheritance of source image SampleModel and ColorModel. il.setSampleModel(source.getSampleModel()); il.setColorModel(source.getColorModel()); return il; } /** * Returns an estimate of the destination region that can potentially be affected by the pixels of a rectangle of a given source. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected destination region. or null if the region is unknown. * @throws IllegalArgumentException if the source index is negative or greater than that of the last source. * @throws IllegalArgumentException if sourceRect is null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if (sourceRect == null) { throw new IllegalArgumentException("Source Rectangle Not Defined"); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException("Source index Out Of Bounds"); } return new Rectangle(sourceRect); } /** * Returns a conservative estimate of the region of a specified source that is required in order to compute the pixels of a given destination * rectangle. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the required source region. * @throws IllegalArgumentException if the source index is negative or greater than that of the last source. * @throws IllegalArgumentException if destRect is null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if (destRect == null) { throw new IllegalArgumentException("Destination Rectangle Not Defined"); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException("Source index Out Of Bounds"); } Rectangle srcBounds = getSourceImage(0).getBounds(); return destRect.intersection(srcBounds); } /** Computes the pixel values for the specified tile. */ public Raster computeTile(int tileX, int tileY) { // Create a new Raster. WritableRaster dest = createTile(tileX, tileY); // Destination Raster data type int dataType = dest.getSampleModel().getDataType(); // Source Image PlanarImage sourceImage = getSourceImage(0); // Source image bounds Rectangle imageBounds = sourceImage.getBounds(); // Destination raster bounds Rectangle destRect = dest.getBounds(); // If image bounds contains all the destination bounds if (imageBounds.contains(destRect)) { copyRasterData(dest, sourceImage); } else { // Get the intersection of the Raster and image bounds. Rectangle isect = imageBounds.intersection(destRect); if (!isect.isEmpty()) { // Copy image data into the dest Raster. WritableRaster isectRaster = dest.createWritableChild(isect.x, isect.y, isect.width, isect.height, isect.x, isect.y, null); copyRasterData(isectRaster, sourceImage); } // Extend the Raster. extender.extend(dest, sourceImage); // Check if the borders contains noData if (checkBorders) { switch (dataType) { case DataBuffer.TYPE_BYTE: fillNoDataByte(dest); break; case DataBuffer.TYPE_USHORT: fillNoDataUshort(dest); break; case DataBuffer.TYPE_SHORT: fillNoDataShort(dest); break; case DataBuffer.TYPE_INT: fillNoDataInt(dest); break; case DataBuffer.TYPE_FLOAT: fillNoDataFloat(dest); break; case DataBuffer.TYPE_DOUBLE: fillNoDataDouble(dest); break; default: throw new IllegalArgumentException("Wrong data type"); } } } return dest; } /** * Method used copying the source image data inside the destination Raster * * @param dest * @param sourceImage * @return */ private WritableRaster copyRasterData(WritableRaster dest, PlanarImage sourceImage) { // Source and destination bounds Rectangle destRect = dest.getBounds(); Rectangle imageBounds = sourceImage.getBounds(); Rectangle region = destRect.intersection(imageBounds); if (region.isEmpty()) { // Raster is outside of image's boundary return dest; } // Data Type int dataType = dest.getSampleModel().getDataType(); // Cycle on all the source tiles that intersect with the destination raster int startTileX = sourceImage.XToTileX(region.x); int startTileY = sourceImage.YToTileY(region.y); int endTileX = sourceImage.XToTileX(region.x + region.width - 1); int endTileY = sourceImage.YToTileY(region.y + region.height - 1); SampleModel[] sampleModels = { sourceImage.getSampleModel() }; int tagID = RasterAccessor.findCompatibleTag(sampleModels, dest.getSampleModel()); RasterFormatTag srcTag = new RasterFormatTag(sampleModels[0], tagID); RasterFormatTag dstTag = new RasterFormatTag(dest.getSampleModel(), tagID); for (int ty = startTileY; ty <= endTileY; ty++) { for (int tx = startTileX; tx <= endTileX; tx++) { Raster tile = sourceImage.getTile(tx, ty); Rectangle subRegion = region.intersection(tile.getBounds()); // RasterAccessor associated with the input and output tiles RasterAccessor src = new RasterAccessor(tile, subRegion, srcTag, sourceImage.getColorModel()); RasterAccessor dst = new RasterAccessor(dest, subRegion, dstTag, null); // Elaboration of the RasterAccessors switch (dataType) { case DataBuffer.TYPE_BYTE: byteLoop(src, dst); break; case DataBuffer.TYPE_USHORT: ushortLoop(src, dst); break; case DataBuffer.TYPE_SHORT: shortLoop(src, dst); break; case DataBuffer.TYPE_INT: intLoop(src, dst); break; case DataBuffer.TYPE_FLOAT: floatLoop(src, dst); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(src, dst); break; default: throw new IllegalArgumentException("Wrong data type"); } if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } } } return dest; } private void byteLoop(RasterAccessor src, RasterAccessor dst) { // RasterAccessor definitions int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth(); int height = dst.getHeight(); int bands = dst.getNumBands(); byte[][] bSrcData = src.getByteDataArrays(); byte[][] bDstData = dst.getByteDataArrays(); if (hasNoData) { // Cycle on the image bands for (int b = 0; b < bands; b++) { byte[] s = bSrcData[b]; byte[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { int value = s[srcPixelOffset] & 0xFF; // No Data check d[dstPixelOffset] = byteLookupTable[value]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } else { // Cycle on the image bands for (int b = 0; b < bands; b++) { byte[] s = bSrcData[b]; byte[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } } private void ushortLoop(RasterAccessor src, RasterAccessor dst) { // RasterAccessor definitions int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth(); int height = dst.getHeight(); int bands = dst.getNumBands(); short[][] bSrcData = src.getShortDataArrays(); short[][] bDstData = dst.getShortDataArrays(); if (hasNoData) { // Cycle on the image bands for (int b = 0; b < bands; b++) { short[] s = bSrcData[b]; short[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { int value = s[srcPixelOffset] & 0xFFFF; short valueShort = (short) value; // No Data check if (noData.contains(valueShort)) { d[dstPixelOffset] = destNoDataShort; } else { d[dstPixelOffset] = valueShort; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } else { // Cycle on the image bands for (int b = 0; b < bands; b++) { short[] s = bSrcData[b]; short[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { // RasterAccessor definitions int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth(); int height = dst.getHeight(); int bands = dst.getNumBands(); short[][] bSrcData = src.getShortDataArrays(); short[][] bDstData = dst.getShortDataArrays(); if (hasNoData) { // Cycle on the image bands for (int b = 0; b < bands; b++) { short[] s = bSrcData[b]; short[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { short value = s[srcPixelOffset]; // No Data check if (noData.contains(value)) { d[dstPixelOffset] = destNoDataShort; } else { d[dstPixelOffset] = value; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } else { // Cycle on the image bands for (int b = 0; b < bands; b++) { short[] s = bSrcData[b]; short[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } } private void intLoop(RasterAccessor src, RasterAccessor dst) { // RasterAccessor definitions int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth(); int height = dst.getHeight(); int bands = dst.getNumBands(); int[][] bSrcData = src.getIntDataArrays(); int[][] bDstData = dst.getIntDataArrays(); if (hasNoData) { // Cycle on the image bands for (int b = 0; b < bands; b++) { int[] s = bSrcData[b]; int[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { int value = s[srcPixelOffset]; // No Data check if (noData.contains(value)) { d[dstPixelOffset] = destNoDataInt; } else { d[dstPixelOffset] = value; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } else { // Cycle on the image bands for (int b = 0; b < bands; b++) { int[] s = bSrcData[b]; int[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { // RasterAccessor definitions int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth(); int height = dst.getHeight(); int bands = dst.getNumBands(); float[][] bSrcData = src.getFloatDataArrays(); float[][] bDstData = dst.getFloatDataArrays(); if (hasNoData) { // Cycle on the image bands for (int b = 0; b < bands; b++) { float[] s = bSrcData[b]; float[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { float value = s[srcPixelOffset]; // No Data check if (noData.contains(value)) { d[dstPixelOffset] = destNoDataFloat; } else { d[dstPixelOffset] = value; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } else { // Cycle on the image bands for (int b = 0; b < bands; b++) { float[] s = bSrcData[b]; float[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { // RasterAccessor definitions int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth(); int height = dst.getHeight(); int bands = dst.getNumBands(); double[][] bSrcData = src.getDoubleDataArrays(); double[][] bDstData = dst.getDoubleDataArrays(); if (hasNoData) { // Cycle on the image bands for (int b = 0; b < bands; b++) { double[] s = bSrcData[b]; double[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { double value = s[srcPixelOffset]; // No Data check if (noData.contains(value)) { d[dstPixelOffset] = destNoDataDouble; } else { d[dstPixelOffset] = value; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } else { // Cycle on the image bands for (int b = 0; b < bands; b++) { double[] s = bSrcData[b]; double[] d = bDstData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int y = 0; y < height; y++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; for (int x = 0; x < width; x++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; } } } } /** * Inner method used for checking if the Borders contains No Data and substituting them. (Byte images). * * @param dest */ private void fillNoDataByte(WritableRaster dest) { Rectangle srcBounds = getSourceImage(0).getBounds(); Rectangle destBounds = dest.getBounds(); // The operation is performed only on the image borders if (!srcBounds.contains(destBounds) && !(destBounds.intersection(srcBounds).isEmpty())) { int minX = dest.getMinX(); int minY = dest.getMinY(); int maxX = minX + dest.getWidth(); int maxY = minY + dest.getHeight(); int numBands = dest.getNumBands(); for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { for (int b = 0; b < numBands; b++) { int sample = dest.getSample(i, j, b) & 0xFF; // No Data check dest.setSample(i, j, b, byteLookupTable[sample]); } } } } } /** * Inner method used for checking if the Borders contains No Data and substituting them. (UShort images). * * @param dest */ private void fillNoDataUshort(WritableRaster dest) { Rectangle srcBounds = getSourceImage(0).getBounds(); Rectangle destBounds = dest.getBounds(); // The operation is performed only on the image borders if (!srcBounds.contains(destBounds) && !(destBounds.intersection(srcBounds).isEmpty())) { int minX = dest.getMinX(); int minY = dest.getMinY(); int maxX = minX + dest.getWidth(); int maxY = minY + dest.getHeight(); int numBands = dest.getNumBands(); for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { for (int b = 0; b < numBands; b++) { int sample = dest.getSample(i, j, b) & 0xFFFF; short sampleShort = (short) sample; // No Data check if (noData.contains(sampleShort)) { dest.setSample(i, j, b, destNoDataShort); } else { dest.setSample(i, j, b, sampleShort); } } } } } } /** * Inner method used for checking if the Borders contains No Data and substituting them. (Short images). * * @param dest */ private void fillNoDataShort(WritableRaster dest) { Rectangle srcBounds = getSourceImage(0).getBounds(); Rectangle destBounds = dest.getBounds(); // The operation is performed only on the image borders if (!srcBounds.contains(destBounds) && !(destBounds.intersection(srcBounds).isEmpty())) { int minX = dest.getMinX(); int minY = dest.getMinY(); int maxX = minX + dest.getWidth(); int maxY = minY + dest.getHeight(); int numBands = dest.getNumBands(); for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { for (int b = 0; b < numBands; b++) { short sample = (short) dest.getSample(i, j, b); // No Data check if (noData.contains(sample)) { dest.setSample(i, j, b, destNoDataShort); } else { dest.setSample(i, j, b, sample); } } } } } } /** * Inner method used for checking if the Borders contains No Data and substituting them. (Integer images). * * @param dest */ private void fillNoDataInt(WritableRaster dest) { Rectangle srcBounds = getSourceImage(0).getBounds(); Rectangle destBounds = dest.getBounds(); // The operation is performed only on the image borders if (!srcBounds.contains(destBounds) && !(destBounds.intersection(srcBounds).isEmpty())) { int minX = dest.getMinX(); int minY = dest.getMinY(); int maxX = minX + dest.getWidth(); int maxY = minY + dest.getHeight(); int numBands = dest.getNumBands(); for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { for (int b = 0; b < numBands; b++) { int sample = dest.getSample(i, j, b); // No Data check if (noData.contains(sample)) { dest.setSample(i, j, b, destNoDataInt); } else { dest.setSample(i, j, b, sample); } } } } } } /** * Inner method used for checking if the Borders contains No Data and substituting them. (Float images). * * @param dest */ private void fillNoDataFloat(WritableRaster dest) { Rectangle srcBounds = getSourceImage(0).getBounds(); Rectangle destBounds = dest.getBounds(); // The operation is performed only on the image borders if (!srcBounds.contains(destBounds) && !(destBounds.intersection(srcBounds).isEmpty())) { int minX = dest.getMinX(); int minY = dest.getMinY(); int maxX = minX + dest.getWidth(); int maxY = minY + dest.getHeight(); int numBands = dest.getNumBands(); for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { for (int b = 0; b < numBands; b++) { float sample = dest.getSampleFloat(i, j, b); // No Data check if (noData.contains(sample)) { dest.setSample(i, j, b, destNoDataFloat); } else { dest.setSample(i, j, b, sample); } } } } } } /** * Inner method used for checking if the Borders contains No Data and substituting them. (Double images). * * @param dest */ private void fillNoDataDouble(WritableRaster dest) { Rectangle srcBounds = getSourceImage(0).getBounds(); Rectangle destBounds = dest.getBounds(); // The operation is performed only on the image borders if (!srcBounds.contains(destBounds) && !(destBounds.intersection(srcBounds).isEmpty())) { int minX = dest.getMinX(); int minY = dest.getMinY(); int maxX = minX + dest.getWidth(); int maxY = minY + dest.getHeight(); int numBands = dest.getNumBands(); for (int i = minX; i < maxX; i++) { for (int j = minY; j < maxY; j++) { for (int b = 0; b < numBands; b++) { double sample = dest.getSampleDouble(i, j, b); // No Data check if (noData.contains(sample)) { dest.setSample(i, j, b, destNoDataDouble); } else { dest.setSample(i, j, b, sample); } } } } } } }