/* 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.orderdither; import it.geosolutions.jaiext.iterators.RandomIterFactory; import it.geosolutions.jaiext.range.Range; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import javax.media.jai.ColorCube; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; 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.RasterFactory; import javax.media.jai.RasterFormatTag; import javax.media.jai.iterator.RandomIter; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An <code>OpImage</code> implementing the ordered dither operation as described in <code>OrderedDitherDescriptor</code>. * * <p> * This <code>OpImage</code> performs dithering of its source image into a single band image using a specified color cube and dither mask. Optional * ROI and NoData will be taken into account during processing. */ public class OrderedDitherOpImage 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; /** * Flag indicating that the generic implementation is used. */ private static final int TYPE_OD_GENERAL = 0; /** * Flag indicating that the optimized three-band implementation is used (byte data only). */ private static final int TYPE_OD_BYTE_LUT_3BAND = 1; /** * Flag indicating that the optimized N-band implementation is used (byte data only). */ private static final int TYPE_OD_BYTE_LUT_NBAND = 2; /** * Maximim dither LUT size: 16x16 4-band byte dither mask. */ private static final int DITHER_LUT_LENGTH_MAX = 16 * 16 * 4 * 256; /** * The maximum number of elements in the <code>DitherLUT</code> cache. */ private static final int DITHER_LUT_CACHE_LENGTH_MAX = 4; /** * A cache of <code>SoftReference</code>s to <code>DitherLUT</code> inner class instances. */ private static ArrayList ditherLUTCache = new ArrayList(DITHER_LUT_CACHE_LENGTH_MAX); /** * Flag indicating the implementation to be used. */ private int odType = TYPE_OD_GENERAL; /** * The number of bands in the source image. */ protected int numBands; /** * The array of color cube dimensions-less-one. */ protected int[] dims; /** * The array of color cube multipliers. */ protected int[] mults; /** * The adjusted offset of the color cube. */ protected int adjustedOffset; /** * The width of the dither mask. */ protected int maskWidth; /** * The height of the dither mask. */ protected int maskHeight; /** * The dither mask matrix scaled by 255. */ protected byte[][] maskDataByte; /** * The dither mask matrix scaled to USHORT range. */ protected int[][] maskDataInt; /** * The dither mask matrix scaled to "unsigned int" range. */ protected long[][] maskDataLong; /** * The dither mask matrix. */ protected float[][] maskDataFloat; /** * An inner class instance representing a dither lookup table. Used for byte data only when the table size is within a specified limit. */ protected DitherLUT odLUT = null; /** Boolean parameter indicating whether NoData range is present */ private final boolean hasNoData; /** Boolean parameter indicating whether ROI is present */ private final boolean hasROI; /** Nodata Range used for checking if input nodata are present */ private Range nodata; /** ROI object used for reducing computation area */ private ROI roi; /** Rectangle containing ROI bounds */ private Rectangle roiBounds; /** Boolean indicating if No Data and ROI are not used */ private boolean caseA; /** Boolean indicating if only the ROI is used */ private boolean caseB; /** Boolean indicating if only the No Data are used */ private boolean caseC; /** {@link PlanarImage} containg ROI data */ private PlanarImage roiImage; /** Output value for NoData */ private double destNoData; /** LookupTable used for a quick check on the input NoData */ private boolean[] lut; /** * Force the destination image to be single-banded. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, ColorCube colorMap) { ImageLayout il; if (layout == null) { il = new ImageLayout(source); } else { il = (ImageLayout) layout.clone(); } // Get the SampleModel. SampleModel sm = il.getSampleModel(source); // Ensure an appropriate SampleModel. if (colorMap.getNumBands() == 1 && colorMap.getNumEntries() == 2 && !ImageUtil.isBinary(il.getSampleModel(source))) { sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, il.getTileWidth(source), il.getTileHeight(source), 1); il.setSampleModel(sm); } // Make sure that this OpImage is single-banded. if (sm.getNumBands() != 1) { // TODO: Force to SHORT or USHORT if FLOAT or DOUBLE? sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if (cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } // Set an IndexColorModel on the image if: // a. none is provided in the layout; // b. source, destination, and colormap have byte data type; // c. the colormap has 3 bands; and // d. the source ColorModel is either null or is non-null // and has a ColorSpace equal to CS_sRGB. if ((layout == null || !il.isValid(ImageLayout.COLOR_MODEL_MASK)) && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE && il.getSampleModel(null).getDataType() == DataBuffer.TYPE_BYTE && colorMap.getDataType() == DataBuffer.TYPE_BYTE && colorMap.getNumBands() == 3) { ColorModel cm = source.getColorModel(); if (cm == null || (cm != null && cm.getColorSpace().isCS_sRGB())) { int size = colorMap.getNumEntries(); byte[][] cmap = new byte[3][256]; for (int i = 0; i < 3; i++) { byte[] band = cmap[i]; byte[] data = colorMap.getByteData(i); int offset = colorMap.getOffset(i); int end = offset + size; for (int j = 0; j < offset; j++) { band[j] = (byte) 0; } for (int j = offset; j < end; j++) { band[j] = data[j - offset]; } for (int j = end; j < 256; j++) { band[j] = (byte) 0xFF; } } il.setColorModel(new IndexColorModel(8, 256, cmap[0], cmap[1], cmap[2])); } } return il; } /** * Constructs an OrderedDitherOpImage object. May be used to convert a single- or multi-band image into a single-band image with a color map. * * <p> * The image dimensions are derived from the source image. The tile grid layout, SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source A RenderedImage. * @param layout An ImageLayout optionally containing the tile grid layout, SampleModel, and ColorModel, or null. * @param colorMap The color map to use which must have a number of bands equal to the number of bands in the source image. The offset of this * <code>ColorCube</code> must be the same for all bands. * @param ditherMask An an array of <code>KernelJAI</code> objects the dimension of which must equal the number of bands in the source image. The * <i>n</i>th element of the array contains a <code>KernelJAI</code> object which represents the dither mask matrix for the corresponding * band. All <code>KernelJAI</code> objects in the array must have the same dimensions and contain floating point values between 0.0F and * 1.0F. * @param roi Optional {@link ROI} used for masking raster areas * @param nodata NoData {@link Range} used for masking unwanted pixel values * @param destNoData Value to set as background */ public OrderedDitherOpImage(RenderedImage source, Map config, ImageLayout layout, ColorCube colorMap, KernelJAI[] ditherMask, ROI roi, Range nodata, double destNoData) { // Construct as a PointOpImage. super(source, layoutHelper(layout, source, colorMap), config, true); // Initialize the instance variables derived from the color map. numBands = colorMap.getNumBands(); mults = (int[]) colorMap.getMultipliers().clone(); dims = (int[]) colorMap.getDimsLessOne().clone(); adjustedOffset = colorMap.getAdjustedOffset(); // Initialize the instance variables derived from the dither mask. maskWidth = ditherMask[0].getWidth(); maskHeight = ditherMask[0].getHeight(); // Check for NoData hasNoData = nodata != null; if (hasNoData) { this.nodata = nodata; } // Check for ROI hasROI = roi != null; if (hasROI) { this.roi = roi; this.roiBounds = roi.getBounds(); } // Define the destination NoData value if ((hasROI || hasNoData) && destNoData < adjustedOffset) { throw new IllegalArgumentException( "Destination NoData must be greater than the adjustedOffset value"); } this.destNoData = destNoData; // 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 = !hasROI && !hasNoData; caseB = hasROI && !hasNoData; caseC = !hasROI && hasNoData; // Initialize the data required to effect the operation. // XXX Postpone until first invocation of computeRect()? initializeDitherData(sampleModel.getTransferType(), ditherMask); // Set flag to permit in-place operation. permitInPlaceOperation(); // Init the nodata lookup table for byte data if (hasNoData && sampleModel.getTransferType() == DataBuffer.TYPE_BYTE) { initNoDataLUT(); } } /** * Initialization of the LookupTable used for checking if a byte value is a NoData */ private void initNoDataLUT() { // init the table lut = new boolean[256]; // Populate it for (int i = 0; i < 256; i++) { byte b = (byte) i; lut[i] = !nodata.contains(b); } } /** * An inner class represting a lookup table to be used in the optimized implementations of ordered dithering of byte data. */ class DitherLUT { // Clones of color cube and dither mask data used to create the // dithering lookup table. private int[] dimsCache; private int[] multsCache; private byte[][] maskDataCache; // Stride values of the dither lookup table. public int ditherLUTBandStride; public int ditherLUTRowStride; public int ditherLUTColStride; // The dither lookup table. public byte[] ditherLUT; /** * Create an inner class object representing an ordered dither lookup table for byte data. * * @param dims The color cube dimensions less one. * @param mults The color cube multipliers. * @param maskData The dither mask data scaled to byte range. */ DitherLUT(int[] dims, int[] mults, byte[][] maskData) { // Clone the constructor parameters. dimsCache = (int[]) dims.clone(); multsCache = (int[]) mults.clone(); maskDataCache = new byte[maskData.length][]; for (int i = 0; i < maskData.length; i++) { maskDataCache[i] = (byte[]) maskData[i].clone(); } // Set dither lookup table stride values. ditherLUTColStride = 256; ditherLUTRowStride = maskWidth * ditherLUTColStride; ditherLUTBandStride = maskHeight * ditherLUTRowStride; // // Construct the big dither table. If indexed as a // multi-dimensional array this would be equivalent to: // // ditherLUT[band][ditherRow][ditherColumn][grayLevel] // // where ditherRow, Col are modulo the dither mask size. // // To minimize the table construction cost, precalculate // the bin value for a given band and gray level. Then use // the dithermask threshold value to determine whether to bump // the value up one level. Thus most of the work is done in the // outer loops, with a simple comparison left for the inner loop. // ditherLUT = new byte[numBands * ditherLUTBandStride]; int pDithBand = 0; int maskSize2D = maskWidth * maskHeight; for (int band = 0; band < numBands; band++) { int step = dims[band]; int delta = mults[band]; byte[] maskDataBand = maskData[band]; int sum = 0; for (int gray = 0; gray < 256; gray++) { int tmp = sum; int frac = (int) (tmp & 0xff); int bin = tmp >> 8; int lowVal = bin * delta; int highVal = lowVal + delta; int pDith = pDithBand + gray; for (int dcount = 0; dcount < maskSize2D; dcount++) { int threshold = maskDataBand[dcount] & 0xff; if (frac > threshold) { ditherLUT[pDith] = (byte) (highVal & 0xff); } else { ditherLUT[pDith] = (byte) (lowVal & 0xff); } pDith += 256; } // end dithermask entry sum += step; } // end gray level pDithBand += ditherLUTBandStride; } // end band } /** * Determine whether the internal table of this <code>DitherLUT</code> is the same as that which would be generated using the supplied * parameters. * * @param dims The color cube dimensions less one. * @param mults The color cube multipliers. * @param maskData The dither mask data scaled to byte range. * * @return Value indicating equivalence of dither LUTs. */ public boolean equals(int[] dims, int[] mults, byte[][] maskData) { // Check dimensions. if (dims.length != dimsCache.length) { return false; } for (int i = 0; i < dims.length; i++) { if (dims[i] != dimsCache[i]) return false; } // Check multipliers. if (mults.length != multsCache.length) { return false; } for (int i = 0; i < mults.length; i++) { if (mults[i] != multsCache[i]) return false; } // Check dither mask. if (maskData.length != maskDataByte.length) { return false; } for (int i = 0; i < maskData.length; i++) { if (maskData[i].length != maskDataCache[i].length) return false; byte[] refData = maskDataCache[i]; byte[] data = maskData[i]; for (int j = 0; j < maskData[i].length; j++) { if (data[j] != refData[j]) return false; } } return true; } } // End inner class DitherLUT. /** * Initialize data type-dependent fields including the dither mask data arrays and, for optimized byte cases, the dither lookup table object. * * @param dataType The data type as defined in <code>DataBuffer</code>. * @param ditherMask The dither mask represented as an array of <code>KernelJAI</code> objects. */ private void initializeDitherData(int dataType, KernelJAI[] ditherMask) { switch (dataType) { case DataBuffer.TYPE_BYTE: { maskDataByte = new byte[ditherMask.length][]; for (int i = 0; i < maskDataByte.length; i++) { float[] maskData = ditherMask[i].getKernelData(); maskDataByte[i] = new byte[maskData.length]; for (int j = 0; j < maskData.length; j++) { maskDataByte[i][j] = (byte) ((int) (maskData[j] * 255.0F) & 0xff); } } initializeDitherLUT(); } break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: { int scaleFactor = (int) Short.MAX_VALUE - (int) Short.MIN_VALUE; maskDataInt = new int[ditherMask.length][]; for (int i = 0; i < maskDataInt.length; i++) { float[] maskData = ditherMask[i].getKernelData(); maskDataInt[i] = new int[maskData.length]; for (int j = 0; j < maskData.length; j++) { maskDataInt[i][j] = (int) (maskData[j] * scaleFactor); } } } break; case DataBuffer.TYPE_INT: { long scaleFactor = (long) Integer.MAX_VALUE - (long) Integer.MIN_VALUE; maskDataLong = new long[ditherMask.length][]; for (int i = 0; i < maskDataLong.length; i++) { float[] maskData = ditherMask[i].getKernelData(); maskDataLong[i] = new long[maskData.length]; for (int j = 0; j < maskData.length; j++) { maskDataLong[i][j] = (long) (maskData[j] * scaleFactor); } } } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: { maskDataFloat = new float[ditherMask.length][]; for (int i = 0; i < maskDataFloat.length; i++) { maskDataFloat[i] = ditherMask[i].getKernelData(); } } break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } } /** * For byte data only, initialize the dither lookup table if it is small enough and set the type of ordered dither implementation to use. */ private synchronized void initializeDitherLUT() { // Check whether a DitherLUT may be used. if (numBands * maskHeight * maskWidth * 256 > DITHER_LUT_LENGTH_MAX) { odType = TYPE_OD_GENERAL; // NB: This is superfluous. return; } // If execution has proceeded to this point then this is one of the // optimized cases so set the type flag accordingly. odType = numBands == 3 ? TYPE_OD_BYTE_LUT_3BAND : TYPE_OD_BYTE_LUT_NBAND; // Check whether an equivalent DitherLUT object already exists. int index = 0; while (index < ditherLUTCache.size()) { SoftReference lutRef = (SoftReference) ditherLUTCache.get(index); DitherLUT lut = (DitherLUT) lutRef.get(); if (lut == null) { // The reference has been cleared: remove the Vector element // but do not increment the loop index. ditherLUTCache.remove(index); } else { if (lut.equals(dims, mults, maskDataByte)) { // Found an equivalent DitherLUT so use it and exit loop. odLUT = lut; break; } // Move on to the next Vector element. index++; } } // Create a new DitherLUT if an equivalent one was not found. if (odLUT == null) { odLUT = new DitherLUT(dims, mults, maskDataByte); // Cache a reference to the DitherLUT if there is room. if (ditherLUTCache.size() < DITHER_LUT_CACHE_LENGTH_MAX) { ditherLUTCache.add(new SoftReference(odLUT)); } } } /** * Computes a tile of the dithered destination image. * * @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) { // ROI check 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) { ImageUtil.fillBackground(dest, destRect, new double[] { destNoData }); return; } // Set format tags RasterFormatTag[] formatTags = null; if (ImageUtil.isBinary(getSampleModel()) && !ImageUtil.isBinary(getSourceImage(0).getSampleModel())) { // XXX Workaround for bug 4521097. This branch of the if-block // should be deleted once bug 4668327 is fixed. RenderedImage[] sourceArray = new RenderedImage[] { getSourceImage(0) }; RasterFormatTag[] sourceTags = RasterAccessor.findCompatibleTags(sourceArray, sourceArray[0]); RasterFormatTag[] destTags = RasterAccessor.findCompatibleTags(sourceArray, this); formatTags = new RasterFormatTag[] { sourceTags[0], destTags[1] }; } else { // Retrieve format tags. formatTags = getFormatTags(); } RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (src.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; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage1")); } dst.copyDataToRaster(); } /** * Computes a <code>Rectangle</code> of data for byte imagery. * * @param roiContainsTile * @param roiIter */ private void computeRectByte(RasterAccessor src, RasterAccessor dst, RandomIter roiIter, boolean roiContainsTile) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); byte[][] sData = src.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); byte[] dData = dst.getByteDataArray(0); int x0 = dst.getX(); int xMod = x0 % maskWidth; int y0 = dst.getY(); switch (odType) { case TYPE_OD_BYTE_LUT_3BAND: case TYPE_OD_BYTE_LUT_NBAND: // Optimized cases int[] srcLineOffsets = (int[]) sBandOffsets.clone(); int[] srcPixelOffsets = (int[]) srcLineOffsets.clone(); int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; if (odType == TYPE_OD_BYTE_LUT_3BAND) { computeLineByteLUT3(sData, srcPixelOffsets, sPixelStride, dData, dLineOffset, dPixelStride, dwidth, xMod, yMod, x0, y0 + h, roiIter, roiContainsTile); } else { computeLineByteLUTN(sData, srcPixelOffsets, sPixelStride, dData, dLineOffset, dPixelStride, dwidth, xMod, yMod, x0, y0 + h, roiIter, roiContainsTile); } for (int i = 0; i < sbands; i++) { srcLineOffsets[i] += sLineStride; srcPixelOffsets[i] = srcLineOffsets[i]; } dLineOffset += dLineStride; } break; case TYPE_OD_GENERAL: default: computeRectByteGeneral(sData, sBandOffsets, sLineStride, sPixelStride, dData, dBandOffset, dLineStride, dPixelStride, dwidth, dheight, xMod, x0, y0, roiIter, roiContainsTile); } } /** * Dithers a line of 3-band byte data using a DitherLUT. * * @param y0 * @param x0 * @param h * @param roiContainsTile * @param roiIter */ private void computeLineByteLUT3(byte[][] sData, int[] sPixelOffsets, int sPixelStride, byte[] dData, int dPixelOffset, int dPixelStride, int dwidth, int xMod, int yMod, int x0, int y, RandomIter roiIter, boolean roiContainsTile) { int ditherLUTBandStride = odLUT.ditherLUTBandStride; int ditherLUTRowStride = odLUT.ditherLUTRowStride; int ditherLUTColStride = odLUT.ditherLUTColStride; byte[] ditherLUT = odLUT.ditherLUT; int base = adjustedOffset; int dlut0 = yMod * ditherLUTRowStride; int dlut1 = dlut0 + ditherLUTBandStride; int dlut2 = dlut1 + ditherLUTBandStride; int dlutLimit = dlut0 + ditherLUTRowStride; int xDelta = xMod * ditherLUTColStride; int pDtab0 = dlut0 + xDelta; int pDtab1 = dlut1 + xDelta; int pDtab2 = dlut2 + xDelta; byte[] sData0 = sData[0]; byte[] sData1 = sData[1]; byte[] sData2 = sData[2]; if (caseA || (caseB && roiContainsTile)) { for (int count = dwidth; count > 0; count--) { int idx = (ditherLUT[pDtab0 + (sData0[sPixelOffsets[0]] & 0xff)] & 0xff) + (ditherLUT[pDtab1 + (sData1[sPixelOffsets[1]] & 0xff)] & 0xff) + (ditherLUT[pDtab2 + (sData2[sPixelOffsets[2]] & 0xff)] & 0xff); dData[dPixelOffset] = (byte) ((idx + base) & 0xff); sPixelOffsets[0] += sPixelStride; sPixelOffsets[1] += sPixelStride; sPixelOffsets[2] += sPixelStride; dPixelOffset += dPixelStride; pDtab0 += ditherLUTColStride; if (pDtab0 >= dlutLimit) { pDtab0 = dlut0; pDtab1 = dlut1; pDtab2 = dlut2; } else { pDtab1 += ditherLUTColStride; pDtab2 += ditherLUTColStride; } } } else if (caseB) { for (int count = dwidth; count > 0; count--) { int x = x0 + dwidth - count; // ROI check if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { int idx = (ditherLUT[pDtab0 + (sData0[sPixelOffsets[0]] & 0xff)] & 0xff) + (ditherLUT[pDtab1 + (sData1[sPixelOffsets[1]] & 0xff)] & 0xff) + (ditherLUT[pDtab2 + (sData2[sPixelOffsets[2]] & 0xff)] & 0xff); dData[dPixelOffset] = (byte) ((idx + base) & 0xff); } else { dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } sPixelOffsets[0] += sPixelStride; sPixelOffsets[1] += sPixelStride; sPixelOffsets[2] += sPixelStride; dPixelOffset += dPixelStride; pDtab0 += ditherLUTColStride; if (pDtab0 >= dlutLimit) { pDtab0 = dlut0; pDtab1 = dlut1; pDtab2 = dlut2; } else { pDtab1 += ditherLUTColStride; pDtab2 += ditherLUTColStride; } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int count = dwidth; count > 0; count--) { int src0 = sData0[sPixelOffsets[0]] & 0xff; int src1 = sData1[sPixelOffsets[1]] & 0xff; int src2 = sData2[sPixelOffsets[2]] & 0xff; // NoData check boolean valid = lut[src0] && lut[src1] && lut[src2]; if (valid) { int idx = (ditherLUT[pDtab0 + (src0)] & 0xff) + (ditherLUT[pDtab1 + (src1)] & 0xff) + (ditherLUT[pDtab2 + (src2)] & 0xff); dData[dPixelOffset] = (byte) ((idx + base) & 0xff); } else { dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } sPixelOffsets[0] += sPixelStride; sPixelOffsets[1] += sPixelStride; sPixelOffsets[2] += sPixelStride; dPixelOffset += dPixelStride; pDtab0 += ditherLUTColStride; if (pDtab0 >= dlutLimit) { pDtab0 = dlut0; pDtab1 = dlut1; pDtab2 = dlut2; } else { pDtab1 += ditherLUTColStride; pDtab2 += ditherLUTColStride; } } } else { for (int count = dwidth; count > 0; count--) { int x = x0 + dwidth - count; int src0 = sData0[sPixelOffsets[0]] & 0xff; int src1 = sData1[sPixelOffsets[1]] & 0xff; int src2 = sData2[sPixelOffsets[2]] & 0xff; // NoData check boolean valid = lut[src0] && lut[src1] && lut[src2]; // ROI check if (valid && (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0)) { int idx = (ditherLUT[pDtab0 + (src0)] & 0xff) + (ditherLUT[pDtab1 + (src1)] & 0xff) + (ditherLUT[pDtab2 + (src2)] & 0xff); dData[dPixelOffset] = (byte) ((idx + base) & 0xff); } else { dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } sPixelOffsets[0] += sPixelStride; sPixelOffsets[1] += sPixelStride; sPixelOffsets[2] += sPixelStride; dPixelOffset += dPixelStride; pDtab0 += ditherLUTColStride; if (pDtab0 >= dlutLimit) { pDtab0 = dlut0; pDtab1 = dlut1; pDtab2 = dlut2; } else { pDtab1 += ditherLUTColStride; pDtab2 += ditherLUTColStride; } } } } /** * Dithers a line of N-band byte data using a DitherLUT. * * @param y0 * @param x0 * @param h * @param roiContainsTile * @param roiIter */ private void computeLineByteLUTN(byte[][] sData, int[] sPixelOffsets, int sPixelStride, byte[] dData, int dPixelOffset, int dPixelStride, int dwidth, int xMod, int yMod, int x0, int y, RandomIter roiIter, boolean roiContainsTile) { int ditherLUTBandStride = odLUT.ditherLUTBandStride; int ditherLUTRowStride = odLUT.ditherLUTRowStride; int ditherLUTColStride = odLUT.ditherLUTColStride; byte[] ditherLUT = odLUT.ditherLUT; int base = adjustedOffset; int dlutRow = yMod * ditherLUTRowStride; int dlutCol = dlutRow + xMod * ditherLUTColStride; int dlutLimit = dlutRow + ditherLUTRowStride; if (caseA || (caseB && roiContainsTile)) { for (int count = dwidth; count > 0; count--) { int dlutBand = dlutCol; int idx = base; for (int i = 0; i < numBands; i++) { idx += (ditherLUT[dlutBand + (sData[i][sPixelOffsets[i]] & 0xff)] & 0xff); dlutBand += ditherLUTBandStride; sPixelOffsets[i] += sPixelStride; } dData[dPixelOffset] = (byte) (idx & 0xff); dPixelOffset += dPixelStride; dlutCol += ditherLUTColStride; if (dlutCol >= dlutLimit) { dlutCol = dlutRow; } } } else if (caseB) { for (int count = dwidth; count > 0; count--) { int x = x0 + dwidth - count; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { int dlutBand = dlutCol; int idx = base; for (int i = 0; i < numBands; i++) { idx += (ditherLUT[dlutBand + (sData[i][sPixelOffsets[i]] & 0xff)] & 0xff); dlutBand += ditherLUTBandStride; sPixelOffsets[i] += sPixelStride; } dData[dPixelOffset] = (byte) (idx & 0xff); } else { for (int i = 0; i < numBands; i++) { sPixelOffsets[i] += sPixelStride; } dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } dPixelOffset += dPixelStride; dlutCol += ditherLUTColStride; if (dlutCol >= dlutLimit) { dlutCol = dlutRow; } } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int count = dwidth; count > 0; count--) { int dlutBand = dlutCol; int idx = base; boolean valid = true; for (int i = 0; i < numBands; i++) { int b = sData[i][sPixelOffsets[i]] & 0xff; valid &= lut[b]; idx += (ditherLUT[dlutBand + b] & 0xff); dlutBand += ditherLUTBandStride; sPixelOffsets[i] += sPixelStride; } if (valid) { dData[dPixelOffset] = (byte) (idx & 0xff); } else { dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } dPixelOffset += dPixelStride; dlutCol += ditherLUTColStride; if (dlutCol >= dlutLimit) { dlutCol = dlutRow; } } } else { for (int count = dwidth; count > 0; count--) { int x = x0 + dwidth - count; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { int dlutBand = dlutCol; int idx = base; boolean valid = true; for (int i = 0; i < numBands; i++) { int b = sData[i][sPixelOffsets[i]] & 0xff; valid &= lut[b]; idx += (ditherLUT[dlutBand + b] & 0xff); dlutBand += ditherLUTBandStride; sPixelOffsets[i] += sPixelStride; } if (valid) { dData[dPixelOffset] = (byte) (idx & 0xff); } else { dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } } else { for (int i = 0; i < numBands; i++) { sPixelOffsets[i] += sPixelStride; } dData[dPixelOffset] = (byte) ((int) destNoData & 0xff); } dPixelOffset += dPixelStride; dlutCol += ditherLUTColStride; if (dlutCol >= dlutLimit) { dlutCol = dlutRow; } } } } /** * Computes a <code>Rectangle</code> of data for byte imagery using the general, unoptimized algorithm. * * @param y0 * @param roiContainsTile * @param roiIter */ private void computeRectByteGeneral(byte[][] sData, int[] sBandOffsets, int sLineStride, int sPixelStride, byte[] dData, int dBandOffset, int dLineStride, int dPixelStride, int dwidth, int dheight, int xMod, int x0, int y0, RandomIter roiIter, boolean roiContainsTile) { if (adjustedOffset > 0) { Arrays.fill(dData, (byte) (adjustedOffset & 0xff)); } int sbands = sBandOffsets.length; int sLineOffset = 0; int dLineOffset = 0; if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands; b++) { int tmp = (sData[b][sPixelOffset + sBandOffsets[b]] & 0xff) * dims[b]; int frac = (int) (tmp & 0xff); tmp >>= 8; if (frac > (int) (maskDataByte[b][maskIndex] & 0xff)) { tmp++; } // Accumulate the value into the destination data array. int result = (dData[bDIndex] & 0xff) + tmp * mults[b]; dData[bDIndex] = (byte) (result & 0xff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseB) { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { for (int b = 0; b < sbands; b++) { int tmp = (sData[b][sPixelOffset + sBandOffsets[b]] & 0xff) * dims[b]; int frac = (int) (tmp & 0xff); tmp >>= 8; if (frac > (int) (maskDataByte[b][maskIndex] & 0xff)) { tmp++; } // Accumulate the value into the destination data array. int result = (dData[bDIndex] & 0xff) + tmp * mults[b]; dData[bDIndex] = (byte) (result & 0xff); } } else { dData[bDIndex] = (byte) ((int) destNoData & 0xff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { boolean valid = true; int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands && valid; b++) { int value = sData[b][sPixelOffset + sBandOffsets[b]] & 0xff; valid &= lut[value]; int tmp = (value) * dims[b]; int frac = (int) (tmp & 0xff); tmp >>= 8; if (frac > (int) (maskDataByte[b][maskIndex] & 0xff)) { tmp++; } // Accumulate the value into the destination data array. int result = (dData[bDIndex] & 0xff) + tmp * mults[b]; dData[bDIndex] = (byte) (result & 0xff); } if (!valid) { dData[bDIndex] = (byte) ((int) destNoData & 0xff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { boolean valid = true; for (int b = 0; b < sbands && valid; b++) { int value = sData[b][sPixelOffset + sBandOffsets[b]] & 0xff; valid &= lut[value]; int tmp = (value) * dims[b]; int frac = (int) (tmp & 0xff); tmp >>= 8; if (frac > (int) (maskDataByte[b][maskIndex] & 0xff)) { tmp++; } // Accumulate the value into the destination data array. int result = (dData[bDIndex] & 0xff) + tmp * mults[b]; dData[bDIndex] = (byte) (result & 0xff); } if (!valid) { dData[bDIndex] = (byte) ((int) destNoData & 0xff); } } else { dData[bDIndex] = (byte) ((int) destNoData & 0xff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } if (adjustedOffset < 0) { // Shift the result by the adjusted offset of the color map. int length = dData.length; for (int i = 0; i < length; i++) { dData[i] = (byte) ((dData[i] & 0xff) + adjustedOffset); } } } /** * Computes a <code>Rectangle</code> of data for signed short imagery. * * @param roiContainsTile * @param roiIter */ private void computeRectShort(RasterAccessor src, RasterAccessor dst, RandomIter roiIter, boolean roiContainsTile) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); short[] dData = dst.getShortDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if (adjustedOffset != 0) { Arrays.fill(dData, (short) (adjustedOffset & 0xffff)); } int x0 = dst.getX(); int xMod = x0 % maskWidth; int y0 = dst.getY(); int sLineOffset = 0; int dLineOffset = 0; if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands; b++) { int tmp = (sData[b][sPixelOffset + sBandOffsets[b]] - Short.MIN_VALUE) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseB) { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { for (int b = 0; b < sbands; b++) { int tmp = (sData[b][sPixelOffset + sBandOffsets[b]] - Short.MIN_VALUE) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } } else { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { boolean valid = true; int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands && valid; b++) { short value = sData[b][sPixelOffset + sBandOffsets[b]]; valid &= !nodata.contains(value); int tmp = (value - Short.MIN_VALUE) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } if (!valid) { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { boolean valid = true; for (int b = 0; b < sbands && valid; b++) { short value = sData[b][sPixelOffset + sBandOffsets[b]]; valid &= !nodata.contains(value); int tmp = (value - Short.MIN_VALUE) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } if (!valid) { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } } else { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } } /** * Computes a <code>Rectangle</code> of data for unsigned short imagery. * * @param roiContainsTile * @param roiIter */ private void computeRectUShort(RasterAccessor src, RasterAccessor dst, RandomIter roiIter, boolean roiContainsTile) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); short[] dData = dst.getShortDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if (adjustedOffset != 0) { Arrays.fill(dData, (short) (adjustedOffset & 0xffff)); } int x0 = dst.getX(); int xMod = x0 % maskWidth; int y0 = dst.getY(); int sLineOffset = 0; int dLineOffset = 0; if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands; b++) { int tmp = (sData[b][sPixelOffset + sBandOffsets[b]] & 0xffff) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseB) { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { for (int b = 0; b < sbands; b++) { int tmp = (sData[b][sPixelOffset + sBandOffsets[b]] & 0xffff) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } } else { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { boolean valid = true; int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands && valid; b++) { short value = sData[b][sPixelOffset + sBandOffsets[b]]; valid &= !nodata.contains(value); int tmp = (value & 0xffff) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } if (!valid) { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { boolean valid = true; for (int b = 0; b < sbands && valid; b++) { short value = sData[b][sPixelOffset + sBandOffsets[b]]; valid &= !nodata.contains(value); int tmp = (value & 0xffff) * dims[b]; int frac = (int) (tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int) (dData[bDIndex] & 0xffff) + (tmp >> 16) * mults[b]; if (frac > maskDataInt[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = (short) (result & 0xffff); } if (!valid) { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } } else { dData[bDIndex] = (short) ((int) destNoData & 0xffff); } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } } /** * Computes a <code>Rectangle</code> of data for integer imagery. * * @param roiContainsTile * @param roiIter */ private void computeRectInt(RasterAccessor src, RasterAccessor dst, RandomIter roiIter, boolean roiContainsTile) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); int[][] sData = src.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); int[] dData = dst.getIntDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if (adjustedOffset != 0) { Arrays.fill(dData, adjustedOffset); } int x0 = dst.getX(); int xMod = x0 % maskWidth; int y0 = dst.getY(); int sLineOffset = 0; int dLineOffset = 0; if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands; b++) { long tmp = ((long) sData[b][sPixelOffset + sBandOffsets[b]] - (long) Integer.MIN_VALUE) * dims[b]; long frac = (long) (tmp & 0xffffffff); // Accumulate the value into the destination data array. int result = dData[bDIndex] + ((int) (tmp >> 32)) * mults[b]; if (frac > maskDataLong[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseB) { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { for (int b = 0; b < sbands; b++) { long tmp = ((long) sData[b][sPixelOffset + sBandOffsets[b]] - (long) Integer.MIN_VALUE) * dims[b]; long frac = (long) (tmp & 0xffffffff); // Accumulate the value into the destination data array. int result = dData[bDIndex] + ((int) (tmp >> 32)) * mults[b]; if (frac > maskDataLong[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } } else { dData[bDIndex] = (int) destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { boolean valid = true; int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands && valid; b++) { int value = sData[b][sPixelOffset + sBandOffsets[b]]; valid &= !nodata.contains(value); long tmp = ((long) value - (long) Integer.MIN_VALUE) * dims[b]; long frac = (long) (tmp & 0xffffffff); // Accumulate the value into the destination data array. int result = dData[bDIndex] + ((int) (tmp >> 32)) * mults[b]; if (frac > maskDataLong[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } if (!valid) { dData[bDIndex] = (int) destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { boolean valid = true; for (int b = 0; b < sbands && valid; b++) { int value = sData[b][sPixelOffset + sBandOffsets[b]]; valid &= !nodata.contains(value); long tmp = ((long) value - (long) Integer.MIN_VALUE) * dims[b]; long frac = (long) (tmp & 0xffffffff); // Accumulate the value into the destination data array. int result = dData[bDIndex] + ((int) (tmp >> 32)) * mults[b]; if (frac > maskDataLong[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } if (!valid) { dData[bDIndex] = (int) destNoData; } } else { dData[bDIndex] = (int) destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } } /** * Computes a <code>Rectangle</code> of data for float imagery. * * @param roiContainsTile * @param roiIter */ private void computeRectFloat(RasterAccessor src, RasterAccessor dst, RandomIter roiIter, boolean roiContainsTile) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); float[][] sData = src.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); float[] dData = dst.getFloatDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if (adjustedOffset != 0) { Arrays.fill(dData, (float) adjustedOffset); } int x0 = dst.getX(); int xMod = x0 % maskWidth; int y0 = dst.getY(); int sLineOffset = 0; int dLineOffset = 0; if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; int tmp = (int) (sData[b][bIndex] * dims[b]); float frac = sData[b][bIndex] * dims[b] - tmp; // Accumulate the value into the destination data array. float result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseB) { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { for (int b = 0; b < sbands; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; int tmp = (int) (sData[b][bIndex] * dims[b]); float frac = sData[b][bIndex] * dims[b] - tmp; // Accumulate the value into the destination data array. float result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } } else { dData[bDIndex] = (float) destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { boolean valid = true; int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands && valid; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; float value = sData[b][bIndex]; valid &= !nodata.contains(value); int tmp = (int) (value * dims[b]); float frac = value * dims[b] - tmp; // Accumulate the value into the destination data array. float result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } if (!valid) { dData[bDIndex] = (float) destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { boolean valid = true; for (int b = 0; b < sbands && valid; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; float value = sData[b][bIndex]; valid &= !nodata.contains(value); int tmp = (int) (value * dims[b]); float frac = value * dims[b] - tmp; // Accumulate the value into the destination data array. float result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } if (!valid) { dData[bDIndex] = (float) destNoData; } } else { dData[bDIndex] = (float) destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } } /** * Computes a <code>Rectangle</code> of data for double imagery. * * @param roiContainsTile * @param roiIter */ private void computeRectDouble(RasterAccessor src, RasterAccessor dst, RandomIter roiIter, boolean roiContainsTile) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); double[][] sData = src.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); double[] dData = dst.getDoubleDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if (adjustedOffset != 0) { Arrays.fill(dData, (double) adjustedOffset); } int x0 = dst.getX(); int xMod = x0 % maskWidth; int y0 = dst.getY(); int sLineOffset = 0; int dLineOffset = 0; if (caseA || (caseB && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; int tmp = (int) (sData[b][bIndex] * dims[b]); float frac = (float) (sData[b][bIndex] * dims[b] - tmp); // Accumulate the value into the destination data array. double result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseB) { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { for (int b = 0; b < sbands; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; int tmp = (int) (sData[b][bIndex] * dims[b]); float frac = (float) (sData[b][bIndex] * dims[b] - tmp); // Accumulate the value into the destination data array. double result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } } else { dData[bDIndex] = destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else if (caseC || (hasROI && hasNoData && roiContainsTile)) { for (int h = 0; h < dheight; h++) { int yMod = (y0 + h) % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { boolean valid = true; int bDIndex = dPixelOffset + dBandOffset; for (int b = 0; b < sbands && valid; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; double value = sData[b][bIndex]; valid &= !nodata.contains(value); int tmp = (int) (value * dims[b]); float frac = (float) (value * dims[b] - tmp); // Accumulate the value into the destination data array. double result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } if (!valid) { dData[bDIndex] = destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } else { for (int h = 0; h < dheight; h++) { int y = y0 + h; int yMod = y % maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod * maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int x = x0 + w; // ROI Check int bDIndex = dPixelOffset + dBandOffset; if (roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0) { boolean valid = true; for (int b = 0; b < sbands && valid; b++) { int bIndex = sPixelOffset + sBandOffsets[b]; double value = sData[b][bIndex]; valid &= !nodata.contains(value); int tmp = (int) (value * dims[b]); float frac = (float) (value * dims[b] - tmp); // Accumulate the value into the destination data array. double result = dData[bDIndex] + tmp * mults[b]; if (frac > maskDataFloat[b][maskIndex]) { result += mults[b]; } dData[bDIndex] = result; } if (!valid) { dData[bDIndex] = destNoData; } } else { dData[bDIndex] = destNoData; } sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if (++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } } /** * 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; } }