/* 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 jj2000.j2k.roi.ROIDeScaler;
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 InterpolationBilinear extends Interpolation implements InterpolationNoData{
/** serialVersionUID */
private static final long serialVersionUID = 5238694001611785385L;
// Method overriding. Performs the default bilinear interpolation without NO DATA or ROI control.
@Override
public int interpolateH(int[] arg0, int xfrac) {
return ((arg0[1] - arg0[0]) * xfrac + (arg0[0] << subsampleBits) + round) >> subsampleBits;
}
@Override
public float interpolateH(float[] arg0, float xfrac) {
return (arg0[1] - arg0[0]) * xfrac + arg0[0];
}
@Override
public double interpolateH(double[] arg0, float xfrac) {
return (arg0[1] - arg0[0]) * xfrac + arg0[0];
}
/** The value of 0.5 scaled by 2^subsampleBits */
private int round;
/**
* The number of bits to shift integer pixels to account for subsampleBits
*/
private int subsampleBits;
/**
* Twice the value of 'shift'. Accounts for accumulated scaling shifts in two-axis interpolation
*/
private int shift2;
/** The value of 0.5 scaled by 2^shift2 */
private int round2;
/** Range of NO DATA values to be checked */
private Range noDataRange;
/** ROI bounds used for checking the position of the pixel */
private Rectangle roiBounds;
/** Boolean for checking if the ROI Accessor must be used by the interpolator */
private boolean useROIAccessor;
/**
* 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;
/** This value is the destination NO DATA values for binary images */
private int black;
/** Image data Type */
private int dataType;
/** 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;
/**
* Default value for subsample bits
* */
public static final int DEFAULT_SUBSAMPLE_BITS = 8;
/**
* Simple interpolator object used for Bilinear interpolation. On construction it is possible to set a range for no data values that will be
* considered in the interpolation method.
*/
public InterpolationBilinear(int subsampleBits, Range noDataRange, boolean useROIAccessor,
double destinationNoData, int dataType) {
super(2, 2, 0, 1, 0, 1, subsampleBits, subsampleBits);
this.subsampleBits = subsampleBits;
round = 1 << (subsampleBits - 1);
shift2 = 2 * subsampleBits;
round2 = 1 << (shift2 - 1);
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;
}
/** This method performs a bilinear interpolation of a pixel inside a not-Binary image. */
public Number interpolate(RasterAccessor src, int bandIndex, int dnumbands, int posX, int posY,
Number[] fracValues, Integer yValueROI, RasterAccessor roi, RandomIter roiIter, boolean setNoData) {
// If the value must be set to NO DATA no other operation are needed.
if (setNoData) {
return destinationNoData;
}
// RasterAccessor data, useful for the pixel position.
int srcScanLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int srcBandOffset = src.getBandOffset(bandIndex);
// 4 surrounding pixel of the central pixel.
int posXlow = posX;
int posYlow = posY;
int posXhigh = posX + srcPixelStride;
int posYhigh = posY + srcScanLineStride;
// Initial value of the 4 pixel (different variables are used for float and double data).
int s00 = 0;
int s01 = 0;
int s10 = 0;
int s11 = 0;
float s00f = 0;
float s01f = 0;
float s10f = 0;
float s11f = 0;
double s00d = 0;
double s01d = 0;
double s10d = 0;
double s11d = 0;
// src data array initialization
byte[] srcDataByte;
short[] srcDataShort;
int[] srcDataInt;
float[] srcDataFloat;
double[] srcDataDouble;
// Fractional Value
int xfrac = 0;
int yfrac = 0;
float xfracf = 0;
float yfracf = 0;
double xfracd = 0;
double yfracd = 0;
// Get the four surrounding pixel values and the fractional value for x and y axes
switch (dataType) {
case DataBuffer.TYPE_BYTE:
srcDataByte = src.getByteDataArray(bandIndex);
s00 = srcDataByte[posXlow + posYlow] & 0xff;
s01 = srcDataByte[posXhigh + posYlow] & 0xff;
s10 = srcDataByte[posXlow + posYhigh] & 0xff;
s11 = srcDataByte[posXhigh + posYhigh] & 0xff;
xfrac = fracValues[0].intValue();
yfrac = fracValues[1].intValue();
break;
case DataBuffer.TYPE_USHORT:
srcDataShort = src.getShortDataArray(bandIndex);
s00 = srcDataShort[posXlow + posYlow] & 0xffff;
s01 = srcDataShort[posXhigh + posYlow] & 0xffff;
s10 = srcDataShort[posXlow + posYhigh] & 0xffff;
s11 = srcDataShort[posXhigh + posYhigh] & 0xffff;
xfrac = fracValues[0].intValue();
yfrac = fracValues[1].intValue();
break;
case DataBuffer.TYPE_SHORT:
srcDataShort = src.getShortDataArray(bandIndex);
s00 = srcDataShort[posXlow + posYlow];
s01 = srcDataShort[posXhigh + posYlow];
s10 = srcDataShort[posXlow + posYhigh];
s11 = srcDataShort[posXhigh + posYhigh];
xfrac = fracValues[0].intValue();
yfrac = fracValues[1].intValue();
break;
case DataBuffer.TYPE_INT:
srcDataInt = src.getIntDataArray(bandIndex);
s00 = srcDataInt[posXlow + posYlow];
s01 = srcDataInt[posXhigh + posYlow];
s10 = srcDataInt[posXlow + posYhigh];
s11 = srcDataInt[posXhigh + posYhigh];
xfrac = fracValues[0].intValue();
yfrac = fracValues[1].intValue();
break;
case DataBuffer.TYPE_FLOAT:
srcDataFloat = src.getFloatDataArray(bandIndex);
s00f = srcDataFloat[posXlow + posYlow];
s01f = srcDataFloat[posXhigh + posYlow];
s10f = srcDataFloat[posXlow + posYhigh];
s11f = srcDataFloat[posXhigh + posYhigh];
xfracf = fracValues[0].floatValue();
yfracf = fracValues[1].floatValue();
break;
case DataBuffer.TYPE_DOUBLE:
srcDataDouble = src.getDoubleDataArray(bandIndex);
s00d = srcDataDouble[posXlow + posYlow];
s01d = srcDataDouble[posXhigh + posYlow];
s10d = srcDataDouble[posXlow + posYhigh];
s11d = srcDataDouble[posXhigh + posYhigh];
xfracd = fracValues[0].doubleValue();
yfracd = fracValues[1].doubleValue();
break;
default:
break;
}
// Pixel weight initialization. This values are set to 1 for the case that this pixel are
// all inside the ROI and are not contained in the NO DATA Range.
int w00 = 1;
int w01 = 1;
int w10 = 1;
int w11 = 1;
float w00f = 1;
float w01f = 1;
float w10f = 1;
float w11f = 1;
double w00d = 1;
double w01d = 1;
double w10d = 1;
double w11d = 1;
// If ROI accessor is present, it is used for checking if any of the 4 surrounding pixel belongs to ROI
if (useROIAccessor) {
if (yValueROI == null || roi == null) {
throw new IllegalArgumentException(
"If rasterAccessor is set, ROI value must be provided");
}
// ROI scan line stride used for selecting the 4 surrounding pixels
int roiScanLineStride = roi.getScanlineStride();
int baseIndex = (posXlow / dnumbands) + (yValueROI);
int w00index = baseIndex;
int w01index = baseIndex + 1;
int w10index = baseIndex + roiScanLineStride;
int w11index = baseIndex + 1 + roiScanLineStride;
// Array length initialization
int roiDataLength = 0;
// Check if the selected index belongs to the roi data array: if it is not present, the weight is 0,
// Otherwise it takes the related value.
byte[] roiDataArrayByte = roi.getByteDataArray(0);
roiDataLength = roiDataArrayByte.length;
if (baseIndex > roiDataLength || roiDataArrayByte[w00index] == 0) {
return destinationNoData;
}
switch (dataType) {
case DataBuffer.TYPE_BYTE:
w00 = w00index < roiDataLength ? roiDataArrayByte[w00index] & 0xff : 0;
w01 = w01index < roiDataLength ? roiDataArrayByte[w01index] & 0xff : 0;
w10 = w10index < roiDataLength ? roiDataArrayByte[w10index] & 0xff : 0;
w11 = w11index < roiDataLength ? roiDataArrayByte[w11index] & 0xff : 0;
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_USHORT:
w00 = w00index < roiDataLength ? roiDataArrayByte[w00index] & 0xffff : 0;
w01 = w01index < roiDataLength ? roiDataArrayByte[w01index] & 0xffff : 0;
w10 = w10index < roiDataLength ? roiDataArrayByte[w10index] & 0xffff : 0;
w11 = w11index < roiDataLength ? roiDataArrayByte[w11index] & 0xffff : 0;
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_SHORT:
case DataBuffer.TYPE_INT:
w00 = w00index < roiDataLength ? roiDataArrayByte[w00index] : 0;
w01 = w01index < roiDataLength ? roiDataArrayByte[w01index] : 0;
w10 = w10index < roiDataLength ? roiDataArrayByte[w10index] : 0;
w11 = w11index < roiDataLength ? roiDataArrayByte[w11index] : 0;
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_FLOAT:
w00f = w00index < roiDataLength ? roiDataArrayByte[w00index] : 0;
w01f = w01index < roiDataLength ? roiDataArrayByte[w01index] : 0;
w10f = w10index < roiDataLength ? roiDataArrayByte[w10index] : 0;
w11f = w11index < roiDataLength ? roiDataArrayByte[w11index] : 0;
if (w00f == 0 && w01f == 0 && w10f == 0 && w11f == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_DOUBLE:
w00d = w00index < roiDataLength ? roiDataArrayByte[w00index] : 0;
w01d = w01index < roiDataLength ? roiDataArrayByte[w01index] : 0;
w10d = w10index < roiDataLength ? roiDataArrayByte[w10index] : 0;
w11d = w11index < roiDataLength ? roiDataArrayByte[w11index] : 0;
if (w00d == 0 && w01d == 0 && w10d == 0 && w11d == 0) {
return destinationNoData;
}
break;
default:
break;
}
// If ROI accessor is not present but an image ROI has been saved, this ROI is used for checking if
// all the surrounding pixel belongs to the ROI.
} else if (roiBounds != null) {
// Central pixel positions
int x0 = src.getX() + posXlow / srcPixelStride;
int y0 = src.getY() + (posYlow - srcBandOffset) / srcScanLineStride;
// ROI control
if (roiBounds.contains(x0, y0)) {
switch (dataType) {
case DataBuffer.TYPE_BYTE:
w00 = roiIter.getSample(x0, y0, 0) & 0xFF;
w01 = roiIter.getSample(x0 + 1, y0, 0) & 0xFF;
w10 = roiIter.getSample(x0, y0 + 1, 0) & 0xFF;
w11 = roiIter.getSample(x0 + 1, y0 + 1, 0) & 0xFF;
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_USHORT:
w00 = roiIter.getSample(x0, y0, 0) & 0xFFFF;
w01 = roiIter.getSample(x0 + 1, y0, 0) & 0xFFFF;
w10 = roiIter.getSample(x0, y0 + 1, 0) & 0xFFFF;
w11 = roiIter.getSample(x0 + 1, y0 + 1, 0) & 0xFFFF;
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_SHORT:
case DataBuffer.TYPE_INT:
w00 = roiIter.getSample(x0, y0, 0);
w01 = roiIter.getSample(x0 + 1, y0, 0);
w10 = roiIter.getSample(x0, y0 + 1, 0);
w11 = roiIter.getSample(x0 + 1, y0 + 1, 0);
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_FLOAT:
w00f = roiIter.getSample(x0, y0, 0);
w01f = roiIter.getSample(x0 + 1, y0, 0);
w10f = roiIter.getSample(x0, y0 + 1, 0);
w11f = roiIter.getSample(x0 + 1, y0 + 1, 0);
if (w00f == 0 && w01f == 0 && w10f == 0 && w11f == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_DOUBLE:
w00d = roiIter.getSample(x0, y0, 0);
w01d = roiIter.getSample(x0 + 1, y0, 0);
w10d = roiIter.getSample(x0, y0 + 1, 0);
w11d = roiIter.getSample(x0 + 1, y0 + 1, 0);
if (w00d == 0 && w01d == 0 && w10d == 0 && w11d == 0) {
return destinationNoData;
}
break;
default:
break;
}
} else {
return destinationNoData;
}
}
w00 = 1;
w01 = 1;
w10 = 1;
w11 = 1;
w00f = 1f;
w01f = 1f;
w10f = 1f;
w11f = 1f;
w00d = 1d;
w01d = 1d;
w10d = 1d;
w11d = 1d;
// No Data Control for the 4 selected pixels.If any of these 4 pixel is NO DATA,
// his related weight is set to 0, else it is leaved unchanged.
if (noDataRange != null) {
switch (dataType) {
case DataBuffer.TYPE_BYTE:
Range rangeB = (noDataRange);
if (rangeB.contains((byte) s00)) {
w00 *= 0;
}
if (rangeB.contains((byte) s01)) {
w01 *= 0;
}
if (rangeB.contains((byte) s10)) {
w10 *= 0;
}
if (rangeB.contains((byte) s11)) {
w11 *= 0;
}
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
Range rangeS = (noDataRange);
if (rangeS.contains((short) s00)) {
w00 *= 0;
}
if (rangeS.contains((short) s01)) {
w01 *= 0;
}
if (rangeS.contains((short) s10)) {
w10 *= 0;
}
if (rangeS.contains((short) s11)) {
w11 *= 0;
}
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_INT:
Range rangeI = (noDataRange);
if (rangeI.contains(s00)) {
w00 *= 0;
}
if (rangeI.contains(s01)) {
w01 *= 0;
}
if (rangeI.contains(s10)) {
w10 *= 0;
}
if (rangeI.contains(s11)) {
w11 *= 0;
}
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_FLOAT:
Range rangeF = (noDataRange);
if (rangeF.contains(s00f)|| (isNotPointRange && Float.isNaN(s00f))) {
w00f *= 0;
}
if (rangeF.contains(s01f)|| (isNotPointRange && Float.isNaN(s01f))) {
w01f *= 0;
}
if (rangeF.contains(s10f)|| (isNotPointRange && Float.isNaN(s10f))) {
w10f *= 0;
}
if (rangeF.contains(s11f)|| (isNotPointRange && Float.isNaN(s11f))) {
w11f *= 0;
}
if (w00f == 0 && w01f == 0 && w10f == 0 && w11f == 0) {
return destinationNoData;
}
break;
case DataBuffer.TYPE_DOUBLE:
Range rangeD = ( noDataRange);
// This code is used for checking if No Data value is Double.NaN,
// Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY
if (rangeD.contains(s00d)|| (isNotPointRange && Double.isNaN(s00d))) {
w00d *= 0;
}
if (rangeD.contains(s01d)|| (isNotPointRange && Double.isNaN(s01d))) {
w01d *= 0;
}
if (rangeD.contains(s10d)|| (isNotPointRange && Double.isNaN(s10d))) {
w10d *= 0;
}
if (rangeD.contains(s11d)|| (isNotPointRange && Double.isNaN(s11d))) {
w11d *= 0;
}
if (w00d == 0 && w01d == 0 && w10d == 0 && w11d == 0) {
return destinationNoData;
}
break;
default:
break;
}
}
// Bilinear Interpolation calculation.
Number s = null;
switch (dataType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
case DataBuffer.TYPE_INT:
s = computeValue(s00, s01, s10, s11, w00, w01, w10, w11, xfrac, yfrac);
break;
case DataBuffer.TYPE_FLOAT:
s = computeValueDouble(s00f, s01f, s10f, s11f, w00f, w01f, w10f, w11f, xfracf, yfracf,
dataType);
break;
case DataBuffer.TYPE_DOUBLE:
s = computeValueDouble(s00d, s01d, s10d, s11d, w00d, w01d, w10d, w11d, xfracd, yfracd,
dataType);
break;
default:
break;
}
return s;
}
/** This method performs a bilinear interpolation of a pixel inside a binary image. */
public int interpolateBinary(int xNextBitNo, Number[] sourceData, int xfrac, int yfrac,
int sourceYOffset, int sourceScanlineStride, int[] coordinates, int[] roiDataArray,
int roiYOffset, int roiScanlineStride, RandomIter roiIter) {
// Shift inside the pixel element, to the adjacent bit.
int xNextShiftNo = 0;
// 4 surrounding pixel initialization
int s00 = 0;
int s01 = 0;
int s10 = 0;
int s11 = 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 w01index = 0;
int w10index = 0;
int w11index = 0;
int w00 = 1;
int w01 = 1;
int w10 = 1;
int w11 = 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;
w01 = roiIter.getSample(x0 + 1, y0, 0) & 0x1;
w10 = roiIter.getSample(x0, y0 + 1, 0) & 0x1;
w11 = roiIter.getSample(x0 + 1, y0 + 1, 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;
// Conversion from bit to Byte for searching the element of the pixel adjacent to the selected one.
int xNextByteNo = xNextBitNo >> 3;
// This value is used for searching the adjacent pixel inside the element.
xNextShiftNo = 7 - (xNextBitNo & 7);
// Searching of the 4 pixels surrounding the selected one.
s00 = (sourceData[sourceYOffset + sbytenum].byteValue() >> sshift) & 0x01;
s01 = (sourceData[sourceYOffset + xNextByteNo].byteValue() >> xNextShiftNo) & 0x01;
s10 = (sourceData[sourceYOffset + sourceScanlineStride + sbytenum].byteValue() >> sshift) & 0x01;
s11 = (sourceData[sourceYOffset + sourceScanlineStride + xNextByteNo].byteValue() >> xNextShiftNo) & 0x01;
if (useROIAccessor) {
int roiDataLength = roiDataArray.length;
w00index = roiYOffset + sbytenum;
if (w00index > roiDataLength || (roiDataArray[w00index] >> sshift & 0x01) == 0) {
return black;
}
w01index = roiYOffset + xNextByteNo;
w10index = roiYOffset + roiScanlineStride + sbytenum;
w11index = roiYOffset + roiScanlineStride + xNextByteNo;
w00 = w00index < roiDataLength ? roiDataArray[w00index] >> sshift & 0x01 : 0;
w01 = w01index < roiDataLength ? roiDataArray[w01index] >> xNextShiftNo & 0x01 : 0;
w10 = w10index < roiDataLength ? roiDataArray[w10index] >> sshift & 0x01 : 0;
w11 = w11index < roiDataLength ? roiDataArray[w11index] >> xNextShiftNo & 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 xNextShortNo = xNextBitNo >> 4;
xNextShiftNo = 15 - (xNextBitNo & 15);
int sshortnum = sbitnum >> 4;
sshift = 15 - (sbitnum & 15);
s00 = (sourceData[sourceYOffset + sshortnum].shortValue() >> sshift) & 0x01;
s01 = (sourceData[sourceYOffset + xNextShortNo].shortValue() >> xNextShiftNo) & 0x01;
s10 = (sourceData[sourceYOffset + sourceScanlineStride + sshortnum].shortValue() >> sshift) & 0x01;
s11 = (sourceData[sourceYOffset + sourceScanlineStride + xNextShortNo].shortValue() >> xNextShiftNo) & 0x01;
if (useROIAccessor) {
int roiDataLength = roiDataArray.length;
w00index = roiYOffset + sshortnum;
if (w00index > roiDataLength || (roiDataArray[w00index] >> sshift & 0x01) == 0) {
return black;
}
w01index = roiYOffset + xNextShortNo;
w10index = roiYOffset + roiScanlineStride + sshortnum;
w11index = roiYOffset + roiScanlineStride + xNextShortNo;
w00 = w00index < roiDataLength ? roiDataArray[w00index] >> sshift & 0x01 : 0;
w01 = w01index < roiDataLength ? roiDataArray[w01index] >> xNextShiftNo & 0x01 : 0;
w10 = w10index < roiDataLength ? roiDataArray[w10index] >> sshift & 0x01 : 0;
w11 = w11index < roiDataLength ? roiDataArray[w11index] >> xNextShiftNo & 0x01 : 0;
}
break;
case DataBuffer.TYPE_INT:
// Same operations like the ones above but the step is done with integers and not short
int xNextIntNo = xNextBitNo >> 5;
xNextShiftNo = 31 - (xNextBitNo & 31);
int sintnum = sbitnum >> 5;
sshift = 31 - (sbitnum & 31);
s00 = (sourceData[sourceYOffset + sintnum].intValue() >> sshift) & 0x01;
s01 = (sourceData[sourceYOffset + xNextIntNo].intValue() >> xNextShiftNo) & 0x01;
s10 = (sourceData[sourceYOffset + sourceScanlineStride + sintnum].intValue() >> sshift) & 0x01;
s11 = (sourceData[sourceYOffset + sourceScanlineStride + xNextIntNo].intValue() >> xNextShiftNo) & 0x01;
if (useROIAccessor) {
int roiDataLength = roiDataArray.length;
w00index = roiYOffset + sintnum;
if (w00index > roiDataLength || (roiDataArray[w00index] >> sshift & 0x01) == 0) {
return black;
}
w01index = roiYOffset + xNextIntNo;
w10index = roiYOffset + roiScanlineStride + sintnum;
w11index = roiYOffset + roiScanlineStride + xNextIntNo;
w00 = w00index < roiDataLength ? roiDataArray[w00index] >> sshift & 0x01 : 0;
w01 = w01index < roiDataLength ? roiDataArray[w01index] >> xNextShiftNo & 0x01 : 0;
w10 = w10index < roiDataLength ? roiDataArray[w10index] >> sshift & 0x01 : 0;
w11 = w11index < roiDataLength ? roiDataArray[w11index] >> xNextShiftNo & 0x01 : 0;
}
break;
default:
break;
}
int sumWeight = w00 + w01 + w10 + w11;
if (sumWeight == 0) {
return black;
}
// Bilinear Interpolation
int s = computeValue(s00, s01, s10, s11, w00, w01, w10, w11, xfrac, yfrac);
return s;
}
/* Private method for calculate bilinear interpolation for byte, short/ushort, integer dataType */
private int computeValue(int s00, int s01, int s10, int s11, int w00, int w01, int w10,
int w11, int xfrac, int yfrac) {
int s0 = 0;
int s1 = 0;
int s = 0;
long s0L = 0;
long s1L = 0;
// Complementary values of the fractional part
int xfracCompl = (int) Math.pow(2, subsampleBits) - xfrac;
int yfracCompl = (int) Math.pow(2, subsampleBits) - yfrac;
int shift = 29 - subsampleBits;
// For Integer value is possible that a bitshift of "subsampleBits" could shift over the integer bit number
// so the samples, in this case, are expanded to Long.
boolean s0Long = ((s00 | s10) >>> shift == 0);
boolean s1Long = ((s01 | s11) >>> shift == 0);
// If all the weight are 0 the destination NO DATA is returned
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return (int) destinationNoData;
}
// Otherwise all the possible weight combination are checked
if (w00 == 0 || w01 == 0 || w10 == 0 || w11 == 0) {
// For integers is even considered the case when the integers are expanded to longs
if (dataType == DataBuffer.TYPE_INT) {
if (w00 == 0 && w01 == 0) {
s0L = 0;
} else if (w00 == 0) { // w01 = 1
if (s1Long) {
s0L = -s01 * xfracCompl + (s01 << subsampleBits);
} else {
s0L = -s01 * xfracCompl + ((long) s01 << subsampleBits);
}
} else if (w01 == 0) {// w00 = 1
if (s0Long) {
s0L = -s00 * xfrac + (s00 << subsampleBits);
} else {
s0L = -s00 * xfrac + ((long) s00 << subsampleBits);
}
} else {// w00 = 1 & W01 = 1
if (s0Long) {
if (s1Long) {
s0L = (s01 - s00) * xfrac + (s00 << subsampleBits);
} else {
s0L = ((long) s01 - s00) * xfrac + (s00 << subsampleBits);
}
} else {
s0L = ((long) s01 - s00) * xfrac + ((long) s00 << subsampleBits);
}
}
// lower value
if (w10 == 0 && w11 == 0) {
s1L = 0;
} else if (w10 == 0) { // w11 = 1
if (s1Long) {
s1L = -s11 * xfracCompl + (s11 << subsampleBits);
} else {
s1L = -s11 * xfracCompl + ((long) s11 << subsampleBits);
}
} else if (w11 == 0) { // w10 = 1
if (s0Long) {// - (s10 * xfrac); //s10;
s1L = -s10 * xfrac + (s10 << subsampleBits);
} else {
s1L = -s10 * xfrac + ((long) s10 << subsampleBits);
}
} else {
if (s0Long) {
if (s1Long) {
s1L = (s11 - s10) * xfrac + (s10 << subsampleBits);
} else {
s1L = ((long) s11 - s10) * xfrac + (s10 << subsampleBits);
}
} else {
s1L = ((long) s11 - s10) * xfrac + ((long) s10 << subsampleBits);
}
}
if (w00 == 0 && w01 == 0) {
s = (int) (-s1L * yfracCompl + ((s1L << subsampleBits) + round2) >> shift2);
} else {
if (w10 == 0 && w11 == 0) {
s = (int) (-s0L * yfrac + ((s0L << subsampleBits) + round2) >> shift2);
} else {
s = (int) (((s1L - s0L) * yfrac + (s0L << subsampleBits) + round2) >> shift2);
}
}
} else {
// Interpolation for type byte, ushort, short
if (w00 == 0 && w01 == 0) {
s0 = 0;
} else if (w00 == 0) { // w01 = 1
s0 = -s01 * xfracCompl + (s01 << subsampleBits);
} else if (w01 == 0) {// w00 = 1
s0 = -s00 * xfrac + (s00 << subsampleBits);// s00;
} else {// w00 = 1 & W01 = 1
s0 = (s01 - s00) * xfrac + (s00 << subsampleBits);
}
// lower value
if (w10 == 0 && w11 == 0) {
s1 = 0;
} else if (w10 == 0) { // w11 = 1
s1 = -s11 * xfracCompl + (s11 << subsampleBits);
} else if (w11 == 0) { // w10 = 1
s1 = -s10 * xfrac + (s10 << subsampleBits);// - (s10 * xfrac); //s10;
} else {
s1 = (s11 - s10) * xfrac + (s10 << subsampleBits);
}
if (w00 == 0 && w01 == 0) {
s = (-s1 * yfracCompl + (s1 << subsampleBits) + round2) >> shift2;
} else {
if (w10 == 0 && w11 == 0) {
s = (-s0 * yfrac + (s0 << subsampleBits) + round2) >> shift2;
} else {
s = ((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2;
}
}
}
} else {
// Perform the bilinear interpolation
if (dataType == DataBuffer.TYPE_INT) {
if (s0Long) {
if (s1Long) {
s0 = (s01 - s00) * xfrac + (s00 << subsampleBits);
s1 = (s11 - s10) * xfrac + (s10 << subsampleBits);
s = ((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2;
} else {
s0L = ((long) s01 - s00) * xfrac + (s00 << subsampleBits);
s1L = ((long) s11 - s10) * xfrac + (s10 << subsampleBits);
s = (int) (((s1L - s0L) * yfrac + (s0L << subsampleBits) + round2) >> shift2);
}
} else {
s0L = ((long) s01 - s00) * xfrac + ((long) s00 << subsampleBits);
s1L = ((long) s11 - s10) * xfrac + ((long) s10 << subsampleBits);
s = (int) (((s1L - s0L) * yfrac + (s0L << subsampleBits) + round2) >> shift2);
}
} else {
s0 = (s01 - s00) * xfrac + (s00 << subsampleBits);
s1 = (s11 - s10) * xfrac + (s10 << subsampleBits);
s = ((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2;
}
}
switch (dataType) {
case DataBuffer.TYPE_BYTE:
s = (byte) s & 0xff;
break;
case DataBuffer.TYPE_USHORT:
s = (short) s & 0xffff;
break;
case DataBuffer.TYPE_SHORT:
s = (short) s;
break;
default:
break;
}
return s;
}
/* Private method for calculate bilinear interpolation for float/double dataType */
private Number computeValueDouble(double s00, double s01, double s10, double s11, double w00,
double w01, double w10, double w11, double xfrac, double yfrac, int dataType) {
double s0 = 0;
double s1 = 0;
double s = 0;
// Complementary values of the fractional part
double xfracCompl = 1 - xfrac;
double yfracCompl = 1 - yfrac;
if (w00 == 0 && w01 == 0 && w10 == 0 && w11 == 0) {
return destinationNoData;
}
if (w00 == 0 || w01 == 0 || w10 == 0 || w11 == 0) {
if (w00 == 0 && w01 == 0) {
s0 = 0;
} else if (w00 == 0) { // w01 = 1
s0 = s01 * xfrac;
} else if (w01 == 0) {// w00 = 1
s0 = s00 * xfracCompl;// s00;
} else {// w00 = 1 & W01 = 1
s0 = (s01 - s00) * xfrac + s00;
}
// lower value
if (w10 == 0 && w11 == 0) {
s1 = 0;
} else if (w10 == 0) { // w11 = 1
s1 = s11 * xfrac;
} else if (w11 == 0) { // w10 = 1
s1 = s10 * xfracCompl;// - (s10 * xfrac); //s10;
} else {
s1 = (s11 - s10) * xfrac + s10;
}
if (w00 == 0 && w01 == 0) {
s = s1 * yfrac;
} else {
if (w10 == 0 && w11 == 0) {
s = s0 * yfracCompl;
} else {
s = (s1 - s0) * yfrac + s0;
}
}
} else {
// Perform the bilinear interpolation because all the weight are not 0.
s0 = (s01 - s00) * xfrac + s00;
s1 = (s11 - s10) * xfrac + s10;
s = (s1 - s0) * yfrac + s0;
}
// Simple conversion for float dataType.
if (dataType == DataBuffer.TYPE_FLOAT) {
return (float) s;
} else {
return s;
}
}
}