blob: eb7363cbb6edc3ad379a60a5b5bcc0f01e80932f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2000 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
26package java.awt.image;
27
28import java.util.Hashtable;
29import java.awt.image.ImageConsumer;
30import java.awt.image.ImageFilter;
31
32/**
33 * The <code>BufferedImageFilter</code> class subclasses an
34 * <code>ImageFilter</code> to provide a simple means of
35 * using a single-source/single-destination image operator
36 * ({@link BufferedImageOp}) to filter a <code>BufferedImage</code>
37 * in the Image Producer/Consumer/Observer
38 * paradigm. Examples of these image operators are: {@link ConvolveOp},
39 * {@link AffineTransformOp} and {@link LookupOp}.
40 *
41 * @see ImageFilter
42 * @see BufferedImage
43 * @see BufferedImageOp
44 */
45
46public class BufferedImageFilter extends ImageFilter implements Cloneable {
47 BufferedImageOp bufferedImageOp;
48 ColorModel model;
49 int width;
50 int height;
51 byte[] bytePixels;
52 int[] intPixels;
53
54 /**
55 * Constructs a <code>BufferedImageFilter</code> with the
56 * specified single-source/single-destination operator.
57 * @param op the specified <code>BufferedImageOp</code> to
58 * use to filter a <code>BufferedImage</code>
59 * @throws NullPointerException if op is null
60 */
61 public BufferedImageFilter (BufferedImageOp op) {
62 super();
63 if (op == null) {
64 throw new NullPointerException("Operation cannot be null");
65 }
66 bufferedImageOp = op;
67 }
68
69 /**
70 * Returns the <code>BufferedImageOp</code>.
71 * @return the operator of this <code>BufferedImageFilter</code>.
72 */
73 public BufferedImageOp getBufferedImageOp() {
74 return bufferedImageOp;
75 }
76
77 /**
78 * Filters the information provided in the
79 * {@link ImageConsumer#setDimensions(int, int) setDimensions } method
80 * of the {@link ImageConsumer} interface.
81 * <p>
82 * Note: This method is intended to be called by the
83 * {@link ImageProducer} of the <code>Image</code> whose pixels are
84 * being filtered. Developers using this class to retrieve pixels from
85 * an image should avoid calling this method directly since that
86 * operation could result in problems with retrieving the requested
87 * pixels.
88 * <p>
89 * @param width the width to which to set the width of this
90 * <code>BufferedImageFilter</code>
91 * @param height the height to which to set the height of this
92 * <code>BufferedImageFilter</code>
93 * @see ImageConsumer#setDimensions
94 */
95 public void setDimensions(int width, int height) {
96 if (width <= 0 || height <= 0) {
97 imageComplete(STATICIMAGEDONE);
98 return;
99 }
100 this.width = width;
101 this.height = height;
102 }
103
104 /**
105 * Filters the information provided in the
106 * {@link ImageConsumer#setColorModel(ColorModel) setColorModel} method
107 * of the <code>ImageConsumer</code> interface.
108 * <p>
109 * If <code>model</code> is <code>null</code>, this
110 * method clears the current <code>ColorModel</code> of this
111 * <code>BufferedImageFilter</code>.
112 * <p>
113 * Note: This method is intended to be called by the
114 * <code>ImageProducer</code> of the <code>Image</code>
115 * whose pixels are being filtered. Developers using this
116 * class to retrieve pixels from an image
117 * should avoid calling this method directly since that
118 * operation could result in problems with retrieving the
119 * requested pixels.
120 * @param model the {@link ColorModel} to which to set the
121 * <code>ColorModel</code> of this <code>BufferedImageFilter</code>
122 * @see ImageConsumer#setColorModel
123 */
124 public void setColorModel(ColorModel model) {
125 this.model = model;
126 }
127
128 private void convertToRGB() {
129 int size = width * height;
130 int newpixels[] = new int[size];
131 if (bytePixels != null) {
132 for (int i = 0; i < size; i++) {
133 newpixels[i] = this.model.getRGB(bytePixels[i] & 0xff);
134 }
135 } else if (intPixels != null) {
136 for (int i = 0; i < size; i++) {
137 newpixels[i] = this.model.getRGB(intPixels[i]);
138 }
139 }
140 bytePixels = null;
141 intPixels = newpixels;
142 this.model = ColorModel.getRGBdefault();
143 }
144
145 /**
146 * Filters the information provided in the <code>setPixels</code>
147 * method of the <code>ImageConsumer</code> interface which takes
148 * an array of bytes.
149 * <p>
150 * Note: This method is intended to be called by the
151 * <code>ImageProducer</code> of the <code>Image</code> whose pixels
152 * are being filtered. Developers using
153 * this class to retrieve pixels from an image should avoid calling
154 * this method directly since that operation could result in problems
155 * with retrieving the requested pixels.
156 * @throws IllegalArgumentException if width or height are less than
157 * zero.
158 * @see ImageConsumer#setPixels(int, int, int, int, ColorModel, byte[],
159 int, int)
160 */
161 public void setPixels(int x, int y, int w, int h,
162 ColorModel model, byte pixels[], int off,
163 int scansize) {
164 // Fix 4184230
165 if (w < 0 || h < 0) {
166 throw new IllegalArgumentException("Width ("+w+
167 ") and height ("+h+
168 ") must be > 0");
169 }
170 // Nothing to do
171 if (w == 0 || h == 0) {
172 return;
173 }
174 if (y < 0) {
175 int diff = -y;
176 if (diff >= h) {
177 return;
178 }
179 off += scansize * diff;
180 y += diff;
181 h -= diff;
182 }
183 if (y + h > height) {
184 h = height - y;
185 if (h <= 0) {
186 return;
187 }
188 }
189 if (x < 0) {
190 int diff = -x;
191 if (diff >= w) {
192 return;
193 }
194 off += diff;
195 x += diff;
196 w -= diff;
197 }
198 if (x + w > width) {
199 w = width - x;
200 if (w <= 0) {
201 return;
202 }
203 }
204 int dstPtr = y*width + x;
205 if (intPixels == null) {
206 if (bytePixels == null) {
207 bytePixels = new byte[width*height];
208 this.model = model;
209 } else if (this.model != model) {
210 convertToRGB();
211 }
212 if (bytePixels != null) {
213 for (int sh = h; sh > 0; sh--) {
214 System.arraycopy(pixels, off, bytePixels, dstPtr, w);
215 off += scansize;
216 dstPtr += width;
217 }
218 }
219 }
220 if (intPixels != null) {
221 int dstRem = width - w;
222 int srcRem = scansize - w;
223 for (int sh = h; sh > 0; sh--) {
224 for (int sw = w; sw > 0; sw--) {
225 intPixels[dstPtr++] = model.getRGB(pixels[off++]&0xff);
226 }
227 off += srcRem;
228 dstPtr += dstRem;
229 }
230 }
231 }
232 /**
233 * Filters the information provided in the <code>setPixels</code>
234 * method of the <code>ImageConsumer</code> interface which takes
235 * an array of integers.
236 * <p>
237 * Note: This method is intended to be called by the
238 * <code>ImageProducer</code> of the <code>Image</code> whose
239 * pixels are being filtered. Developers using this class to
240 * retrieve pixels from an image should avoid calling this method
241 * directly since that operation could result in problems
242 * with retrieving the requested pixels.
243 * @throws IllegalArgumentException if width or height are less than
244 * zero.
245 * @see ImageConsumer#setPixels(int, int, int, int, ColorModel, int[],
246 int, int)
247 */
248 public void setPixels(int x, int y, int w, int h,
249 ColorModel model, int pixels[], int off,
250 int scansize) {
251 // Fix 4184230
252 if (w < 0 || h < 0) {
253 throw new IllegalArgumentException("Width ("+w+
254 ") and height ("+h+
255 ") must be > 0");
256 }
257 // Nothing to do
258 if (w == 0 || h == 0) {
259 return;
260 }
261 if (y < 0) {
262 int diff = -y;
263 if (diff >= h) {
264 return;
265 }
266 off += scansize * diff;
267 y += diff;
268 h -= diff;
269 }
270 if (y + h > height) {
271 h = height - y;
272 if (h <= 0) {
273 return;
274 }
275 }
276 if (x < 0) {
277 int diff = -x;
278 if (diff >= w) {
279 return;
280 }
281 off += diff;
282 x += diff;
283 w -= diff;
284 }
285 if (x + w > width) {
286 w = width - x;
287 if (w <= 0) {
288 return;
289 }
290 }
291
292 if (intPixels == null) {
293 if (bytePixels == null) {
294 intPixels = new int[width * height];
295 this.model = model;
296 } else {
297 convertToRGB();
298 }
299 }
300 int dstPtr = y*width + x;
301 if (this.model == model) {
302 for (int sh = h; sh > 0; sh--) {
303 System.arraycopy(pixels, off, intPixels, dstPtr, w);
304 off += scansize;
305 dstPtr += width;
306 }
307 } else {
308 if (this.model != ColorModel.getRGBdefault()) {
309 convertToRGB();
310 }
311 int dstRem = width - w;
312 int srcRem = scansize - w;
313 for (int sh = h; sh > 0; sh--) {
314 for (int sw = w; sw > 0; sw--) {
315 intPixels[dstPtr++] = model.getRGB(pixels[off++]);
316 }
317 off += srcRem;
318 dstPtr += dstRem;
319 }
320 }
321 }
322
323 /**
324 * Filters the information provided in the <code>imageComplete</code>
325 * method of the <code>ImageConsumer</code> interface.
326 * <p>
327 * Note: This method is intended to be called by the
328 * <code>ImageProducer</code> of the <code>Image</code> whose pixels
329 * are being filtered. Developers using
330 * this class to retrieve pixels from an image should avoid calling
331 * this method directly since that operation could result in problems
332 * with retrieving the requested pixels.
333 * @param status the status of image loading
334 * @throws ImagingOpException if there was a problem calling the filter
335 * method of the <code>BufferedImageOp</code> associated with this
336 * instance.
337 * @see ImageConsumer#imageComplete
338 */
339 public void imageComplete(int status) {
340 WritableRaster wr;
341 switch(status) {
342 case IMAGEERROR:
343 case IMAGEABORTED:
344 // reinitialize the params
345 model = null;
346 width = -1;
347 height = -1;
348 intPixels = null;
349 bytePixels = null;
350 break;
351
352 case SINGLEFRAMEDONE:
353 case STATICIMAGEDONE:
354 if (width <= 0 || height <= 0) break;
355 if (model instanceof DirectColorModel) {
356 if (intPixels == null) break;
357 wr = createDCMraster();
358 }
359 else if (model instanceof IndexColorModel) {
360 int[] bandOffsets = {0};
361 if (bytePixels == null) break;
362 DataBufferByte db = new DataBufferByte(bytePixels,
363 width*height);
364 wr = Raster.createInterleavedRaster(db, width, height, width,
365 1, bandOffsets, null);
366 }
367 else {
368 convertToRGB();
369 if (intPixels == null) break;
370 wr = createDCMraster();
371 }
372 BufferedImage bi = new BufferedImage(model, wr,
373 model.isAlphaPremultiplied(),
374 null);
375 bi = bufferedImageOp.filter(bi, null);
376 WritableRaster r = bi.getRaster();
377 ColorModel cm = bi.getColorModel();
378 int w = r.getWidth();
379 int h = r.getHeight();
380 consumer.setDimensions(w, h);
381 consumer.setColorModel(cm);
382 if (cm instanceof DirectColorModel) {
383 DataBufferInt db = (DataBufferInt) r.getDataBuffer();
384 consumer.setPixels(0, 0, w, h,
385 cm, db.getData(), 0, w);
386 }
387 else if (cm instanceof IndexColorModel) {
388 DataBufferByte db = (DataBufferByte) r.getDataBuffer();
389 consumer.setPixels(0, 0, w, h,
390 cm, db.getData(), 0, w);
391 }
392 else {
393 throw new InternalError("Unknown color model "+cm);
394 }
395 break;
396 }
397 consumer.imageComplete(status);
398 }
399
400 private final WritableRaster createDCMraster() {
401 WritableRaster wr;
402 DirectColorModel dcm = (DirectColorModel) model;
403 boolean hasAlpha = model.hasAlpha();
404 int[] bandMasks = new int[3+(hasAlpha ? 1 : 0)];
405 bandMasks[0] = dcm.getRedMask();
406 bandMasks[1] = dcm.getGreenMask();
407 bandMasks[2] = dcm.getBlueMask();
408 if (hasAlpha) {
409 bandMasks[3] = dcm.getAlphaMask();
410 }
411 DataBufferInt db = new DataBufferInt(intPixels, width*height);
412 wr = Raster.createPackedRaster(db, width, height, width,
413 bandMasks, null);
414 return wr;
415 }
416
417}