/*
* $RCSfile: AddConstOpImage.java,v $
*
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 04:56:12 $
* $State: Exp $
*/
package com.lightcrafts.media.jai.opimage;
import com.lightcrafts.mediax.jai.ColormapOpImage;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import com.lightcrafts.mediax.jai.ImageLayout;
import com.lightcrafts.mediax.jai.RasterAccessor;
import com.lightcrafts.mediax.jai.RasterFormatTag;
import java.util.Map;
import com.lightcrafts.media.jai.util.ImageUtil;
// import com.lightcrafts.media.jai.test.OpImageTester;
/**
* An <code>OpImage</code> implementing the "AddConst" operation.
*
* <p>This <code>OpImage</code> adds a set of constants, one for each
* band of the source image, to the pixels of a rendered image.
* The destination pixel values are calculated as:
* <pre>
* for (int h = 0; h < dstHeight; h++) {
* for (int w = 0; w < dstWidth; w++) {
* for (int b = 0; b < dstNumBands; b++) {
* if (constants.length < dstNumBands) {
* dst[h][w][b] = srcs[h][w][b] + constants[0];
* } else {
* dst[h][w][b] = srcs[h][w][b] + constants[b];
* }
* }
* }
* }
* </pre>
*
* @see com.lightcrafts.mediax.jai.operator.AddConstDescriptor
* @see AddConstCRIF
*
*/
final class AddConstOpImage extends ColormapOpImage {
/** The constants to be added, one for each band. */
protected double[] constants;
private byte[][] byteTable = null;
private synchronized void initByteTable() {
if (byteTable != null) {
return;
}
int nbands = constants.length;
byteTable = new byte[nbands][256];
// Initialize table which implements AddConst and clamp
for(int band=0; band<nbands; band++) {
int k = ImageUtil.clampRoundInt(constants[band]);
byte[] t = byteTable[band];
for (int i = 0; i < 256; i++) {
int sum = i + k;
if (sum < 0) {
t[i] = (byte)0;
} else if (sum > 255) {
t[i] = (byte)255;
} else {
t[i] = (byte)sum;
}
}
}
}
/**
* Constructor.
*
* @param source The source image.
* @param layout The destination image layout.
* @param constants The constants to be added, stored as reference.
*/
public AddConstOpImage(RenderedImage source,
Map config,
ImageLayout layout,
double[] constants) {
super(source, layout, config, true);
int numBands = getSampleModel().getNumBands();
if (constants.length < numBands) {
this.constants = new double[numBands];
for (int i = 0; i < numBands; i++) {
this.constants[i] = constants[0];
}
} else {
this.constants = (double[])constants.clone();
}
// Set flag to permit in-place operation.
permitInPlaceOperation();
// Initialize the colormap if necessary.
initializeColormapOperation();
}
/**
* Transform the colormap according to the rescaling parameters.
*/
protected void transformColormap(byte[][] colormap) {
initByteTable();
for(int b = 0; b < 3; b++) {
byte[] map = colormap[b];
byte[] luTable = byteTable[b >= byteTable.length ? 0 : b];
int mapSize = map.length;
for(int i = 0; i < mapSize; i++) {
map[i] = luTable[(map[i] & 0xFF)];
}
}
}
/**
* Adds a constant to the pixel values within a specified rectangle.
*
* @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) {
// Retrieve format tags.
RasterFormatTag[] formatTags = getFormatTags();
Rectangle srcRect = mapDestRect(destRect, 0);
RasterAccessor dst = new RasterAccessor(dest, destRect,
formatTags[1], getColorModel());
RasterAccessor src = new RasterAccessor(sources[0], srcRect,
formatTags[0],
getSourceImage(0).getColorModel());
switch (dst.getDataType()) {
case DataBuffer.TYPE_BYTE:
computeRectByte(src, dst);
break;
case DataBuffer.TYPE_USHORT:
computeRectUShort(src, dst);
break;
case DataBuffer.TYPE_SHORT:
computeRectShort(src, dst);
break;
case DataBuffer.TYPE_INT:
computeRectInt(src, dst);
break;
case DataBuffer.TYPE_FLOAT:
computeRectFloat(src, dst);
break;
case DataBuffer.TYPE_DOUBLE:
computeRectDouble(src, dst);
break;
}
if (dst.needsClamping()) {
/* Further clamp down to underlying raster data type. */
dst.clampDataArrays();
}
dst.copyDataToRaster();
}
private void computeRectByte(RasterAccessor src,
RasterAccessor dst) {
initByteTable();
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int dstLineStride = dst.getScanlineStride();
int dstPixelStride = dst.getPixelStride();
int[] dstBandOffsets = dst.getBandOffsets();
byte[][] dstData = dst.getByteDataArrays();
int srcLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int[] srcBandOffsets = src.getBandOffsets();
byte[][] srcData = src.getByteDataArrays();
for (int b = 0; b < dstBands; b++) {
byte[] d = dstData[b];
byte[] s = srcData[b];
byte[] clamp = byteTable[b];
int dstLineOffset = dstBandOffsets[b];
int srcLineOffset = srcBandOffsets[b];
for (int h = 0; h < dstHeight; h++) {
int dstPixelOffset = dstLineOffset;
int srcPixelOffset = srcLineOffset;
dstLineOffset += dstLineStride;
srcLineOffset += srcLineStride;
int dstEnd = dstPixelOffset + dstWidth*dstPixelStride;
while(dstPixelOffset < dstEnd) {
d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF];
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
private void computeRectUShort(RasterAccessor src,
RasterAccessor dst) {
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int dstLineStride = dst.getScanlineStride();
int dstPixelStride = dst.getPixelStride();
int[] dstBandOffsets = dst.getBandOffsets();
short[][] dstData = dst.getShortDataArrays();
int srcLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int[] srcBandOffsets = src.getBandOffsets();
short[][] srcData = src.getShortDataArrays();
for (int b = 0; b < dstBands; b++) {
int c = ImageUtil.clampRoundInt(constants[b]);
short[] d = dstData[b];
short[] s = srcData[b];
int dstLineOffset = dstBandOffsets[b];
int srcLineOffset = srcBandOffsets[b];
for (int h = 0; h < dstHeight; h++) {
int dstPixelOffset = dstLineOffset;
int srcPixelOffset = srcLineOffset;
dstLineOffset += dstLineStride;
srcLineOffset += srcLineStride;
for (int w = 0; w < dstWidth; w++) {
d[dstPixelOffset] = ImageUtil.clampUShort(
(s[srcPixelOffset] & 0xFFFF) + c);
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
private void computeRectShort(RasterAccessor src,
RasterAccessor dst) {
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int dstLineStride = dst.getScanlineStride();
int dstPixelStride = dst.getPixelStride();
int[] dstBandOffsets = dst.getBandOffsets();
short[][] dstData = dst.getShortDataArrays();
int srcLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int[] srcBandOffsets = src.getBandOffsets();
short[][] srcData = src.getShortDataArrays();
for (int b = 0; b < dstBands; b++) {
int c = ImageUtil.clampRoundInt(constants[b]);
short[] d = dstData[b];
short[] s = srcData[b];
int dstLineOffset = dstBandOffsets[b];
int srcLineOffset = srcBandOffsets[b];
for (int h = 0; h < dstHeight; h++) {
int dstPixelOffset = dstLineOffset;
int srcPixelOffset = srcLineOffset;
dstLineOffset += dstLineStride;
srcLineOffset += srcLineStride;
for (int w = 0; w < dstWidth; w++) {
d[dstPixelOffset] = ImageUtil.clampShort(s[srcPixelOffset] + c);
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
private void computeRectInt(RasterAccessor src,
RasterAccessor dst) {
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int dstLineStride = dst.getScanlineStride();
int dstPixelStride = dst.getPixelStride();
int[] dstBandOffsets = dst.getBandOffsets();
int[][] dstData = dst.getIntDataArrays();
int srcLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int[] srcBandOffsets = src.getBandOffsets();
int[][] srcData = src.getIntDataArrays();
for (int b = 0; b < dstBands; b++) {
long c = ImageUtil.clampRoundInt(constants[b]);
int[] d = dstData[b];
int[] s = srcData[b];
int dstLineOffset = dstBandOffsets[b];
int srcLineOffset = srcBandOffsets[b];
for (int h = 0; h < dstHeight; h++) {
int dstPixelOffset = dstLineOffset;
int srcPixelOffset = srcLineOffset;
dstLineOffset += dstLineStride;
srcLineOffset += srcLineStride;
for (int w = 0; w < dstWidth; w++) {
d[dstPixelOffset] = ImageUtil.clampInt(s[srcPixelOffset] + c);
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
private void computeRectFloat(RasterAccessor src,
RasterAccessor dst) {
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int dstLineStride = dst.getScanlineStride();
int dstPixelStride = dst.getPixelStride();
int[] dstBandOffsets = dst.getBandOffsets();
float[][] dstData = dst.getFloatDataArrays();
int srcLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int[] srcBandOffsets = src.getBandOffsets();
float[][] srcData = src.getFloatDataArrays();
for (int b = 0; b < dstBands; b++) {
double c = constants[b];
float[] d = dstData[b];
float[] s = srcData[b];
int dstLineOffset = dstBandOffsets[b];
int srcLineOffset = srcBandOffsets[b];
for (int h = 0; h < dstHeight; h++) {
int dstPixelOffset = dstLineOffset;
int srcPixelOffset = srcLineOffset;
dstLineOffset += dstLineStride;
srcLineOffset += srcLineStride;
for (int w = 0; w < dstWidth; w++) {
d[dstPixelOffset] = ImageUtil.clampFloat(s[srcPixelOffset] + c);
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
private void computeRectDouble(RasterAccessor src,
RasterAccessor dst) {
int dstWidth = dst.getWidth();
int dstHeight = dst.getHeight();
int dstBands = dst.getNumBands();
int dstLineStride = dst.getScanlineStride();
int dstPixelStride = dst.getPixelStride();
int[] dstBandOffsets = dst.getBandOffsets();
double[][] dstData = dst.getDoubleDataArrays();
int srcLineStride = src.getScanlineStride();
int srcPixelStride = src.getPixelStride();
int[] srcBandOffsets = src.getBandOffsets();
double[][] srcData = src.getDoubleDataArrays();
for (int b = 0; b < dstBands; b++) {
double c = constants[b];
double[] d = dstData[b];
double[] s = srcData[b];
int dstLineOffset = dstBandOffsets[b];
int srcLineOffset = srcBandOffsets[b];
for (int h = 0; h < dstHeight; h++) {
int dstPixelOffset = dstLineOffset;
int srcPixelOffset = srcLineOffset;
dstLineOffset += dstLineStride;
srcLineOffset += srcLineStride;
for (int w = 0; w < dstWidth; w++) {
d[dstPixelOffset] = s[srcPixelOffset] + c;
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
// public static void main(String args[]) {
// System.out.println("AddConstOpImage Test");
// ImageLayout layout;
// OpImage src, dst;
// Rectangle rect = new Rectangle(0, 0, 5, 5);
// double[] constants = new double[3];
// constants[0] = 10.0;
// constants[1] = 20.0;
// constants[2] = 30.0;
// System.out.println("1. PixelInterleaved byte 3-band");
// layout = OpImageTester.createImageLayout(
// 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false);
// src = OpImageTester.createRandomOpImage(layout);
// dst = new AddConstOpImage(src, null, null, constants);
// OpImageTester.testOpImage(dst, rect);
// OpImageTester.timeOpImage(dst, 10);
// System.out.println("3. PixelInterleaved int 3-band");
// layout = OpImageTester.createImageLayout(
// 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false);
// src = OpImageTester.createRandomOpImage(layout);
// dst = new AddConstOpImage(src, null, null, constants);
// OpImageTester.testOpImage(dst, rect);
// OpImageTester.timeOpImage(dst, 10);
// }
}