/* 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.interpolators;
import it.geosolutions.jaiext.range.Range;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
import javax.media.jai.Interpolation;
import javax.media.jai.RasterAccessor;
import javax.media.jai.iterator.RandomIter;
public class InterpolationNearest extends Interpolation implements InterpolationNoData {
/** serialVersionUID */
private static final long serialVersionUID = -6994369085300227735L;
/** Boolean for checking if the ROI Accessor must be used by the interpolator */
private boolean useROIAccessor;
/** Range of NO DATA values to be checked */
private Range noDataRange;
/**
* Destination NO DATA value used when the image pixel is outside of the ROI or is contained in the NO DATA range
* */
private double destinationNoData;
/** ROI bounds used for checking the position of the pixel */
private Rectangle roiBounds;
/** Image data Type */
private int dataType;
/** This value is the destination NO DATA values for binary images */
private int black;
/** Boolean used for indicating that the No Data Range is not degenarated(useful only for NaN check inside Float or Double Range) */
private boolean isNotPointRange;
// Method overriding. Performs the default nearest-neighbor interpolation without NO DATA or ROI control.
@Override
public int interpolateH(int[] samples, int arg1) {
return samples[0];
}
@Override
public float interpolateH(float[] samples, float arg1) {
return samples[0];
}
@Override
public double interpolateH(double[] samples, float arg1) {
return samples[0];
}
/**
* Simple interpolator object used for Nearest-Neighbor interpolation. On construction it is possible to set a range for no data values that will
* be considered in the interpolation method.
*/
public InterpolationNearest(Range noDataRange, boolean useROIAccessor,
double destinationNoData, int dataType) {
super(1, 1, 0, 0, 0, 0, 0, 0);
if (noDataRange != null) {
this.noDataRange = noDataRange;
this.isNotPointRange = !noDataRange.isPoint();
}
this.useROIAccessor = useROIAccessor;
this.destinationNoData = destinationNoData;
black = ((int) destinationNoData) & 1;
this.dataType = dataType;
}
public void setROIBounds(Rectangle roiBounds) {
this.roiBounds = roiBounds;
}
public double getDestinationNoData() {
return destinationNoData;
}
public void setDestinationNoData(double destinationNoData) {
this.destinationNoData = destinationNoData;
}
public boolean getUseROIAccessor() {
return useROIAccessor;
}
public void setUseROIAccessor(boolean useROIAccessor) {
this.useROIAccessor = useROIAccessor;
}
public Range getNoDataRange() {
return noDataRange;
}
public void setNoDataRange(Range noDataRange) {
if (noDataRange != null) {
this.noDataRange = noDataRange;
this.isNotPointRange = !noDataRange.isPoint();
}
}
public int getDataType() {
return dataType;
}
// method for calculating the nearest-neighbor interpolation (no Binary data).
public Number interpolate(RasterAccessor src, int bandIndex, int dnumband, int posx, int posy,
Integer yROIValue, RasterAccessor roiAccessor, RandomIter roiIter, boolean setNoData) {
// src data and destination data
Number destData = null;
// the destination data is set equal to the source data but could change if the ROI is present. If
// it is no data, destination no data value is returned.
switch (dataType) {
case DataBuffer.TYPE_BYTE:
byte srcDataByte = src.getByteDataArray(bandIndex)[posx + posy];
if ((noDataRange != null && (noDataRange).contains(srcDataByte)) || setNoData) {
return destinationNoData;
}
destData = srcDataByte;
break;
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
short srcDataShort = src.getShortDataArray(bandIndex)[posx + posy];
if ((noDataRange != null && (noDataRange).contains(srcDataShort)) || setNoData) {
return destinationNoData;
}
destData = srcDataShort;
break;
case DataBuffer.TYPE_INT:
int srcDataInt = src.getIntDataArray(bandIndex)[posx + posy];
if ((noDataRange != null && (noDataRange).contains(srcDataInt)) || setNoData) {
return destinationNoData;
}
destData = srcDataInt;
break;
case DataBuffer.TYPE_FLOAT:
float srcDataFloat = src.getFloatDataArray(bandIndex)[posx + posy];
if ((noDataRange != null && (noDataRange).contains(srcDataFloat)) || (isNotPointRange && Float.isNaN(srcDataFloat)) || setNoData) {
return destinationNoData;
}
destData = srcDataFloat;
break;
case DataBuffer.TYPE_DOUBLE:
double srcDataDouble = src.getDoubleDataArray(bandIndex)[posx + posy];
if ((noDataRange != null && (noDataRange).contains(srcDataDouble)) || (isNotPointRange && Double.isNaN(srcDataDouble)) || setNoData) {
return destinationNoData;
}
destData = srcDataDouble;
break;
default:
break;
}
// If ROI accessor is used,source pixel is tested if is contained inside the ROI.
if (useROIAccessor) {
if (roiAccessor == null || yROIValue == null) {
throw new IllegalArgumentException("ROI Accessor or ROI y value not found");
}
// Operations for taking the correct index pixel in roi array.
int roiIndex = posx / dnumband + yROIValue;
byte[] roiDataArray = roiAccessor.getByteDataArray(0);
if (roiIndex < roiDataArray.length) {
// if the ROI pixel value is 0 the value returned is NO DATA
switch (dataType) {
case DataBuffer.TYPE_BYTE:
byte valueROIByte = (byte) (roiDataArray[roiIndex] & 0xff);
if (valueROIByte != 0) {
return destData;
} else {
return destinationNoData;
}
case DataBuffer.TYPE_USHORT:
short valueROIUShort = (short) (roiDataArray[roiIndex] & 0xffff);
if (valueROIUShort != 0) {
return destData;
} else {
return destinationNoData;
}
case DataBuffer.TYPE_SHORT:
case DataBuffer.TYPE_INT:
case DataBuffer.TYPE_FLOAT:
case DataBuffer.TYPE_DOUBLE:
double valueROI = roiDataArray[roiIndex];
if (valueROI != 0) {
return destData;
} else {
return destinationNoData;
}
default:
break;
}
} else {
return destinationNoData;
}
// If there is no ROI accessor but a ROI object is present, a test similar to that above is performed.
} else if (roiBounds != null) {
// Pixel position
int x0 = src.getX() + posx / src.getPixelStride();
int y0 = src.getY() + (posy - src.getBandOffset(bandIndex)) / src.getScanlineStride();
// check if the roi pixel is inside the roi bounds
if (!roiBounds.contains(x0, y0)) {
return destinationNoData;
} else {
// if it is inside ROI bounds and the associated roi pixel is 1, the src pixel is returned.
// Otherwise, destination NO DATA is returned.
int wx = 0;
switch (dataType) {
case DataBuffer.TYPE_BYTE:
wx = roiIter.getSample(x0, y0, 0) & 0xFF;
break;
case DataBuffer.TYPE_USHORT:
wx = roiIter.getSample(x0, y0, 0) & 0xFFFF;
break;
case DataBuffer.TYPE_SHORT:
case DataBuffer.TYPE_INT:
case DataBuffer.TYPE_FLOAT:
case DataBuffer.TYPE_DOUBLE:
wx = roiIter.getSample(x0, y0, 0);
break;
default:
break;
}
final boolean insideROI = wx == 1;
if (insideROI) {
return destData;
} else {
return destinationNoData;
}
}
}
return destData;
}
// Interpolation operation for Binary images (coordinates are useful only if ROI is present)
public int interpolateBinary(int xNextBitNo, Number[] sourceData, int sourceYOffset,
int sourceScanlineStride, int[] coordinates, int[] roiDataArray, int roiYOffset,
int roiScanlineStride, RandomIter roiIter) {
// pixel initialization
int s = 0;
// Shift to the selected pixel
int sshift = 0;
// Calculates the bit number of the selected pixel's position
int sbitnum = xNextBitNo - 1;
int w00index = 0;
int w00 = 1;
// If an image ROI has been saved, this ROI is used for checking if
// all the surrounding pixel belongs to the ROI.
if (coordinates != null && roiBounds != null && !useROIAccessor) {
// Central pixel positions
int x0 = coordinates[0];
int y0 = coordinates[1];
// ROI control
if (roiBounds.contains(x0, y0)) {
w00 = roiIter.getSample(x0, y0, 0) & 0x1;
} else {
return black;
}
}
// Calculates the interpolation for every type of data that allows binary images.
switch (dataType) {
case DataBuffer.TYPE_BYTE:
// This value is used for searching the selected pixel inside the element.
sshift = 7 - (sbitnum & 7);
// Conversion from bit to Byte for searching the element in which the selected pixel is found.
int sbytenum = sbitnum >> 3;
// Searching of the 4 pixels surrounding the selected one.
s = (sourceData[sourceYOffset + sbytenum].byteValue() >> sshift) & 0x01;
if (useROIAccessor) {
int roiDataLength = roiDataArray.length;
w00index = roiYOffset + sbytenum;
w00 *= w00index < roiDataLength ? roiDataArray[w00index] >> sshift & 0x01 : 0;
}
break;
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
// Same operations like the ones above but the step is done with short and not byte
int sshortnum = sbitnum >> 4;
sshift = 15 - (sbitnum & 15);
s = (sourceData[sourceYOffset + sshortnum].shortValue() >> sshift) & 0x01;
if (useROIAccessor) {
int roiDataLength = roiDataArray.length;
w00index = roiYOffset + sshortnum;
w00 *= w00index < roiDataLength ? roiDataArray[w00index] >> sshift & 0x01 : 0;
}
break;
case DataBuffer.TYPE_INT:
// Same operations like the ones above but the step is done with integers and not short
int sintnum = sbitnum >> 5;
sshift = 31 - (sbitnum & 31);
s = (sourceData[sourceYOffset + sintnum].intValue() >> sshift) & 0x01;
if (useROIAccessor) {
int roiDataLength = roiDataArray.length;
w00index = roiYOffset + sintnum;
w00 *= w00index < roiDataLength ? roiDataArray[w00index] >> sshift & 0x01 : 0;
}
break;
default:
break;
}
if (w00 == 0) {
return black;
}
return s;
}
}