/* 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.algebra.constant; import it.geosolutions.jaiext.algebra.AlgebraDescriptor.Operator; import it.geosolutions.jaiext.iterators.RandomIterFactory; 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.WritableRaster; import java.util.Arrays; import java.util.Map; import javax.media.jai.ColormapOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.iterator.RandomIter; import com.sun.media.jai.util.ImageUtil; /** * An <code>OpImage</code> implementing any operation defined by the {@link Operator} enum on an image with a constant value array. * * <p> * This <code>OpImage</code> executes the operation on the pixel values of the source image on a per-band basis. * <p> * * The value of the pixel (x, y) in the destination image is defined as: * * <pre> * for (b = 0; b < numBands; b++) { * dst[y][x][b] = op.calculate(src1[y][x][b],const[b]); * } * </pre> * * <p> * If the result of the operation overflows/underflows the maximum/minimum value supported by the destination image, then it will be clamped to the * maximum/minimum value respectively. The data type <code>byte</code> is treated as unsigned, with maximum value as 255 and minimum value as 0. * * */ public final class OperationConstOpImage extends ColormapOpImage { /** Constant indicating that the inner random iterators must pre-calculate an array of the image positions */ public static final boolean ARRAY_CALC = true; /** Constant indicating that the inner random iterators must cache the current tile position */ public static final boolean TILE_CACHED = true; // private final static Logger LOGGER = Logger.getLogger(OperationConstOpImage.class.toString()); private final boolean hasNoData; private Range noData; private byte[][] byteLookupTable; private final boolean hasROI; private ROI roi; private final boolean caseA; private final boolean caseB; private final boolean caseC; private Operator op; private byte destNoDataByte; private short destNoDataShort; private int destNoDataInt; private float destNoDataFloat; private double destNoDataDouble; private double[] constants; private Rectangle roiBounds; private PlanarImage roiImage; /** * Constructs an <code>OperationConstOpImage</code>. * * <p> * The <code>layout</code> parameter may optionally contains the tile grid layout, sample model, and/or color model. The image dimension is * determined by the intersection of the bounding boxes of the two source images. * * <p> * The image layout of the first source image, <code>source1</code>, is used as the fall-back for the image layout of the destination image. Any * layout parameters not specified in the <code>layout</code> argument are set to the same value as that of <code>source1</code>. * * @param source the source image * @param config the hints * @param layout The destination image layout. * @param op Operation selected * @param constants the constants values to use during the operations * @param srcROI ROI used for reducing computation Area * @param noData NoData Range used for checking noData * @param destinationNoData value for replacing the source nodata values */ public OperationConstOpImage(RenderedImage source, Map config, ImageLayout layout, Operator op, double[] constants, ROI srcROI, Range noData, double destinationNoData) { super(source, layout, config, true); if (op == null) { throw new IllegalArgumentException("Operation Not Defined"); } else { this.op = op; if (!op.supportsMultipleValues()) { throw new IllegalArgumentException("Wrong Operation Defined"); } } // DataType check int srcDataType = source.getSampleModel().getDataType(); int dataType = getSampleModel().getDataType(); // DataType check for the operation if (!op.isDataTypeSupported(srcDataType)) { throw new IllegalArgumentException("This operation does not support DataType: " + srcDataType); } // Check on the Constants value if (constants == null || constants.length == 0) { throw new IllegalArgumentException("Constants not defined"); } int numBands = getSampleModel().getNumBands(); // If constants size is smaller than the bands number, we take only the first value if (constants.length < numBands) { this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (double[]) constants.clone(); } // Destination No Data value is clamped to the image data type this.destNoDataDouble = destinationNoData; switch (dataType) { case DataBuffer.TYPE_BYTE: this.destNoDataByte = ImageUtil.clampRoundByte(destinationNoData); break; case DataBuffer.TYPE_USHORT: this.destNoDataShort = ImageUtil.clampRoundUShort(destinationNoData); break; case DataBuffer.TYPE_SHORT: this.destNoDataShort = ImageUtil.clampRoundShort(destinationNoData); break; case DataBuffer.TYPE_INT: this.destNoDataInt = ImageUtil.clampRoundInt(destinationNoData); break; case DataBuffer.TYPE_FLOAT: this.destNoDataFloat = ImageUtil.clampFloat(destinationNoData); break; case DataBuffer.TYPE_DOUBLE: break; default: throw new IllegalArgumentException("Wrong image data type"); } // Check if No Data control must be done if (noData != null) { hasNoData = true; this.noData = noData; } else { hasNoData = false; } // Check if ROI control must be done if (srcROI != null) { hasROI = true; // Roi object roi = srcROI; roiBounds = roi.getBounds(); } else { hasROI = false; roi = null; } // Definition of the possible cases that can be found // caseA = no ROI nor No Data // caseB = ROI present but No Data not present // caseC = No Data present but ROI not present // Last case not defined = both ROI and No Data are present caseA = !hasNoData && !hasROI; caseB = !hasNoData && hasROI; caseC = hasNoData && !hasROI; // Set flag to permit in-place operation. permitInPlaceOperation(); // Permit colormap operation initializeColormapOperation(); if (dataType == DataBuffer.TYPE_BYTE) { initByteTable(); } } private void initByteTable() { if (byteLookupTable != null) { return; } int nbands = constants.length; byteLookupTable = new byte[nbands][256]; boolean supportsFloat = op.isDataTypeSupported(DataBuffer.TYPE_FLOAT); // Initialize table which implements the operation and clamp for (int band = 0; band < nbands; band++) { int k = ImageUtil.clampRoundInt(constants[band]); float kF = (float) constants[band]; byte[] t = byteLookupTable[band]; for (int i = 0; i < 256; i++) { if (hasNoData && noData.contains((byte) i)) { t[i] = destNoDataByte; } else { if (supportsFloat) { t[i] = ImageUtil.clampRoundByte(op.calculate(i, kF)); } else { t[i] = ImageUtil.clampByte(op.calculate(i, k)); } } } } } /** * Computes the final pixel from the source image within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. final RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); final RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); ROI roiTile = null; RandomIter roiIter = null; boolean roiContainsTile = false; boolean roiDisjointTile = false; // If a ROI is present, then only the part contained inside the current tile bounds is taken. if (hasROI) { Rectangle srcRectExpanded = mapDestRect(destRect, 0); // The tile dimension is extended for avoiding border errors srcRectExpanded.setRect(srcRectExpanded.getMinX() - 1, srcRectExpanded.getMinY() - 1, srcRectExpanded.getWidth() + 2, srcRectExpanded.getHeight() + 2); roiTile = roi.intersect(new ROIShape(srcRectExpanded)); if (!roiBounds.intersects(srcRectExpanded)) { roiDisjointTile = true; } else { roiContainsTile = roiTile.contains(srcRectExpanded); if (!roiContainsTile) { if (!roiTile.intersects(srcRectExpanded)) { roiDisjointTile = true; } else { PlanarImage roiIMG = getImage(); roiIter = RandomIterFactory.create(roiIMG, null, TILE_CACHED, ARRAY_CALC); } } } } if (!hasROI || !roiDisjointTile) { switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst, roiIter, roiContainsTile); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst, roiIter, roiContainsTile); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst, roiIter, roiContainsTile); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst, roiIter, roiContainsTile); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst, roiIter, roiContainsTile); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst, roiIter, roiContainsTile); break; } // After the calculations, the output data are copied into the WritableRaster if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } } else { // If the tile is outside the ROI, then the destination Raster is set to backgroundValues double[] bkg = new double[dest.getSampleModel().getNumBands()]; Arrays.fill(bkg, destNoDataDouble); ImageUtil.fillBackground(dest, destRect, bkg); } } private void computeRectByte(RasterAccessor src, RasterAccessor dst, final RandomIter roiIter, boolean roiContainsTile) { // Initial settings int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); final int dstBands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); int x0 = 0; int y0 = 0; int srcX = src.getX(); int srcY = src.getY(); if (hasROI && !roiContainsTile) { for (int b = 0; b < dstBands; b++) { byte[] d = dData[b]; byte[] s = srcData[b]; byte[] bandTable = byteLookupTable[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { d[dstPixelOffset] = destNoDataByte; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = bandTable[s[srcPixelOffset] & 0xFF]; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else { for (int b = 0; b < dstBands; b++) { byte[] d = dData[b]; byte[] s = srcData[b]; byte[] bandTable = byteLookupTable[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = bandTable[s[srcPixelOffset] & 0xFF]; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst, final RandomIter roiIter, boolean roiContainsTile) { // Initial settings int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); final int dstBands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); int x0 = 0; int y0 = 0; int srcX = src.getX(); int srcY = src.getY(); boolean supportsFloat = op.isDataTypeSupported(DataBuffer.TYPE_FLOAT); if (caseA || (caseB && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (op.isUshortSupported()) { if (supportsFloat) { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], cf); } else { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], c); } } else { if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, cf)); } else { d[dstPixelOffset] = ImageUtil.clampUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, c)); } } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseB) { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { d[dstPixelOffset] = destNoDataShort; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (op.isUshortSupported()) { if (supportsFloat) { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], cf); } else { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], c); } } else { if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, cf)); } else { d[dstPixelOffset] = ImageUtil.clampUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, c)); } } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataShort; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (op.isUshortSupported()) { if (supportsFloat) { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], cf); } else { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], c); } } else { if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, cf)); } else { d[dstPixelOffset] = ImageUtil.clampUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, c)); } } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0) || noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataShort; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (op.isUshortSupported()) { if (supportsFloat) { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], cf); } else { d[dstPixelOffset] = (short) op.calculate(s[srcPixelOffset], c); } } else { if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, cf)); } else { d[dstPixelOffset] = ImageUtil.clampUShort(op.calculate( s[srcPixelOffset] & 0xFFFF, c)); } } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst, final RandomIter roiIter, boolean roiContainsTile) { // Initial settings int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); final int dstBands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); int x0 = 0; int y0 = 0; int srcX = src.getX(); int srcY = src.getY(); boolean supportsFloat = op.isDataTypeSupported(DataBuffer.TYPE_FLOAT); if (caseA || (caseB && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundShort(op.calculate( s[srcPixelOffset], cf)); } else { d[dstPixelOffset] = ImageUtil.clampShort(op.calculate( s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseB) { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { d[dstPixelOffset] = destNoDataShort; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundShort(op.calculate( s[srcPixelOffset], cf)); } else { d[dstPixelOffset] = ImageUtil.clampShort(op.calculate( s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataShort; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundShort(op.calculate( s[srcPixelOffset], cf)); } else { d[dstPixelOffset] = ImageUtil.clampShort(op.calculate( s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else { for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); float cf = (float) constants[b]; short[] d = dData[b]; short[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0) || noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataShort; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (supportsFloat) { d[dstPixelOffset] = ImageUtil.clampRoundShort(op.calculate( s[srcPixelOffset], cf)); } else { d[dstPixelOffset] = ImageUtil.clampShort(op.calculate( s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst, final RandomIter roiIter, boolean roiContainsTile) { // Initial settings int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); final int dstBands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); int x0 = 0; int y0 = 0; int srcX = src.getX(); int srcY = src.getY(); boolean supportsDouble = op.isDataTypeSupported(DataBuffer.TYPE_DOUBLE); if (caseA || (caseB && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { long c = ImageUtil.clampRoundInt(constants[b]); int[] d = dData[b]; int[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (supportsDouble) { d[dstPixelOffset] = ImageUtil.clampRoundInt(op.calculate( s[srcPixelOffset], constants[b])); } else { d[dstPixelOffset] = ImageUtil.clampInt(op.calculateL(s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseB) { for (int b = 0; b < dstBands; b++) { long c = ImageUtil.clampRoundInt(constants[b]); int[] d = dData[b]; int[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { d[dstPixelOffset] = destNoDataInt; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (supportsDouble) { d[dstPixelOffset] = ImageUtil.clampRoundInt(op.calculate( s[srcPixelOffset], constants[b])); } else { d[dstPixelOffset] = ImageUtil.clampInt(op.calculateL(s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { long c = ImageUtil.clampRoundInt(constants[b]); int[] d = dData[b]; int[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataInt; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (supportsDouble) { d[dstPixelOffset] = ImageUtil.clampRoundInt(op.calculate( s[srcPixelOffset], constants[b])); } else { d[dstPixelOffset] = ImageUtil.clampInt(op.calculateL(s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else { for (int b = 0; b < dstBands; b++) { long c = ImageUtil.clampRoundInt(constants[b]); int[] d = dData[b]; int[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0) || noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataInt; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } if (supportsDouble) { d[dstPixelOffset] = ImageUtil.clampRoundInt(op.calculate( s[srcPixelOffset], constants[b])); } else { d[dstPixelOffset] = ImageUtil.clampInt(op.calculateL(s[srcPixelOffset], c)); } dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst, final RandomIter roiIter, boolean roiContainsTile) { // Initial settings int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); final int dstBands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); float[][] dData = dst.getFloatDataArrays(); int x0 = 0; int y0 = 0; int srcX = src.getX(); int srcY = src.getY(); if (caseA || (caseB && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { float[] d = dData[b]; float[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampFloat(op.calculate(s[srcPixelOffset], constants[b])); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseB) { for (int b = 0; b < dstBands; b++) { float[] d = dData[b]; float[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { d[dstPixelOffset] = destNoDataFloat; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = ImageUtil.clampFloat(op.calculate(s[srcPixelOffset], constants[b])); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { float[] d = dData[b]; float[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataFloat; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = ImageUtil.clampFloat(op.calculate(s[srcPixelOffset], constants[b])); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else { for (int b = 0; b < dstBands; b++) { float[] d = dData[b]; float[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0) || noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataFloat; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = ImageUtil.clampFloat(op.calculate(s[srcPixelOffset], constants[b])); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst, final RandomIter roiIter, boolean roiContainsTile) { // Initial settings int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); final int dstWidth = dst.getWidth(); final int dstHeight = dst.getHeight(); final int dstBands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); double[][] dData = dst.getDoubleDataArrays(); int x0 = 0; int y0 = 0; int srcX = src.getX(); int srcY = src.getY(); if (caseA || (caseB && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { double[] d = dData[b]; double[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = op.calculate(s[srcPixelOffset], constants[b]); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseB) { for (int b = 0; b < dstBands; b++) { double[] d = dData[b]; double[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) { d[dstPixelOffset] = destNoDataDouble; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = op.calculate(s[srcPixelOffset], constants[b]); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int b = 0; b < dstBands; b++) { double[] d = dData[b]; double[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { if (noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataDouble; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = op.calculate(s[srcPixelOffset], constants[b]); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } else { for (int b = 0; b < dstBands; b++) { double[] d = dData[b]; double[] s = srcData[b]; int dstLineOffset = dBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { x0 = srcX + w; y0 = srcY + h; if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0) || noData.contains(s[srcPixelOffset])) { d[dstPixelOffset] = destNoDataDouble; dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; continue; } d[dstPixelOffset] = op.calculate(s[srcPixelOffset], constants[b]); dstPixelOffset += dPixelStride; srcPixelOffset += srcPixelStride; } } } } } @Override protected void transformColormap(byte[][] colormap) { initByteTable(); for (int b = 0; b < 3; b++) { byte[] map = colormap[b]; byte[] luTable = byteLookupTable[b >= byteLookupTable.length ? 0 : b]; int mapSize = map.length; for (int i = 0; i < mapSize; i++) { map[i] = luTable[(map[i] & 0xFF)]; } } } /** * This method provides a lazy initialization of the image associated to the ROI. The method uses the Double-checked locking in order to maintain * thread-safety * * @return */ private PlanarImage getImage() { PlanarImage img = roiImage; if (img == null) { synchronized (this) { img = roiImage; if (img == null) { roiImage = img = roi.getAsImage(); } } } return img; } }