/* 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.binarize;
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.MultiPixelPackedSampleModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.util.Map;
import javax.media.jai.ImageLayout;
import javax.media.jai.PackedImageData;
import javax.media.jai.PixelAccessor;
import javax.media.jai.PlanarImage;
import javax.media.jai.PointOpImage;
import javax.media.jai.ROI;
import javax.media.jai.ROIShape;
import javax.media.jai.UnpackedImageData;
import javax.media.jai.iterator.RandomIter;
import com.sun.media.jai.util.ImageUtil;
import com.sun.media.jai.util.JDKWorkarounds;
public class BinarizeOpImage 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;
/** Boolean indicating whether Nodata check must be done or not */
private final boolean hasNoData;
/** Input NoData Range */
private Range noData;
/** Input LookupTable used for Byte data in order to increase performances on nodata check */
private boolean[] lut;
/** Boolean indicating whether ROI check must be done or not */
private final boolean hasROI;
/** ROI object used for reducing the image active area */
private ROI roi;
/** Flag indicating that No ROI nor NoData are present */
private final boolean caseA;
/** Flag indicating that only ROI is present */
private final boolean caseB;
/** Flag indicating that only NoData range is present */
private final boolean caseC;
/** Rectangle defining the bounds of the input ROI */
private final Rectangle roiBounds;
/** {@link PlanarImage} representing ROI */
private PlanarImage roiImage;
/**
* Lookup table for output bytes.
*/
private static byte[] byteTable = new byte[] { (byte) 0x80, (byte) 0x40, (byte) 0x20,
(byte) 0x10, (byte) 0x08, (byte) 0x04, (byte) 0x02, (byte) 0x01, };
/**
* bitsOn[j + (i<<3)] sets bits on from i to j
*/
private static int[] bitsUp = null;
/** The threshold. */
private double threshold;
/**
* Constructor.
*
* @param source The source image.
* @param layout The destination image layout.
* @param threshold The threshold value for binarization.
*/
public BinarizeOpImage(RenderedImage source, Map config, ImageLayout layout, double threshold,
ROI roi, Range nodata) {
super(source, layoutHelper(source, layout, config), config, true);
if (source.getSampleModel().getNumBands() != 1) {
throw new IllegalArgumentException(JaiI18N.getString("BinarizeOpImage0"));
}
this.threshold = threshold;
// Check if No Data control must be done
if (nodata != null) {
hasNoData = true;
this.noData = nodata;
} else {
hasNoData = false;
}
// Check if ROI control must be done
if (roi != null) {
hasROI = true;
// Roi object
this.roi = roi;
roiBounds = roi.getBounds();
} else {
hasROI = false;
this.roi = null;
roiBounds = null;
}
// Definition of the possible cases that can be found
// caseA = no ROI nor No Data
// caseB = ROI present but No Data not present
// caseC = No Data present but ROI not present
// Last case not defined = both ROI and No Data are present
caseA = !hasNoData && !hasROI;
caseB = !hasNoData && hasROI;
caseC = hasNoData && !hasROI;
// Getting datatype
int dataType = source.getSampleModel().getDataType();
if (dataType == DataBuffer.TYPE_BYTE) {
initBooleanTable();
}
}
// set the OpImage's SM to be MultiPixelPackedSampleModel
private static ImageLayout layoutHelper(RenderedImage source, ImageLayout il, Map config) {
ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout) il.clone();
SampleModel sm = layout.getSampleModel(source);
if (!ImageUtil.isBinary(sm)) {
sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, layout.getTileWidth(source),
layout.getTileHeight(source), 1);
layout.setSampleModel(sm);
}
ColorModel cm = layout.getColorModel(null);
if (cm == null || !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
layout.setColorModel(ImageUtil.getCompatibleColorModel(sm, config));
}
return layout;
}
/**
* Map the pixels inside a specified rectangle whose value is within a rang to a constant on a per-band basis.
*
* @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 (!hasROI || !roiDisjointTile) {
switch (sources[0].getSampleModel().getDataType()) {
case DataBuffer.TYPE_BYTE:
byteLoop(sources[0], dest, destRect, roiIter, roiContainsTile);
break;
case DataBuffer.TYPE_USHORT:
ushortLoop(sources[0], dest, destRect, roiIter, roiContainsTile);
break;
case DataBuffer.TYPE_SHORT:
shortLoop(sources[0], dest, destRect, roiIter, roiContainsTile);
break;
case DataBuffer.TYPE_INT:
intLoop(sources[0], dest, destRect, roiIter, roiContainsTile);
break;
case DataBuffer.TYPE_FLOAT:
floatLoop(sources[0], dest, destRect, roiIter, roiContainsTile);
break;
case DataBuffer.TYPE_DOUBLE:
doubleLoop(sources[0], dest, destRect, roiIter, roiContainsTile);
break;
default:
throw new RuntimeException(JaiI18N.getString("BinarizeOpImage1"));
}
}
}
private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect,
RandomIter roiIter, boolean roiContainsTile) {
if (threshold <= 0.0D && (!hasROI || (hasROI && roiContainsTile)) && !hasNoData) {
// every bit is 1
setTo1(dest, destRect);
return;
} else if (threshold > 255.0D) {
// every bit is zeros;
return;
}
// computation can be done in integer
// even though threshold is of double type
// through a lookup table for byte case
Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null);
UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_BYTE, false);
int srcOffset = srcImD.bandOffsets[0];
byte[] srcData = ((byte[][]) srcImD.data)[0];
int pixelStride = srcImD.pixelStride;
int ind0 = pid.bitOffset;
int x0 = 0;
int y0 = 0;
int srcX = source.getMinX();
int srcY = source.getMinY();
// Cycles
if (hasROI && !roiContainsTile) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
// Check on the ROI
if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) {
continue;
}
// Using LUT in order to skip continuous NoData check
if (lut[(srcData[s] & 0xFF)]) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
// Using LUT in order to skip continuous NoData check
if (lut[(srcData[s] & 0xFF)]) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
}
pa.setPackedPixels(pid);
}
private void ushortLoop(Raster source, WritableRaster dest, Rectangle destRect,
RandomIter roiIter, boolean roiContainsTile) {
if (threshold <= 0.0D && (!hasROI || (hasROI && roiContainsTile)) && !hasNoData) {
// every bit is 1
setTo1(dest, destRect);
return;
} else if (threshold > (double) (0xFFFF)) {
// every bit is zeros;
return;
}
int thresholdI = (int) (Math.ceil(threshold));
// computation can be done in integer
// even though threshold is of double type
Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null);
UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_USHORT, false);
int srcOffset = srcImD.bandOffsets[0];
short[] srcData = ((short[][]) srcImD.data)[0];
int pixelStride = srcImD.pixelStride;
int ind0 = pid.bitOffset;
int x0 = 0;
int y0 = 0;
int srcX = source.getMinX();
int srcY = source.getMinY();
// Cycles
if (caseA || (caseB && roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if ((srcData[s] & 0xFFFF) >= thresholdI) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseB) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) {
continue;
}
if ((srcData[s] & 0xFFFF) >= thresholdI) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseC || (hasNoData && hasROI & roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if (noData.contains(srcData[s])) {
continue;
}
if ((srcData[s] & 0xFFFF) >= thresholdI) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if ((!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0))
|| noData.contains(srcData[s])) {
continue;
}
if ((srcData[s] & 0xFFFF) >= thresholdI) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
}
pa.setPackedPixels(pid);
}
private void shortLoop(Raster source, WritableRaster dest, Rectangle destRect,
RandomIter roiIter, boolean roiContainsTile) {
if (threshold <= Short.MIN_VALUE && (!hasROI || (hasROI && roiContainsTile)) && !hasNoData) {
// every bit is 1
setTo1(dest, destRect);
return;
} else if (threshold > Short.MAX_VALUE) {
// every bit is zeros;
return;
}
short thresholdS = (short) (Math.ceil(threshold));
// computation can be done in integer
// even though threshold is of double type
Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null);
UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_SHORT, false);
int srcOffset = srcImD.bandOffsets[0];
short[] srcData = ((short[][]) srcImD.data)[0];
int pixelStride = srcImD.pixelStride;
int ind0 = pid.bitOffset;
int x0 = 0;
int y0 = 0;
int srcX = source.getMinX();
int srcY = source.getMinY();
// Cycles
if (caseA || (caseB && roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if ((srcData[s]) >= thresholdS) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseB) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) {
continue;
}
if ((srcData[s]) >= thresholdS) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseC || (hasNoData && hasROI & roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if (noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= thresholdS) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if ((!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0))
|| noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= thresholdS) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
}
pa.setPackedPixels(pid);
}
private void intLoop(Raster source, WritableRaster dest, Rectangle destRect,
RandomIter roiIter, boolean roiContainsTile) {
if (threshold <= Integer.MIN_VALUE && (!hasROI || (hasROI && roiContainsTile))
&& !hasNoData) {
// every bit is 1
setTo1(dest, destRect);
return;
} else if (threshold > Integer.MAX_VALUE) {
// every bit is zeros;
return;
}
// computation can be done in integer
// even though threshold is of double type
Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null);
UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_INT, false);
int srcOffset = srcImD.bandOffsets[0];
int[] srcData = ((int[][]) srcImD.data)[0];
int pixelStride = srcImD.pixelStride;
int ind0 = pid.bitOffset;
int x0 = 0;
int y0 = 0;
int srcX = source.getMinX();
int srcY = source.getMinY();
// Cycles
if (caseA || (caseB && roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseB) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseC || (hasNoData && hasROI & roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if (noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if ((!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0))
|| noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
}
pa.setPackedPixels(pid);
}
private void floatLoop(Raster source, WritableRaster dest, Rectangle destRect,
RandomIter roiIter, boolean roiContainsTile) {
// computation can be done in integer
// even though threshold is of double type
Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null);
UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_FLOAT, false);
int srcOffset = srcImD.bandOffsets[0];
float[] srcData = ((float[][]) srcImD.data)[0];
int pixelStride = srcImD.pixelStride;
int ind0 = pid.bitOffset;
int x0 = 0;
int y0 = 0;
int srcX = source.getMinX();
int srcY = source.getMinY();
// Cycles
if (caseA || (caseB && roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseB) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseC || (hasNoData && hasROI & roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if (noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if ((!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0))
|| noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
}
pa.setPackedPixels(pid);
}
private void doubleLoop(Raster source, WritableRaster dest, Rectangle destRect,
RandomIter roiIter, boolean roiContainsTile) {
// computation can be done in integer
// even though threshold is of double type
Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null);
UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_DOUBLE, false);
int srcOffset = srcImD.bandOffsets[0];
double[] srcData = ((double[][]) srcImD.data)[0];
int pixelStride = srcImD.pixelStride;
int ind0 = pid.bitOffset;
int x0 = 0;
int y0 = 0;
int srcX = source.getMinX();
int srcY = source.getMinY();
// Cycles
if (caseA || (caseB && roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseB) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if (!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0)) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else if (caseC || (hasNoData && hasROI & roiContainsTile)) {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
if (noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
} else {
for (int h = 0; h < destRect.height; h++) {
int indE = ind0 + destRect.width;
for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
x0 = srcX + b - ind0;
y0 = srcY + h;
if ((!(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0))
|| noData.contains(srcData[s])) {
continue;
}
if ((srcData[s]) >= threshold) {
pid.data[offset + (b >> 3)] |= byteTable[b % 8];
}
}
offset += pid.lineStride;
srcOffset += srcImD.lineStride;
}
}
pa.setPackedPixels(pid);
}
// set all bits in a rectangular region to be 1
// need to be sure that paddings not changing
private void setTo1(Raster dest, Rectangle destRect) {
initBitsUp();
PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null);
PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false);
int offset = pid.offset;
for (int h = 0; h < destRect.height; h++) {
int ind0 = pid.bitOffset;
int indE = ind0 + destRect.width - 1;
if (indE < 8) {
// the entire row in data[offset]
pid.data[offset] = (byte) (pid.data[offset] | bitsUp[indE]); // (0<<3) + indE
} else {
// 1st byte
pid.data[offset] = (byte) (pid.data[offset] | bitsUp[7]); // (0<<3) + 7
// middle bytes
for (int b = offset + 1; b <= offset + (indE - 7) / 8; b++) {
pid.data[b] = (byte) (0xff);
}
// last byte
int remBits = indE % 8;
if (remBits % 8 != 7) {
indE = offset + indE / 8;
pid.data[indE] = (byte) (pid.data[indE] | bitsUp[remBits]); // (0<<3)+remBits
}
}
offset += pid.lineStride;
}
pa.setPackedPixels(pid);
}
// setting bits i to j to 1;
// i <= j
private static synchronized void initBitsUp() {
if (bitsUp != null)
return;
bitsUp = new int[64];
for (int i = 0; i < 8; i++) {
for (int j = i; j < 8; j++) {
int bi = (0x00ff) >> i;
int bj = (0x00ff) << (7 - j);
bitsUp[j + (i << 3)] = bi & bj;
}
}
}
private void initBooleanTable() {
if (lut != null) {
return;
}
short thresholdI = (short) Math.ceil(threshold);
lut = new boolean[256];
// Initialize table which do a check for nodata and the threshold
for (int i = 0; i < 256; i++) {
if (hasNoData && noData.contains((byte) i)) {
lut[i] = false;
} else {
lut[i] = (i & 0xFF) >= thresholdI;
}
}
}
/**
* 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;
}
}