/*
* @(#)BufferedImageFilter.java 1.31 05/11/17
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.awt.image;
import java.util.Hashtable;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageFilter;
/**
* The <code>BufferedImageFilter</code> class subclasses an
* <code>ImageFilter</code> to provide a simple means of
* using a single-source/single-destination image operator
* ({@link BufferedImageOp}) to filter a <code>BufferedImage</code>
* in the Image Producer/Consumer/Observer
* paradigm. Examples of these image operators are: {@link ConvolveOp},
* {@link AffineTransformOp} and {@link LookupOp}.
*
* @see ImageFilter
* @see BufferedImage
* @see BufferedImageOp
* @version 10 Feb 1997
*/
public class BufferedImageFilter extends ImageFilter implements Cloneable {
BufferedImageOp bufferedImageOp;
ColorModel model;
int width;
int height;
byte[] bytePixels;
int[] intPixels;
/**
* Constructs a <code>BufferedImageFilter</code> with the
* specified single-source/single-destination operator.
* @param op the specified <code>BufferedImageOp</code> to
* use to filter a <code>BufferedImage</code>
* @throws NullPointerException if op is null
*/
public BufferedImageFilter (BufferedImageOp op) {
super();
if (op == null) {
throw new NullPointerException("Operation cannot be null");
}
bufferedImageOp = op;
}
/**
* Returns the <code>BufferedImageOp</code>.
* @return the operator of this <code>BufferedImageFilter</code>.
*/
public BufferedImageOp getBufferedImageOp() {
return bufferedImageOp;
}
/**
* Filters the information provided in the
* {@link ImageConsumer#setDimensions(int, int) setDimensions } method
* of the {@link ImageConsumer} interface.
* <p>
* Note: This method is intended to be called by the
* {@link ImageProducer} of the <code>Image</code> whose pixels are
* being filtered. Developers using this class to retrieve pixels from
* an image should avoid calling this method directly since that
* operation could result in problems with retrieving the requested
* pixels.
* <p>
* @param width the width to which to set the width of this
* <code>BufferedImageFilter</code>
* @param height the height to which to set the height of this
* <code>BufferedImageFilter</code>
* @see ImageConsumer#setDimensions
*/
public void setDimensions(int width, int height) {
if (width <= 0 || height <= 0) {
imageComplete(STATICIMAGEDONE);
return;
}
this.width = width;
this.height = height;
}
/**
* Filters the information provided in the
* {@link ImageConsumer#setColorModel(ColorModel) setColorModel} method
* of the <code>ImageConsumer</code> interface.
* <p>
* If <code>model</code> is <code>null</code>, this
* method clears the current <code>ColorModel</code> of this
* <code>BufferedImageFilter</code>.
* <p>
* Note: This method is intended to be called by the
* <code>ImageProducer</code> of the <code>Image</code>
* whose pixels are being filtered. Developers using this
* class to retrieve pixels from an image
* should avoid calling this method directly since that
* operation could result in problems with retrieving the
* requested pixels.
* @param model the {@link ColorModel} to which to set the
* <code>ColorModel</code> of this <code>BufferedImageFilter</code>
* @see ImageConsumer#setColorModel
*/
public void setColorModel(ColorModel model) {
this.model = model;
}
private void convertToRGB() {
int size = width * height;
int newpixels[] = new int[size];
if (bytePixels != null) {
for (int i = 0; i < size; i++) {
newpixels[i] = this.model.getRGB(bytePixels[i] & 0xff);
}
} else if (intPixels != null) {
for (int i = 0; i < size; i++) {
newpixels[i] = this.model.getRGB(intPixels[i]);
}
}
bytePixels = null;
intPixels = newpixels;
this.model = ColorModel.getRGBdefault();
}
/**
* Filters the information provided in the <code>setPixels</code>
* method of the <code>ImageConsumer</code> interface which takes
* an array of bytes.
* <p>
* Note: This method is intended to be called by the
* <code>ImageProducer</code> of the <code>Image</code> whose pixels
* are being filtered. Developers using
* this class to retrieve pixels from an image should avoid calling
* this method directly since that operation could result in problems
* with retrieving the requested pixels.
* @throws IllegalArgumentException if width or height are less than
* zero.
* @see ImageConsumer#setPixels(int, int, int, int, ColorModel, byte[],
int, int)
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte pixels[], int off,
int scansize) {
// Fix 4184230
if (w < 0 || h < 0) {
throw new IllegalArgumentException("Width ("+w+
") and height ("+h+
") must be > 0");
}
// Nothing to do
if (w == 0 || h == 0) {
return;
}
if (y < 0) {
int diff = -y;
if (diff >= h) {
return;
}
off += scansize * diff;
y += diff;
h -= diff;
}
if (y + h > height) {
h = height - y;
if (h <= 0) {
return;
}
}
if (x < 0) {
int diff = -x;
if (diff >= w) {
return;
}
off += diff;
x += diff;
w -= diff;
}
if (x + w > width) {
w = width - x;
if (w <= 0) {
return;
}
}
int dstPtr = y*width + x;
if (intPixels == null) {
if (bytePixels == null) {
bytePixels = new byte[width*height];
this.model = model;
} else if (this.model != model) {
convertToRGB();
}
if (bytePixels != null) {
for (int sh = h; sh > 0; sh--) {
System.arraycopy(pixels, off, bytePixels, dstPtr, w);
off += scansize;
dstPtr += width;
}
}
}
if (intPixels != null) {
int dstRem = width - w;
int srcRem = scansize - w;
for (int sh = h; sh > 0; sh--) {
for (int sw = w; sw > 0; sw--) {
intPixels[dstPtr++] = model.getRGB(pixels[off++]&0xff);
}
off += srcRem;
dstPtr += dstRem;
}
}
}
/**
* Filters the information provided in the <code>setPixels</code>
* method of the <code>ImageConsumer</code> interface which takes
* an array of integers.
* <p>
* Note: This method is intended to be called by the
* <code>ImageProducer</code> of the <code>Image</code> whose
* pixels are being filtered. Developers using this class to
* retrieve pixels from an image should avoid calling this method
* directly since that operation could result in problems
* with retrieving the requested pixels.
* @throws IllegalArgumentException if width or height are less than
* zero.
* @see ImageConsumer#setPixels(int, int, int, int, ColorModel, int[],
int, int)
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, int pixels[], int off,
int scansize) {
// Fix 4184230
if (w < 0 || h < 0) {
throw new IllegalArgumentException("Width ("+w+
") and height ("+h+
") must be > 0");
}
// Nothing to do
if (w == 0 || h == 0) {
return;
}
if (y < 0) {
int diff = -y;
if (diff >= h) {
return;
}
off += scansize * diff;
y += diff;
h -= diff;
}
if (y + h > height) {
h = height - y;
if (h <= 0) {
return;
}
}
if (x < 0) {
int diff = -x;
if (diff >= w) {
return;
}
off += diff;
x += diff;
w -= diff;
}
if (x + w > width) {
w = width - x;
if (w <= 0) {
return;
}
}
if (intPixels == null) {
if (bytePixels == null) {
intPixels = new int[width * height];
this.model = model;
} else {
convertToRGB();
}
}
int dstPtr = y*width + x;
if (this.model == model) {
for (int sh = h; sh > 0; sh--) {
System.arraycopy(pixels, off, intPixels, dstPtr, w);
off += scansize;
dstPtr += width;
}
} else {
if (this.model != ColorModel.getRGBdefault()) {
convertToRGB();
}
int dstRem = width - w;
int srcRem = scansize - w;
for (int sh = h; sh > 0; sh--) {
for (int sw = w; sw > 0; sw--) {
intPixels[dstPtr++] = model.getRGB(pixels[off++]);
}
off += srcRem;
dstPtr += dstRem;
}
}
}
/**
* Filters the information provided in the <code>imageComplete</code>
* method of the <code>ImageConsumer</code> interface.
* <p>
* Note: This method is intended to be called by the
* <code>ImageProducer</code> of the <code>Image</code> whose pixels
* are being filtered. Developers using
* this class to retrieve pixels from an image should avoid calling
* this method directly since that operation could result in problems
* with retrieving the requested pixels.
* @param status the status of image loading
* @throws ImagingOpException if there was a problem calling the filter
* method of the <code>BufferedImageOp</code> associated with this
* instance.
* @see ImageConsumer#imageComplete
*/
public void imageComplete(int status) {
WritableRaster wr;
switch(status) {
case IMAGEERROR:
case IMAGEABORTED:
// reinitialize the params
model = null;
width = -1;
height = -1;
intPixels = null;
bytePixels = null;
break;
case SINGLEFRAMEDONE:
case STATICIMAGEDONE:
if (width <= 0 || height <= 0) break;
if (model instanceof DirectColorModel) {
if (intPixels == null) break;
wr = createDCMraster();
}
else if (model instanceof IndexColorModel) {
int[] bandOffsets = {0};
if (bytePixels == null) break;
DataBufferByte db = new DataBufferByte(bytePixels,
width*height);
wr = Raster.createInterleavedRaster(db, width, height, width,
1, bandOffsets, null);
}
else {
convertToRGB();
if (intPixels == null) break;
wr = createDCMraster();
}
BufferedImage bi = new BufferedImage(model, wr,
model.isAlphaPremultiplied(),
null);
bi = bufferedImageOp.filter(bi, null);
WritableRaster r = bi.getRaster();
ColorModel cm = bi.getColorModel();
int w = r.getWidth();
int h = r.getHeight();
consumer.setDimensions(w, h);
consumer.setColorModel(cm);
if (cm instanceof DirectColorModel) {
DataBufferInt db = (DataBufferInt) r.getDataBuffer();
consumer.setPixels(0, 0, w, h,
cm, db.getData(), 0, w);
}
else if (cm instanceof IndexColorModel) {
DataBufferByte db = (DataBufferByte) r.getDataBuffer();
consumer.setPixels(0, 0, w, h,
cm, db.getData(), 0, w);
}
else {
throw new InternalError("Unknown color model "+cm);
}
break;
}
consumer.imageComplete(status);
}
private final WritableRaster createDCMraster() {
WritableRaster wr;
DirectColorModel dcm = (DirectColorModel) model;
boolean hasAlpha = model.hasAlpha();
int[] bandMasks = new int[3+(hasAlpha ? 1 : 0)];
bandMasks[0] = dcm.getRedMask();
bandMasks[1] = dcm.getGreenMask();
bandMasks[2] = dcm.getBlueMask();
if (hasAlpha) {
bandMasks[3] = dcm.getAlphaMask();
}
DataBufferInt db = new DataBufferInt(intPixels, width*height);
wr = Raster.createPackedRaster(db, width, height, width,
bandMasks, null);
return wr;
}
}