/* 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.convolve;
import it.geosolutions.jaiext.range.Range;
import java.awt.RenderingHints;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import javax.media.jai.BorderExtender;
import javax.media.jai.ImageLayout;
import javax.media.jai.KernelJAI;
import javax.media.jai.ROI;
import javax.media.jai.RasterAccessor;
import javax.media.jai.iterator.RandomIter;
import com.sun.media.jai.util.ImageUtil;
public class SeparableConvolveOpImage extends ConvolveOpImage {
private float hValues[];
private float vValues[];
private float hTables[][];
public SeparableConvolveOpImage(RenderedImage source, BorderExtender extender,
RenderingHints hints, ImageLayout l, KernelJAI kernel, ROI roi, Range noData,
double destinationNoData, boolean skipNoData) {
super(source, extender, hints, l, kernel, roi, noData, destinationNoData, skipNoData); // TODO Auto-generated constructor stub
this.kernel = kernel;
kw = kernel.getWidth();
kh = kernel.getHeight();
kx = kernel.getXOrigin();
ky = kernel.getYOrigin();
hValues = kernel.getHorizontalKernelData();
vValues = kernel.getVerticalKernelData();
if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE) {
hTables = new float[hValues.length][256];
for (int i = 0; i < hValues.length; i++) {
float k = hValues[i];
for (int j = 0; j < 256; j++) {
byte b = (byte)j;
float f = (float)j;
hTables[i][b+128] = hasNoData && noData.contains(b) ? 0 : k*f;
}
}
}
}
@Override
protected void byteLoop(RasterAccessor src, RasterAccessor dst, RandomIter roiIter,
boolean roiContainsTile) {
int dwidth = dst.getWidth();
int dheight = dst.getHeight();
int dnumBands = dst.getNumBands();
byte dstDataArrays[][] = dst.getByteDataArrays();
int dstBandOffsets[] = dst.getBandOffsets();
int dstPixelStride = dst.getPixelStride();
int dstScanlineStride = dst.getScanlineStride();
byte srcDataArrays[][] = src.getByteDataArrays();
int srcBandOffsets[] = src.getBandOffsets();
int srcPixelStride = src.getPixelStride();
int srcScanlineStride = src.getScanlineStride();
float tmpBuffer[] = new float[kh*dwidth];
int tmpBufferSize = kh*dwidth;
// X,Y positions
int x0 = 0;
int y0 = 0;
int srcX = src.getX();
int srcY = src.getY();
if(caseA || (hasROI && hasNoData && roiContainsTile)){
for (int k = 0; k < dnumBands; k++) {
byte dstData[] = dstDataArrays[k];
byte srcData[] = srcDataArrays[k];
int srcScanlineOffset = srcBandOffsets[k];
int dstScanlineOffset = dstBandOffsets[k];
int revolver = 0;
int kvRevolver = 0; // to match kernel vValues
for (int j = 0; j < kh-1; j++) {
int srcPixelOffset = srcScanlineOffset;
for (int i = 0; i < dwidth; i++) {
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver+i] = f;
srcPixelOffset += srcPixelStride;
}
revolver += dwidth;
srcScanlineOffset += srcScanlineStride;
}
// srcScanlineStride already bumped by
// kh-1*scanlineStride
for (int j = 0; j < dheight; j++) {
int srcPixelOffset = srcScanlineOffset;
int dstPixelOffset = dstScanlineOffset;
for (int i = 0; i < dwidth; i++) {
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver + i] = f;
f = 0.5f;
// int a = 0;
// The vertical kernel must revolve as well
int b = kvRevolver + i;
for (int a=0; a < kh; a++){
f += tmpBuffer[b] * vValues[a];
b += dwidth;
if (b >= tmpBufferSize) b -= tmpBufferSize;
}
dstData[dstPixelOffset] = ImageUtil.clampRoundByte(f);
srcPixelOffset += srcPixelStride;
dstPixelOffset += dstPixelStride;
}
revolver += dwidth;
if (revolver == tmpBufferSize) {
revolver = 0;
}
kvRevolver += dwidth;
if (kvRevolver == tmpBufferSize) {
kvRevolver = 0;
}
srcScanlineOffset += srcScanlineStride;
dstScanlineOffset += dstScanlineStride;
}
}
}else if(caseB){
for (int k = 0; k < dnumBands; k++) {
byte dstData[] = dstDataArrays[k];
byte srcData[] = srcDataArrays[k];
int srcScanlineOffset = srcBandOffsets[k];
int dstScanlineOffset = dstBandOffsets[k];
int revolver = 0;
int kvRevolver = 0; // to match kernel vValues
for (int j = 0; j < kh-1; j++) {
int srcPixelOffset = srcScanlineOffset;
for (int i = 0; i < dwidth; i++) {
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver+i] = f;
srcPixelOffset += srcPixelStride;
}
revolver += dwidth;
srcScanlineOffset += srcScanlineStride;
}
// srcScanlineStride already bumped by
// kh-1*scanlineStride
for (int j = 0; j < dheight; j++) {
int srcPixelOffset = srcScanlineOffset;
int dstPixelOffset = dstScanlineOffset;
y0 = srcY + j;
for (int i = 0; i < dwidth; i++) {
x0 = srcX + i;
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver + i] = f;
if(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0){
f = 0.5f;
// int a = 0;
// The vertical kernel must revolve as well
int b = kvRevolver + i;
for (int a=0; a < kh; a++){
f += tmpBuffer[b] * vValues[a];
b += dwidth;
if (b >= tmpBufferSize) b -= tmpBufferSize;
}
dstData[dstPixelOffset] = ImageUtil.clampRoundByte(f);
} else {
dstData[dstPixelOffset] = destNoDataByte;
}
srcPixelOffset += srcPixelStride;
dstPixelOffset += dstPixelStride;
}
revolver += dwidth;
if (revolver == tmpBufferSize) {
revolver = 0;
}
kvRevolver += dwidth;
if (kvRevolver == tmpBufferSize) {
kvRevolver = 0;
}
srcScanlineOffset += srcScanlineStride;
dstScanlineOffset += dstScanlineStride;
}
}
}else if(caseC || (hasROI && hasNoData && roiContainsTile)){
for (int k = 0; k < dnumBands; k++) {
byte dstData[] = dstDataArrays[k];
byte srcData[] = srcDataArrays[k];
int srcScanlineOffset = srcBandOffsets[k];
int dstScanlineOffset = dstBandOffsets[k];
int revolver = 0;
int kvRevolver = 0; // to match kernel vValues
for (int j = 0; j < kh-1; j++) {
int srcPixelOffset = srcScanlineOffset;
for (int i = 0; i < dwidth; i++) {
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver+i] = f;
srcPixelOffset += srcPixelStride;
}
revolver += dwidth;
srcScanlineOffset += srcScanlineStride;
}
// srcScanlineStride already bumped by
// kh-1*scanlineStride
for (int j = 0; j < dheight; j++) {
int srcPixelOffset = srcScanlineOffset;
int dstPixelOffset = dstScanlineOffset;
for (int i = 0; i < dwidth; i++) {
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver + i] = f;
boolean isValid = true;
if(skipNoData){
int bandOff = srcBandOffsets[k];
for(int kj = 0; kj < kh && isValid; kj++){
int lineOff = (j + kj)*srcScanlineStride + bandOff;
for(int ki = 0; ki < kw && isValid; ki++){
int pixelOff = (i + ki) * srcPixelStride + lineOff;
byte value = srcData[pixelOff];
if(!lut[value + 128]){
isValid = false;
}
}
}
}
if(isValid){
f = 0.5f;
// int a = 0;
// The vertical kernel must revolve as well
int b = kvRevolver + i;
for (int a=0; a < kh; a++){
f += tmpBuffer[b] * vValues[a];
b += dwidth;
if (b >= tmpBufferSize) b -= tmpBufferSize;
}
dstData[dstPixelOffset] = ImageUtil.clampRoundByte(f);
} else {
dstData[dstPixelOffset] = destNoDataByte;
}
srcPixelOffset += srcPixelStride;
dstPixelOffset += dstPixelStride;
}
revolver += dwidth;
if (revolver == tmpBufferSize) {
revolver = 0;
}
kvRevolver += dwidth;
if (kvRevolver == tmpBufferSize) {
kvRevolver = 0;
}
srcScanlineOffset += srcScanlineStride;
dstScanlineOffset += dstScanlineStride;
}
}
}else{
for (int k = 0; k < dnumBands; k++) {
byte dstData[] = dstDataArrays[k];
byte srcData[] = srcDataArrays[k];
int srcScanlineOffset = srcBandOffsets[k];
int dstScanlineOffset = dstBandOffsets[k];
int revolver = 0;
int kvRevolver = 0; // to match kernel vValues
for (int j = 0; j < kh-1; j++) {
int srcPixelOffset = srcScanlineOffset;
y0 = srcY + j;
for (int i = 0; i < dwidth; i++) {
x0 = srcX + i;
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver+i] = f;
srcPixelOffset += srcPixelStride;
}
revolver += dwidth;
srcScanlineOffset += srcScanlineStride;
}
// srcScanlineStride already bumped by
// kh-1*scanlineStride
for (int j = 0; j < dheight; j++) {
int srcPixelOffset = srcScanlineOffset;
int dstPixelOffset = dstScanlineOffset;
for (int i = 0; i < dwidth; i++) {
int imageOffset = srcPixelOffset;
float f = 0.0f;
for (int v = 0; v < kw; v++) {
f += hTables[v][srcData[imageOffset]+128];
imageOffset += srcPixelStride;
}
tmpBuffer[revolver + i] = f;
if(roiBounds.contains(x0, y0) && roiIter.getSample(x0, y0, 0) > 0){
}
boolean isValid = true;
if(skipNoData){
int bandOff = srcBandOffsets[k];
for(int kj = 0; kj < kh && isValid; kj++){
int lineOff = (j + kj)*srcScanlineStride + bandOff;
for(int ki = 0; ki < kw && isValid; ki++){
int pixelOff = (i + ki) * srcPixelStride + lineOff;
byte value = srcData[pixelOff];
if(!lut[value + 128]){
isValid = false;
}
}
}
}
if(isValid){
f = 0.5f;
// int a = 0;
// The vertical kernel must revolve as well
int b = kvRevolver + i;
for (int a=0; a < kh; a++){
f += tmpBuffer[b] * vValues[a];
b += dwidth;
if (b >= tmpBufferSize) b -= tmpBufferSize;
}
dstData[dstPixelOffset] = ImageUtil.clampRoundByte(f);
} else {
dstData[dstPixelOffset] = destNoDataByte;
}
srcPixelOffset += srcPixelStride;
dstPixelOffset += dstPixelStride;
}
revolver += dwidth;
if (revolver == tmpBufferSize) {
revolver = 0;
}
kvRevolver += dwidth;
if (kvRevolver == tmpBufferSize) {
kvRevolver = 0;
}
srcScanlineOffset += srcScanlineStride;
dstScanlineOffset += dstScanlineStride;
}
}
}
}
@Override
protected void ushortLoop(RasterAccessor src, RasterAccessor dst, RandomIter roiIter,
boolean roiContainsTile) {
// TODO Auto-generated method stub
}
@Override
protected void shortLoop(RasterAccessor src, RasterAccessor dst, RandomIter roiIter,
boolean roiContainsTile) {
// TODO Auto-generated method stub
}
@Override
protected void intLoop(RasterAccessor src, RasterAccessor dst, RandomIter roiIter,
boolean roiContainsTile) {
// TODO Auto-generated method stub
}
@Override
protected void floatLoop(RasterAccessor src, RasterAccessor dst, RandomIter roiIter,
boolean roiContainsTile) {
// TODO Auto-generated method stub
}
@Override
protected void doubleLoop(RasterAccessor src, RasterAccessor dst, RandomIter roiIter,
boolean roiContainsTile) {
// TODO Auto-generated method stub
}
}