1 /*
2 * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package java.awt.image;
27
28 import java.awt.image.ImageConsumer;
29 import java.awt.image.ColorModel;
30
31 /**
32 * This class provides an easy way to create an ImageFilter which modifies
33 * the pixels of an image in the default RGB ColorModel. It is meant to
34 * be used in conjunction with a FilteredImageSource object to produce
35 * filtered versions of existing images. It is an abstract class that
36 * provides the calls needed to channel all of the pixel data through a
37 * single method which converts pixels one at a time in the default RGB
38 * ColorModel regardless of the ColorModel being used by the ImageProducer.
39 * The only method which needs to be defined to create a useable image
40 * filter is the filterRGB method. Here is an example of a definition
41 * of a filter which swaps the red and blue components of an image:
42 * <pre>
43 *
44 * class RedBlueSwapFilter extends RGBImageFilter {
45 * public RedBlueSwapFilter() {
46 * // The filter's operation does not depend on the
47 * // pixel's location, so IndexColorModels can be
48 * // filtered directly.
49 * canFilterIndexColorModel = true;
50 * }
51 *
52 * public int filterRGB(int x, int y, int rgb) {
53 * return ((rgb & 0xff00ff00)
54 * | ((rgb & 0xff0000) >> 16)
55 * | ((rgb & 0xff) << 16));
56 * }
57 * }
58 *
59 * </pre>
60 *
61 * @see FilteredImageSource
62 * @see ImageFilter
63 * @see ColorModel#getRGBdefault
64 *
65 * @author Jim Graham
66 */
67 public abstract class RGBImageFilter extends ImageFilter {
68
69 /**
70 * The <code>ColorModel</code> to be replaced by
71 * <code>newmodel</code> when the user calls
72 * {@link #substituteColorModel(ColorModel, ColorModel) substituteColorModel}.
73 */
74 protected ColorModel origmodel;
75
76 /**
77 * The <code>ColorModel</code> with which to
78 * replace <code>origmodel</code> when the user calls
79 * <code>substituteColorModel</code>.
80 */
81 protected ColorModel newmodel;
82
83 /**
84 * This boolean indicates whether or not it is acceptable to apply
85 * the color filtering of the filterRGB method to the color table
86 * entries of an IndexColorModel object in lieu of pixel by pixel
87 * filtering. Subclasses should set this variable to true in their
88 * constructor if their filterRGB method does not depend on the
89 * coordinate of the pixel being filtered.
90 * @see #substituteColorModel
91 * @see #filterRGB
92 * @see IndexColorModel
93 */
94 protected boolean canFilterIndexColorModel;
95
96 /**
97 * If the ColorModel is an IndexColorModel and the subclass has
98 * set the canFilterIndexColorModel flag to true, we substitute
99 * a filtered version of the color model here and wherever
100 * that original ColorModel object appears in the setPixels methods.
101 * If the ColorModel is not an IndexColorModel or is null, this method
102 * overrides the default ColorModel used by the ImageProducer and
103 * specifies the default RGB ColorModel instead.
104 * <p>
105 * Note: This method is intended to be called by the
106 * <code>ImageProducer</code> of the <code>Image</code> whose pixels
107 * are being filtered. Developers using
108 * this class to filter pixels from an image should avoid calling
109 * this method directly since that operation could interfere
110 * with the filtering operation.
111 * @see ImageConsumer
112 * @see ColorModel#getRGBdefault
113 */
114 public void setColorModel(ColorModel model) {
115 if (canFilterIndexColorModel && (model instanceof IndexColorModel)) {
116 ColorModel newcm = filterIndexColorModel((IndexColorModel)model);
117 substituteColorModel(model, newcm);
118 consumer.setColorModel(newcm);
119 } else {
120 consumer.setColorModel(ColorModel.getRGBdefault());
121 }
122 }
123
124 /**
125 * Registers two ColorModel objects for substitution. If the oldcm
126 * is encountered during any of the setPixels methods, the newcm
127 * is substituted and the pixels passed through
128 * untouched (but with the new ColorModel object).
129 * @param oldcm the ColorModel object to be replaced on the fly
130 * @param newcm the ColorModel object to replace oldcm on the fly
131 */
132 public void substituteColorModel(ColorModel oldcm, ColorModel newcm) {
133 origmodel = oldcm;
134 newmodel = newcm;
135 }
136
137 /**
138 * Filters an IndexColorModel object by running each entry in its
139 * color tables through the filterRGB function that RGBImageFilter
140 * subclasses must provide. Uses coordinates of -1 to indicate that
141 * a color table entry is being filtered rather than an actual
142 * pixel value.
143 * @param icm the IndexColorModel object to be filtered
144 * @exception NullPointerException if <code>icm</code> is null
145 * @return a new IndexColorModel representing the filtered colors
146 */
147 public IndexColorModel filterIndexColorModel(IndexColorModel icm) {
148 int mapsize = icm.getMapSize();
149 byte r[] = new byte[mapsize];
150 byte g[] = new byte[mapsize];
151 byte b[] = new byte[mapsize];
152 byte a[] = new byte[mapsize];
153 icm.getReds(r);
154 icm.getGreens(g);
155 icm.getBlues(b);
156 icm.getAlphas(a);
157 int trans = icm.getTransparentPixel();
158 boolean needalpha = false;
159 for (int i = 0; i < mapsize; i++) {
160 int rgb = filterRGB(-1, -1, icm.getRGB(i));
161 a[i] = (byte) (rgb >> 24);
162 if (a[i] != ((byte)0xff) && i != trans) {
163 needalpha = true;
164 }
165 r[i] = (byte) (rgb >> 16);
166 g[i] = (byte) (rgb >> 8);
167 b[i] = (byte) (rgb >> 0);
168 }
169 if (needalpha) {
170 return new IndexColorModel(icm.getPixelSize(), mapsize,
171 r, g, b, a);
172 } else {
173 return new IndexColorModel(icm.getPixelSize(), mapsize,
174 r, g, b, trans);
175 }
176 }
177
178 /**
179 * Filters a buffer of pixels in the default RGB ColorModel by passing
180 * them one by one through the filterRGB method.
181 * @param x the X coordinate of the upper-left corner of the region
182 * of pixels
183 * @param y the Y coordinate of the upper-left corner of the region
184 * of pixels
185 * @param w the width of the region of pixels
186 * @param h the height of the region of pixels
187 * @param pixels the array of pixels
188 * @param off the offset into the <code>pixels</code> array
189 * @param scansize the distance from one row of pixels to the next
190 * in the array
191 * @see ColorModel#getRGBdefault
192 * @see #filterRGB
193 */
194 public void filterRGBPixels(int x, int y, int w, int h,
195 int pixels[], int off, int scansize) {
196 int index = off;
197 for (int cy = 0; cy < h; cy++) {
198 for (int cx = 0; cx < w; cx++) {
199 pixels[index] = filterRGB(x + cx, y + cy, pixels[index]);
200 index++;
201 }
202 index += scansize - w;
203 }
204 consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(),
205 pixels, off, scansize);
206 }
207
208 /**
209 * If the ColorModel object is the same one that has already
210 * been converted, then simply passes the pixels through with the
211 * converted ColorModel. Otherwise converts the buffer of byte
212 * pixels to the default RGB ColorModel and passes the converted
213 * buffer to the filterRGBPixels method to be converted one by one.
214 * <p>
215 * Note: This method is intended to be called by the
216 * <code>ImageProducer</code> of the <code>Image</code> whose pixels
217 * are being filtered. Developers using
218 * this class to filter pixels from an image should avoid calling
219 * this method directly since that operation could interfere
220 * with the filtering operation.
221 * @see ColorModel#getRGBdefault
222 * @see #filterRGBPixels
223 */
224 public void setPixels(int x, int y, int w, int h,
225 ColorModel model, byte pixels[], int off,
226 int scansize) {
227 if (model == origmodel) {
228 consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize);
229 } else {
230 int filteredpixels[] = new int[w];
231 int index = off;
232 for (int cy = 0; cy < h; cy++) {
233 for (int cx = 0; cx < w; cx++) {
234 filteredpixels[cx] = model.getRGB((pixels[index] & 0xff));
235 index++;
236 }
237 index += scansize - w;
238 filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w);
239 }
240 }
241 }
242
243 /**
244 * If the ColorModel object is the same one that has already
245 * been converted, then simply passes the pixels through with the
246 * converted ColorModel, otherwise converts the buffer of integer
247 * pixels to the default RGB ColorModel and passes the converted
248 * buffer to the filterRGBPixels method to be converted one by one.
249 * Converts a buffer of integer pixels to the default RGB ColorModel
250 * and passes the converted buffer to the filterRGBPixels method.
251 * <p>
252 * Note: This method is intended to be called by the
253 * <code>ImageProducer</code> of the <code>Image</code> whose pixels
254 * are being filtered. Developers using
255 * this class to filter pixels from an image should avoid calling
256 * this method directly since that operation could interfere
257 * with the filtering operation.
258 * @see ColorModel#getRGBdefault
259 * @see #filterRGBPixels
260 */
261 public void setPixels(int x, int y, int w, int h,
262 ColorModel model, int pixels[], int off,
263 int scansize) {
264 if (model == origmodel) {
265 consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize);
266 } else {
267 int filteredpixels[] = new int[w];
268 int index = off;
269 for (int cy = 0; cy < h; cy++) {
270 for (int cx = 0; cx < w; cx++) {
271 filteredpixels[cx] = model.getRGB(pixels[index]);
272 index++;
273 }
274 index += scansize - w;
275 filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w);
276 }
277 }
278 }
279
280 /**
281 * Subclasses must specify a method to convert a single input pixel
282 * in the default RGB ColorModel to a single output pixel.
283 * @param x the X coordinate of the pixel
284 * @param y the Y coordinate of the pixel
285 * @param rgb the integer pixel representation in the default RGB
286 * color model
287 * @return a filtered pixel in the default RGB color model.
288 * @see ColorModel#getRGBdefault
289 * @see #filterRGBPixels
290 */
291 public abstract int filterRGB(int x, int y, int rgb);
292 }