/*
* $RCSfile: OverlayOpImage.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:39 $
* $State: Exp $
*/
package com.lightcrafts.media.jai.opimage;
import com.lightcrafts.media.jai.util.ImageUtil;
import com.lightcrafts.media.jai.util.JDKWorkarounds;
import java.awt.Rectangle;
import java.awt.image.DataBuffer;
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 java.util.Vector;
import com.lightcrafts.mediax.jai.ImageLayout;
import com.lightcrafts.mediax.jai.PlanarImage;
import com.lightcrafts.mediax.jai.PointOpImage;
import com.lightcrafts.mediax.jai.RasterAccessor;
import com.lightcrafts.mediax.jai.RasterFormatTag;
/**
* An <code>OpImage</code> implementing the "Overlay" operation.
*
* <p>This <code>OpImage</code> overlays one rendered image (source2)
* on top of another (source1). The two sources are required to have
* the same data type and number of bands.
*
* @see com.lightcrafts.mediax.jai.operator.OverlayDescriptor
* @see OverlayCRIF
*
*/
final class OverlayOpImage extends PointOpImage {
/**
* OverlayOpImage always has the bounds of the sourceUnder image.
* The image bounds specified in the "layout" are always ignored.
* Hence unset the image bounds so that PointImage.layoutHelper checks
* dont fail.
*/
private static ImageLayout layoutHelper(ImageLayout layout,
Vector sources,
Map config) {
if (layout != null) {
layout = (ImageLayout)layout.clone();
layout.unsetImageBounds();
}
return layout;
}
/**
* Construct an <code>OverlayOpImage</code>.
*
* @param sourceUnder The source image on the bottom.
* @param sourceOver The source image to be overlayed on top of
* <code>sourceUnder</code>.
* @param layout The image layout for the destination image.
*/
public OverlayOpImage(RenderedImage sourceUnder,
RenderedImage sourceOver,
Map config,
ImageLayout layout) {
super(sourceUnder, sourceOver,
layoutHelper(layout, vectorize(sourceUnder, sourceOver), config),
config, true);
/* Validate destination sampleModel. */
SampleModel srcSM = sourceUnder.getSampleModel();
if (sampleModel.getTransferType() != srcSM.getTransferType() ||
sampleModel.getNumBands() != srcSM.getNumBands()) {
sampleModel = srcSM.createCompatibleSampleModel(
tileWidth, tileHeight);
if(colorModel != null &&
!JDKWorkarounds.areCompatibleDataModels(sampleModel,
colorModel)) {
colorModel = ImageUtil.getCompatibleColorModel(sampleModel,
config);
}
}
/*
* Unlike other multi-source OpImages, the image dimension in this
* case is set to the same values as that of the source image on
* the bottom.
*/
minX = sourceUnder.getMinX();
minY = sourceUnder.getMinY();
width = sourceUnder.getWidth();
height = sourceUnder.getHeight();
}
/**
* Computes a tile. This method overrides PointOpImage.computeTile()
*
* @param tileX The X index of the tile.
* @param tileY The Y index of the tile.
*/
public Raster computeTile(int tileX, int tileY) {
/* Create a new WritableRaster to represent this tile. */
WritableRaster dest = createTile(tileX, tileY);
/* Clip the raster bound to image bounds. */
Rectangle destRect = dest.getBounds().intersection(getBounds());
PlanarImage srcUnder = getSource(0);
PlanarImage srcOver = getSource(1);
Rectangle srcUnderBounds = srcUnder.getBounds();
Rectangle srcOverBounds = srcOver.getBounds();
/* In case of PointOpImage, mapDestRect(destRect, i) = destRect). */
Raster[] sources = new Raster[1];
if (srcOverBounds.contains(destRect)) {
/* Tile is entirely inside sourceOver. */
sources[0] = srcOver.getData(destRect);
computeRect(sources, dest, destRect);
// Recycle the source tile
if(srcOver.overlapsMultipleTiles(destRect)) {
recycleTile(sources[0]);
}
return dest;
} else if (srcUnderBounds.contains(destRect) &&
!srcOverBounds.intersects(destRect)) {
/* Tile is entirely inside sourceUnder. */
sources[0] = srcUnder.getData(destRect);
computeRect(sources, dest, destRect);
// Recycle the source tile
if(srcUnder.overlapsMultipleTiles(destRect)) {
recycleTile(sources[0]);
}
return dest;
} else {
/* Tile is inside both sources. */
Rectangle isectUnder = destRect.intersection(srcUnderBounds);
sources[0] = srcUnder.getData(isectUnder);
computeRect(sources, dest, isectUnder);
// Recycle the source tile
if(srcUnder.overlapsMultipleTiles(isectUnder)) {
recycleTile(sources[0]);
}
if (srcOverBounds.intersects(destRect)) {
Rectangle isectOver = destRect.intersection(srcOverBounds);
sources[0] = srcOver.getData(isectOver);
computeRect(sources, dest, isectOver);
// Recycle the source tile
if(srcOver.overlapsMultipleTiles(isectOver)) {
recycleTile(sources[0]);
}
}
return dest;
}
}
/**
* Copies the pixel values of a source image 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();
RasterAccessor src = new RasterAccessor(sources[0], destRect,
formatTags[0],
getSource(0).getColorModel());
RasterAccessor dst = new RasterAccessor(dest, destRect,
formatTags[1], getColorModel());
switch (dst.getDataType()) {
case DataBuffer.TYPE_BYTE:
computeRectByte(src, dst);
break;
case DataBuffer.TYPE_USHORT:
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;
}
dst.copyDataToRaster();
}
private void computeRectByte(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();
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];
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];
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++) {
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] = s[srcPixelOffset];
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++) {
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] = s[srcPixelOffset];
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++) {
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] = s[srcPixelOffset];
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[] 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];
dstPixelOffset += dstPixelStride;
srcPixelOffset += srcPixelStride;
}
}
}
}
}