blob: 2131bb50a30fabde864f8e7f571a9ca29eb79d20 [file] [log] [blame]
/*
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.awt.image;
import java.awt.image.ImageConsumer;
import java.awt.image.ColorModel;
/**
* This class provides an easy way to create an ImageFilter which modifies
* the pixels of an image in the default RGB ColorModel. It is meant to
* be used in conjunction with a FilteredImageSource object to produce
* filtered versions of existing images. It is an abstract class that
* provides the calls needed to channel all of the pixel data through a
* single method which converts pixels one at a time in the default RGB
* ColorModel regardless of the ColorModel being used by the ImageProducer.
* The only method which needs to be defined to create a useable image
* filter is the filterRGB method. Here is an example of a definition
* of a filter which swaps the red and blue components of an image:
* <pre>{@code
*
* class RedBlueSwapFilter extends RGBImageFilter {
* public RedBlueSwapFilter() {
* // The filter's operation does not depend on the
* // pixel's location, so IndexColorModels can be
* // filtered directly.
* canFilterIndexColorModel = true;
* }
*
* public int filterRGB(int x, int y, int rgb) {
* return ((rgb & 0xff00ff00)
* | ((rgb & 0xff0000) >> 16)
* | ((rgb & 0xff) << 16));
* }
* }
*
* }</pre>
*
* @see FilteredImageSource
* @see ImageFilter
* @see ColorModel#getRGBdefault
*
* @author Jim Graham
*/
public abstract class RGBImageFilter extends ImageFilter {
/**
* The {@code ColorModel} to be replaced by
* {@code newmodel} when the user calls
* {@link #substituteColorModel(ColorModel, ColorModel) substituteColorModel}.
*/
protected ColorModel origmodel;
/**
* The {@code ColorModel} with which to
* replace {@code origmodel} when the user calls
* {@code substituteColorModel}.
*/
protected ColorModel newmodel;
/**
* This boolean indicates whether or not it is acceptable to apply
* the color filtering of the filterRGB method to the color table
* entries of an IndexColorModel object in lieu of pixel by pixel
* filtering. Subclasses should set this variable to true in their
* constructor if their filterRGB method does not depend on the
* coordinate of the pixel being filtered.
* @see #substituteColorModel
* @see #filterRGB
* @see IndexColorModel
*/
protected boolean canFilterIndexColorModel;
/**
* If the ColorModel is an IndexColorModel and the subclass has
* set the canFilterIndexColorModel flag to true, we substitute
* a filtered version of the color model here and wherever
* that original ColorModel object appears in the setPixels methods.
* If the ColorModel is not an IndexColorModel or is null, this method
* overrides the default ColorModel used by the ImageProducer and
* specifies the default RGB ColorModel instead.
* <p>
* Note: This method is intended to be called by the
* {@code ImageProducer} of the {@code Image} whose pixels
* are being filtered. Developers using
* this class to filter pixels from an image should avoid calling
* this method directly since that operation could interfere
* with the filtering operation.
* @see ImageConsumer
* @see ColorModel#getRGBdefault
*/
public void setColorModel(ColorModel model) {
if (canFilterIndexColorModel && (model instanceof IndexColorModel)) {
ColorModel newcm = filterIndexColorModel((IndexColorModel)model);
substituteColorModel(model, newcm);
consumer.setColorModel(newcm);
} else {
consumer.setColorModel(ColorModel.getRGBdefault());
}
}
/**
* Registers two ColorModel objects for substitution. If the oldcm
* is encountered during any of the setPixels methods, the newcm
* is substituted and the pixels passed through
* untouched (but with the new ColorModel object).
* @param oldcm the ColorModel object to be replaced on the fly
* @param newcm the ColorModel object to replace oldcm on the fly
*/
public void substituteColorModel(ColorModel oldcm, ColorModel newcm) {
origmodel = oldcm;
newmodel = newcm;
}
/**
* Filters an IndexColorModel object by running each entry in its
* color tables through the filterRGB function that RGBImageFilter
* subclasses must provide. Uses coordinates of -1 to indicate that
* a color table entry is being filtered rather than an actual
* pixel value.
* @param icm the IndexColorModel object to be filtered
* @exception NullPointerException if {@code icm} is null
* @return a new IndexColorModel representing the filtered colors
*/
public IndexColorModel filterIndexColorModel(IndexColorModel icm) {
int mapsize = icm.getMapSize();
byte r[] = new byte[mapsize];
byte g[] = new byte[mapsize];
byte b[] = new byte[mapsize];
byte a[] = new byte[mapsize];
icm.getReds(r);
icm.getGreens(g);
icm.getBlues(b);
icm.getAlphas(a);
int trans = icm.getTransparentPixel();
boolean needalpha = false;
for (int i = 0; i < mapsize; i++) {
int rgb = filterRGB(-1, -1, icm.getRGB(i));
a[i] = (byte) (rgb >> 24);
if (a[i] != ((byte)0xff) && i != trans) {
needalpha = true;
}
r[i] = (byte) (rgb >> 16);
g[i] = (byte) (rgb >> 8);
b[i] = (byte) (rgb >> 0);
}
if (needalpha) {
return new IndexColorModel(icm.getPixelSize(), mapsize,
r, g, b, a);
} else {
return new IndexColorModel(icm.getPixelSize(), mapsize,
r, g, b, trans);
}
}
/**
* Filters a buffer of pixels in the default RGB ColorModel by passing
* them one by one through the filterRGB method.
* @param x the X coordinate of the upper-left corner of the region
* of pixels
* @param y the Y coordinate of the upper-left corner of the region
* of pixels
* @param w the width of the region of pixels
* @param h the height of the region of pixels
* @param pixels the array of pixels
* @param off the offset into the {@code pixels} array
* @param scansize the distance from one row of pixels to the next
* in the array
* @see ColorModel#getRGBdefault
* @see #filterRGB
*/
public void filterRGBPixels(int x, int y, int w, int h,
int pixels[], int off, int scansize) {
int index = off;
for (int cy = 0; cy < h; cy++) {
for (int cx = 0; cx < w; cx++) {
pixels[index] = filterRGB(x + cx, y + cy, pixels[index]);
index++;
}
index += scansize - w;
}
consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(),
pixels, off, scansize);
}
/**
* If the ColorModel object is the same one that has already
* been converted, then simply passes the pixels through with the
* converted ColorModel. Otherwise converts the buffer of byte
* pixels to the default RGB ColorModel and passes the converted
* buffer to the filterRGBPixels method to be converted one by one.
* <p>
* Note: This method is intended to be called by the
* {@code ImageProducer} of the {@code Image} whose pixels
* are being filtered. Developers using
* this class to filter pixels from an image should avoid calling
* this method directly since that operation could interfere
* with the filtering operation.
* @see ColorModel#getRGBdefault
* @see #filterRGBPixels
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte pixels[], int off,
int scansize) {
if (model == origmodel) {
consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize);
} else {
int filteredpixels[] = new int[w];
int index = off;
for (int cy = 0; cy < h; cy++) {
for (int cx = 0; cx < w; cx++) {
filteredpixels[cx] = model.getRGB((pixels[index] & 0xff));
index++;
}
index += scansize - w;
filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w);
}
}
}
/**
* If the ColorModel object is the same one that has already
* been converted, then simply passes the pixels through with the
* converted ColorModel, otherwise converts the buffer of integer
* pixels to the default RGB ColorModel and passes the converted
* buffer to the filterRGBPixels method to be converted one by one.
* Converts a buffer of integer pixels to the default RGB ColorModel
* and passes the converted buffer to the filterRGBPixels method.
* <p>
* Note: This method is intended to be called by the
* {@code ImageProducer} of the {@code Image} whose pixels
* are being filtered. Developers using
* this class to filter pixels from an image should avoid calling
* this method directly since that operation could interfere
* with the filtering operation.
* @see ColorModel#getRGBdefault
* @see #filterRGBPixels
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, int pixels[], int off,
int scansize) {
if (model == origmodel) {
consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize);
} else {
int filteredpixels[] = new int[w];
int index = off;
for (int cy = 0; cy < h; cy++) {
for (int cx = 0; cx < w; cx++) {
filteredpixels[cx] = model.getRGB(pixels[index]);
index++;
}
index += scansize - w;
filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w);
}
}
}
/**
* Subclasses must specify a method to convert a single input pixel
* in the default RGB ColorModel to a single output pixel.
* @param x the X coordinate of the pixel
* @param y the Y coordinate of the pixel
* @param rgb the integer pixel representation in the default RGB
* color model
* @return a filtered pixel in the default RGB color model.
* @see ColorModel#getRGBdefault
* @see #filterRGBPixels
*/
public abstract int filterRGB(int x, int y, int rgb);
}