/* 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.rlookup; 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.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; 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; /** * This is a variation on the JAI {@linkplain javax.media.jai.LookupDescriptor}. It works with a {@linkplain RangeLookupTable} object in which each * entry maps a source image value range to a destination image value. Optional {@link ROI}s may be used in computations. * * @see RangeLookupDescriptor * * @author Michael Bedward * @author Simone Giannecchini, GeoSolutions */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class RangeLookupOpImage extends PointOpImage { /** * 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; /** {@link RangeLookupTable} object used for the operation */ private final RangeLookupTable table; /** Value to set for pixel outside {@link RangeLookupTable} range or outside ROI */ private final Number defaultValue; /** Optional {@link ROI} used in computation */ private final ROI roi; /** Boolean indicating if the default value is defined */ private final boolean hasDefault; /** Boolean indicating if ROI is defined */ private boolean hasROI; /** ROI bounds, used for a fast ROI check */ private Rectangle roiBounds; /** {@link PlanarImage} which represents the binarized ROI */ private PlanarImage roiImage; /** * Constructor * * @param source a RenderedImage. * @param config configurable attributes of the image * * @param layout an ImageLayout optionally containing the tile grid layout, SampleModel, and ColorModel, or null. * * @param table an instance of RangeLookupTable that defines the mappings from source value ranges to destination values * * @param defaultValue either a value to use for all unmatched source values or null to indicate that unmatched values should pass-through to the * destination * @param roi {@link ROI} object used for masking image areas. * * @see RangeLookupDescriptor */ public RangeLookupOpImage(RenderedImage source, Map config, ImageLayout layout, RangeLookupTable table, Number defaultValue, ROI roi) { super(source, layout, config, true); this.table = table; this.defaultValue = defaultValue; this.hasDefault = defaultValue != null; this.roi = roi; hasROI = roi != null; if (hasROI) { roiBounds = roi.getBounds(); } } /** * Do lookups for the specified destination rectangle * * @param sources an array of source Rasters * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ @Override protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); 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 (roiDisjointTile) { int numBands = dest.getNumBands(); double[] destNoData = new double[numBands]; double nodata = hasDefault ? defaultValue.doubleValue() : 0d; for (int i = 0; i < numBands; i++) { destNoData[i] = nodata; } ImageUtil.fillBackground(dest, destRect, destNoData); return; } RasterAccessor srcAcc = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor destAcc = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); doLookup(srcAcc, destAcc, roiIter, roiContainsTile); } private void doLookup(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { switch (destAcc.getDataType()) { case DataBuffer.TYPE_BYTE: lookupAsByteData(srcAcc, destAcc, roiIter, roiContainsTile); break; case DataBuffer.TYPE_INT: lookupAsIntData(srcAcc, destAcc, roiIter, roiContainsTile); break; case DataBuffer.TYPE_SHORT: lookupAsShortData(srcAcc, destAcc, roiIter, roiContainsTile); break; case DataBuffer.TYPE_USHORT: lookupAsUShortData(srcAcc, destAcc, roiIter, roiContainsTile); break; case DataBuffer.TYPE_FLOAT: lookupAsFloatData(srcAcc, destAcc, roiIter, roiContainsTile); break; case DataBuffer.TYPE_DOUBLE: lookupAsDoubleData(srcAcc, destAcc, roiIter, roiContainsTile); break; } if (destAcc.isDataCopy()) { destAcc.clampDataArrays(); destAcc.copyDataToRaster(); } } private void lookupAsByteData(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { byte srcData[][] = srcAcc.getByteDataArrays(); byte destData[][] = destAcc.getByteDataArrays(); int destWidth = destAcc.getWidth(); int destHeight = destAcc.getHeight(); int destBands = destAcc.getNumBands(); int[] dstBandOffsets = destAcc.getBandOffsets(); int dstPixelStride = destAcc.getPixelStride(); int dstScanlineStride = destAcc.getScanlineStride(); int[] srcBandOffsets = srcAcc.getBandOffsets(); int srcPixelStride = srcAcc.getPixelStride(); int srcScanlineStride = srcAcc.getScanlineStride(); Range lastRange = null; byte typedDefaultValue = hasDefault ? defaultValue.byteValue() : Byte.MIN_VALUE; byte destinationValue = typedDefaultValue; if (hasROI && !roiContainsTile) { int srcScanlineOffset = 0; int dstScanlineOffset = 0; for (int j = 0, destY = destAcc.getY(); j < destHeight; j++, destY++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0, destX = destAcc.getX(); i < destWidth; i++, destX++) { if (!(roiBounds.contains(destX, destY) && roiIter.getSample(destX, destY, 0) > 0)) { for (int k = 0; k < destBands; k++) { // input value byte val = hasDefault ? typedDefaultValue : (byte) (srcData[k][srcPixelOffset + srcBandOffsets[k]] & 0xff); destData[k][dstPixelOffset + dstBandOffsets[k]] = val; } } else { for (int k = 0; k < destBands; k++) { // input value byte val = (byte) (srcData[k][srcPixelOffset + srcBandOffsets[k]] & 0xff); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().byteValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destData[k][dstPixelOffset + dstBandOffsets[k]] = destinationValue; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { for (int k = 0; k < destBands; k++) { byte destBandData[] = destData[k]; byte srcBandData[] = srcData[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < destHeight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < destWidth; i++) { // input value byte val = (byte) (srcBandData[srcPixelOffset] & 0xff); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().byteValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destBandData[dstPixelOffset] = destinationValue; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void lookupAsShortData(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { short srcData[][] = srcAcc.getShortDataArrays(); short destData[][] = destAcc.getShortDataArrays(); int destWidth = destAcc.getWidth(); int destHeight = destAcc.getHeight(); int destBands = destAcc.getNumBands(); int[] dstBandOffsets = destAcc.getBandOffsets(); int dstPixelStride = destAcc.getPixelStride(); int dstScanlineStride = destAcc.getScanlineStride(); int[] srcBandOffsets = srcAcc.getBandOffsets(); int srcPixelStride = srcAcc.getPixelStride(); int srcScanlineStride = srcAcc.getScanlineStride(); Range lastRange = null; short typedDefaultValue = hasDefault ? defaultValue.shortValue() : Short.MIN_VALUE; short destinationValue = typedDefaultValue; if (hasROI && !roiContainsTile) { int srcScanlineOffset = 0; int dstScanlineOffset = 0; for (int j = 0, destY = destAcc.getY(); j < destHeight; j++, destY++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0, destX = destAcc.getX(); i < destWidth; i++, destX++) { if (!(roiBounds.contains(destX, destY) && roiIter.getSample(destX, destY, 0) > 0)) { for (int k = 0; k < destBands; k++) { // input value short val = hasDefault ? typedDefaultValue : (srcData[k][srcPixelOffset + srcBandOffsets[k]]); destData[k][dstPixelOffset + dstBandOffsets[k]] = val; } } else { for (int k = 0; k < destBands; k++) { // input value short val = (srcData[k][srcPixelOffset + srcBandOffsets[k]]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().shortValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destData[k][dstPixelOffset + dstBandOffsets[k]] = destinationValue; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { for (int k = 0; k < destBands; k++) { short destBandData[] = destData[k]; short srcBandData[] = srcData[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < destHeight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < destWidth; i++) { // input value short val = (srcBandData[srcPixelOffset]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().shortValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destBandData[dstPixelOffset] = destinationValue; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void lookupAsUShortData(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { short srcData[][] = srcAcc.getShortDataArrays(); short destData[][] = destAcc.getShortDataArrays(); int destWidth = destAcc.getWidth(); int destHeight = destAcc.getHeight(); int destBands = destAcc.getNumBands(); int[] dstBandOffsets = destAcc.getBandOffsets(); int dstPixelStride = destAcc.getPixelStride(); int dstScanlineStride = destAcc.getScanlineStride(); int[] srcBandOffsets = srcAcc.getBandOffsets(); int srcPixelStride = srcAcc.getPixelStride(); int srcScanlineStride = srcAcc.getScanlineStride(); Range lastRange = null; short typedDefaultValue = hasDefault ? defaultValue.shortValue() : 0; short destinationValue = typedDefaultValue; if (hasROI && !roiContainsTile) { int srcScanlineOffset = 0; int dstScanlineOffset = 0; for (int j = 0, destY = destAcc.getY(); j < destHeight; j++, destY++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0, destX = destAcc.getX(); i < destWidth; i++, destX++) { if (!(roiBounds.contains(destX, destY) && roiIter.getSample(destX, destY, 0) > 0)) { for (int k = 0; k < destBands; k++) { // input value int val = hasDefault ? typedDefaultValue : srcData[k][srcPixelOffset] & 0xffff; destData[k][dstPixelOffset + dstBandOffsets[k]] = (short) val; } } else { for (int k = 0; k < destBands; k++) { // input value int val = srcData[k][srcPixelOffset] & 0xffff; // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().shortValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = (hasDefault ? typedDefaultValue : (short) val); } } destData[k][dstPixelOffset + dstBandOffsets[k]] = destinationValue; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { for (int k = 0; k < destBands; k++) { short destBandData[] = destData[k]; short srcBandData[] = srcData[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < destHeight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < destWidth; i++) { // input value int val = srcBandData[srcPixelOffset] & 0xffff; // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().shortValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : (short) val; } } destBandData[dstPixelOffset] = destinationValue; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void lookupAsIntData(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { int srcData[][] = srcAcc.getIntDataArrays(); int destData[][] = destAcc.getIntDataArrays(); int destWidth = destAcc.getWidth(); int destHeight = destAcc.getHeight(); int destBands = destAcc.getNumBands(); int[] dstBandOffsets = destAcc.getBandOffsets(); int dstPixelStride = destAcc.getPixelStride(); int dstScanlineStride = destAcc.getScanlineStride(); int[] srcBandOffsets = srcAcc.getBandOffsets(); int srcPixelStride = srcAcc.getPixelStride(); int srcScanlineStride = srcAcc.getScanlineStride(); Range lastRange = null; int typedDefaultValue = hasDefault ? defaultValue.intValue() : Integer.MIN_VALUE; int destinationValue = typedDefaultValue; if (hasROI && !roiContainsTile) { int srcScanlineOffset = 0; int dstScanlineOffset = 0; for (int j = 0, destY = destAcc.getY(); j < destHeight; j++, destY++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0, destX = destAcc.getX(); i < destWidth; i++, destX++) { if (!(roiBounds.contains(destX, destY) && roiIter.getSample(destX, destY, 0) > 0)) { for (int k = 0; k < destBands; k++) { // input value int val = hasDefault ? typedDefaultValue : (srcData[k][srcPixelOffset + srcBandOffsets[k]]); destData[k][dstPixelOffset + dstBandOffsets[k]] = val; } } else { for (int k = 0; k < destBands; k++) { // input value int val = (srcData[k][srcPixelOffset + srcBandOffsets[k]]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().intValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destData[k][dstPixelOffset + dstBandOffsets[k]] = destinationValue; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { for (int k = 0; k < destBands; k++) { int destBandData[] = destData[k]; int srcBandData[] = srcData[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < destHeight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < destWidth; i++) { // input value int val = (srcBandData[srcPixelOffset]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().intValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destBandData[dstPixelOffset] = destinationValue; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void lookupAsFloatData(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { float srcData[][] = srcAcc.getFloatDataArrays(); float destData[][] = destAcc.getFloatDataArrays(); int destWidth = destAcc.getWidth(); int destHeight = destAcc.getHeight(); int destBands = destAcc.getNumBands(); int[] dstBandOffsets = destAcc.getBandOffsets(); int dstPixelStride = destAcc.getPixelStride(); int dstScanlineStride = destAcc.getScanlineStride(); int[] srcBandOffsets = srcAcc.getBandOffsets(); int srcPixelStride = srcAcc.getPixelStride(); int srcScanlineStride = srcAcc.getScanlineStride(); Range lastRange = null; float typedDefaultValue = hasDefault ? defaultValue.floatValue() : Float.NaN; float destinationValue = typedDefaultValue; if (hasROI && !roiContainsTile) { int srcScanlineOffset = 0; int dstScanlineOffset = 0; for (int j = 0, destY = destAcc.getY(); j < destHeight; j++, destY++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0, destX = destAcc.getX(); i < destWidth; i++, destX++) { if (!(roiBounds.contains(destX, destY) && roiIter.getSample(destX, destY, 0) > 0)) { for (int k = 0; k < destBands; k++) { // input value float val = hasDefault ? typedDefaultValue : (srcData[k][srcPixelOffset + srcBandOffsets[k]]); destData[k][dstPixelOffset + dstBandOffsets[k]] = val; } } else { for (int k = 0; k < destBands; k++) { // input value float val = (srcData[k][srcPixelOffset + srcBandOffsets[k]]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().floatValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destData[k][dstPixelOffset + dstBandOffsets[k]] = destinationValue; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { for (int k = 0; k < destBands; k++) { float destBandData[] = destData[k]; float srcBandData[] = srcData[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < destHeight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < destWidth; i++) { // input value float val = (srcBandData[srcPixelOffset]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().floatValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destBandData[dstPixelOffset] = destinationValue; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void lookupAsDoubleData(RasterAccessor srcAcc, RasterAccessor destAcc, RandomIter roiIter, boolean roiContainsTile) { double srcData[][] = srcAcc.getDoubleDataArrays(); double destData[][] = destAcc.getDoubleDataArrays(); int destWidth = destAcc.getWidth(); int destHeight = destAcc.getHeight(); int destBands = destAcc.getNumBands(); int[] dstBandOffsets = destAcc.getBandOffsets(); int dstPixelStride = destAcc.getPixelStride(); int dstScanlineStride = destAcc.getScanlineStride(); int[] srcBandOffsets = srcAcc.getBandOffsets(); int srcPixelStride = srcAcc.getPixelStride(); int srcScanlineStride = srcAcc.getScanlineStride(); Range lastRange = null; double typedDefaultValue = hasDefault ? defaultValue.doubleValue() : Double.NaN; double destinationValue = typedDefaultValue; if (hasROI && !roiContainsTile) { int srcScanlineOffset = 0; int dstScanlineOffset = 0; for (int j = 0, destY = destAcc.getY(); j < destHeight; j++, destY++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0, destX = destAcc.getX(); i < destWidth; i++, destX++) { if (!(roiBounds.contains(destX, destY) && roiIter.getSample(destX, destY, 0) > 0)) { for (int k = 0; k < destBands; k++) { // input value double val = hasDefault ? typedDefaultValue : (srcData[k][srcPixelOffset + srcBandOffsets[k]]); destData[k][dstPixelOffset + dstBandOffsets[k]] = val; } } else { for (int k = 0; k < destBands; k++) { // input value double val = (srcData[k][srcPixelOffset + srcBandOffsets[k]]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().doubleValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destData[k][dstPixelOffset + dstBandOffsets[k]] = destinationValue; } } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { for (int k = 0; k < destBands; k++) { double destBandData[] = destData[k]; double srcBandData[] = srcData[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < destHeight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < destWidth; i++) { // input value double val = (srcBandData[srcPixelOffset]); // === destination value if (lastRange == null || !lastRange.contains(val)) { // nullify the current rane lastRange = null; // get a new one if the value falls within some LookupItem item = table.getLookupItem(val); if (item != null) { lastRange = item.getRange(); destinationValue = item.getValue().doubleValue(); } else { // no match: set destination to default value (if defined) // or source value destinationValue = hasDefault ? typedDefaultValue : val; } } destBandData[dstPixelOffset] = destinationValue; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } /** * 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; } }