001 /*
002 * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt.image;
027
028 import java.util.Hashtable;
029 import java.awt.image.ImageProducer;
030 import java.awt.image.ImageConsumer;
031 import java.awt.image.ColorModel;
032 import java.awt.Image;
033
034 /**
035 * The PixelGrabber class implements an ImageConsumer which can be attached
036 * to an Image or ImageProducer object to retrieve a subset of the pixels
037 * in that image. Here is an example:
038 * <pre>
039 *
040 * public void handlesinglepixel(int x, int y, int pixel) {
041 * int alpha = (pixel >> 24) & 0xff;
042 * int red = (pixel >> 16) & 0xff;
043 * int green = (pixel >> 8) & 0xff;
044 * int blue = (pixel ) & 0xff;
045 * // Deal with the pixel as necessary...
046 * }
047 *
048 * public void handlepixels(Image img, int x, int y, int w, int h) {
049 * int[] pixels = new int[w * h];
050 * PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w);
051 * try {
052 * pg.grabPixels();
053 * } catch (InterruptedException e) {
054 * System.err.println("interrupted waiting for pixels!");
055 * return;
056 * }
057 * if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
058 * System.err.println("image fetch aborted or errored");
059 * return;
060 * }
061 * for (int j = 0; j < h; j++) {
062 * for (int i = 0; i < w; i++) {
063 * handlesinglepixel(x+i, y+j, pixels[j * w + i]);
064 * }
065 * }
066 * }
067 *
068 * </pre>
069 *
070 * @see ColorModel#getRGBdefault
071 *
072 * @version 1.33, 05/05/07
073 * @author Jim Graham
074 */
075 public class PixelGrabber implements ImageConsumer {
076 ImageProducer producer;
077
078 int dstX;
079 int dstY;
080 int dstW;
081 int dstH;
082
083 ColorModel imageModel;
084 byte[] bytePixels;
085 int[] intPixels;
086 int dstOff;
087 int dstScan;
088
089 private boolean grabbing;
090 private int flags;
091
092 private static final int GRABBEDBITS = (ImageObserver.FRAMEBITS | ImageObserver.ALLBITS);
093 private static final int DONEBITS = (GRABBEDBITS | ImageObserver.ERROR);
094
095 /**
096 * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
097 * section of pixels from the specified image into the given array.
098 * The pixels are stored into the array in the default RGB ColorModel.
099 * The RGB data for pixel (i, j) where (i, j) is inside the rectangle
100 * (x, y, w, h) is stored in the array at
101 * <tt>pix[(j - y) * scansize + (i - x) + off]</tt>.
102 * @see ColorModel#getRGBdefault
103 * @param img the image to retrieve pixels from
104 * @param x the x coordinate of the upper left corner of the rectangle
105 * of pixels to retrieve from the image, relative to the default
106 * (unscaled) size of the image
107 * @param y the y coordinate of the upper left corner of the rectangle
108 * of pixels to retrieve from the image
109 * @param w the width of the rectangle of pixels to retrieve
110 * @param h the height of the rectangle of pixels to retrieve
111 * @param pix the array of integers which are to be used to hold the
112 * RGB pixels retrieved from the image
113 * @param off the offset into the array of where to store the first pixel
114 * @param scansize the distance from one row of pixels to the next in
115 * the array
116 */
117 public PixelGrabber(Image img, int x, int y, int w, int h,
118 int[] pix, int off, int scansize) {
119 this (img.getSource(), x, y, w, h, pix, off, scansize);
120 }
121
122 /**
123 * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
124 * section of pixels from the image produced by the specified
125 * ImageProducer into the given array.
126 * The pixels are stored into the array in the default RGB ColorModel.
127 * The RGB data for pixel (i, j) where (i, j) is inside the rectangle
128 * (x, y, w, h) is stored in the array at
129 * <tt>pix[(j - y) * scansize + (i - x) + off]</tt>.
130 * @param ip the <code>ImageProducer</code> that produces the
131 * image from which to retrieve pixels
132 * @param x the x coordinate of the upper left corner of the rectangle
133 * of pixels to retrieve from the image, relative to the default
134 * (unscaled) size of the image
135 * @param y the y coordinate of the upper left corner of the rectangle
136 * of pixels to retrieve from the image
137 * @param w the width of the rectangle of pixels to retrieve
138 * @param h the height of the rectangle of pixels to retrieve
139 * @param pix the array of integers which are to be used to hold the
140 * RGB pixels retrieved from the image
141 * @param off the offset into the array of where to store the first pixel
142 * @param scansize the distance from one row of pixels to the next in
143 * the array
144 * @see ColorModel#getRGBdefault
145 */
146 public PixelGrabber(ImageProducer ip, int x, int y, int w, int h,
147 int[] pix, int off, int scansize) {
148 producer = ip;
149 dstX = x;
150 dstY = y;
151 dstW = w;
152 dstH = h;
153 dstOff = off;
154 dstScan = scansize;
155 intPixels = pix;
156 imageModel = ColorModel.getRGBdefault();
157 }
158
159 /**
160 * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
161 * section of pixels from the specified image. The pixels are
162 * accumulated in the original ColorModel if the same ColorModel
163 * is used for every call to setPixels, otherwise the pixels are
164 * accumulated in the default RGB ColorModel. If the forceRGB
165 * parameter is true, then the pixels will be accumulated in the
166 * default RGB ColorModel anyway. A buffer is allocated by the
167 * PixelGrabber to hold the pixels in either case. If (w < 0) or
168 * (h < 0), then they will default to the remaining width and
169 * height of the source data when that information is delivered.
170 * @param img the image to retrieve the image data from
171 * @param x the x coordinate of the upper left corner of the rectangle
172 * of pixels to retrieve from the image, relative to the default
173 * (unscaled) size of the image
174 * @param y the y coordinate of the upper left corner of the rectangle
175 * of pixels to retrieve from the image
176 * @param w the width of the rectangle of pixels to retrieve
177 * @param h the height of the rectangle of pixels to retrieve
178 * @param forceRGB true if the pixels should always be converted to
179 * the default RGB ColorModel
180 */
181 public PixelGrabber(Image img, int x, int y, int w, int h,
182 boolean forceRGB) {
183 producer = img.getSource();
184 dstX = x;
185 dstY = y;
186 dstW = w;
187 dstH = h;
188 if (forceRGB) {
189 imageModel = ColorModel.getRGBdefault();
190 }
191 }
192
193 /**
194 * Request the PixelGrabber to start fetching the pixels.
195 */
196 public synchronized void startGrabbing() {
197 if ((flags & DONEBITS) != 0) {
198 return;
199 }
200 if (!grabbing) {
201 grabbing = true;
202 flags &= ~(ImageObserver.ABORT);
203 producer.startProduction(this );
204 }
205 }
206
207 /**
208 * Request the PixelGrabber to abort the image fetch.
209 */
210 public synchronized void abortGrabbing() {
211 imageComplete(IMAGEABORTED);
212 }
213
214 /**
215 * Request the Image or ImageProducer to start delivering pixels and
216 * wait for all of the pixels in the rectangle of interest to be
217 * delivered.
218 * @return true if the pixels were successfully grabbed, false on
219 * abort, error or timeout
220 * @exception InterruptedException
221 * Another thread has interrupted this thread.
222 */
223 public boolean grabPixels() throws InterruptedException {
224 return grabPixels(0);
225 }
226
227 /**
228 * Request the Image or ImageProducer to start delivering pixels and
229 * wait for all of the pixels in the rectangle of interest to be
230 * delivered or until the specified timeout has elapsed. This method
231 * behaves in the following ways, depending on the value of
232 * <code>ms</code>:
233 * <ul>
234 * <li> If <code>ms</code> == 0, waits until all pixels are delivered
235 * <li> If <code>ms</code> > 0, waits until all pixels are delivered
236 * as timeout expires.
237 * <li> If <code>ms</code> < 0, returns <code>true</code> if all pixels
238 * are grabbed, <code>false</code> otherwise and does not wait.
239 * </ul>
240 * @param ms the number of milliseconds to wait for the image pixels
241 * to arrive before timing out
242 * @return true if the pixels were successfully grabbed, false on
243 * abort, error or timeout
244 * @exception InterruptedException
245 * Another thread has interrupted this thread.
246 */
247 public synchronized boolean grabPixels(long ms)
248 throws InterruptedException {
249 if ((flags & DONEBITS) != 0) {
250 return (flags & GRABBEDBITS) != 0;
251 }
252 long end = ms + System.currentTimeMillis();
253 if (!grabbing) {
254 grabbing = true;
255 flags &= ~(ImageObserver.ABORT);
256 producer.startProduction(this );
257 }
258 while (grabbing) {
259 long timeout;
260 if (ms == 0) {
261 timeout = 0;
262 } else {
263 timeout = end - System.currentTimeMillis();
264 if (timeout <= 0) {
265 break;
266 }
267 }
268 wait(timeout);
269 }
270 return (flags & GRABBEDBITS) != 0;
271 }
272
273 /**
274 * Return the status of the pixels. The ImageObserver flags
275 * representing the available pixel information are returned.
276 * @return the bitwise OR of all relevant ImageObserver flags
277 * @see ImageObserver
278 */
279 public synchronized int getStatus() {
280 return flags;
281 }
282
283 /**
284 * Get the width of the pixel buffer (after adjusting for image width).
285 * If no width was specified for the rectangle of pixels to grab then
286 * then this information will only be available after the image has
287 * delivered the dimensions.
288 * @return the final width used for the pixel buffer or -1 if the width
289 * is not yet known
290 * @see #getStatus
291 */
292 public synchronized int getWidth() {
293 return (dstW < 0) ? -1 : dstW;
294 }
295
296 /**
297 * Get the height of the pixel buffer (after adjusting for image height).
298 * If no width was specified for the rectangle of pixels to grab then
299 * then this information will only be available after the image has
300 * delivered the dimensions.
301 * @return the final height used for the pixel buffer or -1 if the height
302 * is not yet known
303 * @see #getStatus
304 */
305 public synchronized int getHeight() {
306 return (dstH < 0) ? -1 : dstH;
307 }
308
309 /**
310 * Get the pixel buffer. If the PixelGrabber was not constructed
311 * with an explicit pixel buffer to hold the pixels then this method
312 * will return null until the size and format of the image data is
313 * known.
314 * Since the PixelGrabber may fall back on accumulating the data
315 * in the default RGB ColorModel at any time if the source image
316 * uses more than one ColorModel to deliver the data, the array
317 * object returned by this method may change over time until the
318 * image grab is complete.
319 * @return either a byte array or an int array
320 * @see #getStatus
321 * @see #setPixels(int, int, int, int, ColorModel, byte[], int, int)
322 * @see #setPixels(int, int, int, int, ColorModel, int[], int, int)
323 */
324 public synchronized Object getPixels() {
325 return (bytePixels == null) ? ((Object) intPixels)
326 : ((Object) bytePixels);
327 }
328
329 /**
330 * Get the ColorModel for the pixels stored in the array. If the
331 * PixelGrabber was constructed with an explicit pixel buffer then
332 * this method will always return the default RGB ColorModel,
333 * otherwise it may return null until the ColorModel used by the
334 * ImageProducer is known.
335 * Since the PixelGrabber may fall back on accumulating the data
336 * in the default RGB ColorModel at any time if the source image
337 * uses more than one ColorModel to deliver the data, the ColorModel
338 * object returned by this method may change over time until the
339 * image grab is complete and may not reflect any of the ColorModel
340 * objects that was used by the ImageProducer to deliver the pixels.
341 * @return the ColorModel object used for storing the pixels
342 * @see #getStatus
343 * @see ColorModel#getRGBdefault
344 * @see #setColorModel(ColorModel)
345 */
346 public synchronized ColorModel getColorModel() {
347 return imageModel;
348 }
349
350 /**
351 * The setDimensions method is part of the ImageConsumer API which
352 * this class must implement to retrieve the pixels.
353 * <p>
354 * Note: This method is intended to be called by the ImageProducer
355 * of the Image whose pixels are being grabbed. Developers using
356 * this class to retrieve pixels from an image should avoid calling
357 * this method directly since that operation could result in problems
358 * with retrieving the requested pixels.
359 * @param width the width of the dimension
360 * @param height the height of the dimension
361 */
362 public void setDimensions(int width, int height) {
363 if (dstW < 0) {
364 dstW = width - dstX;
365 }
366 if (dstH < 0) {
367 dstH = height - dstY;
368 }
369 if (dstW <= 0 || dstH <= 0) {
370 imageComplete(STATICIMAGEDONE);
371 } else if (intPixels == null
372 && imageModel == ColorModel.getRGBdefault()) {
373 intPixels = new int[dstW * dstH];
374 dstScan = dstW;
375 dstOff = 0;
376 }
377 flags |= (ImageObserver.WIDTH | ImageObserver.HEIGHT);
378 }
379
380 /**
381 * The setHints method is part of the ImageConsumer API which
382 * this class must implement to retrieve the pixels.
383 * <p>
384 * Note: This method is intended to be called by the ImageProducer
385 * of the Image whose pixels are being grabbed. Developers using
386 * this class to retrieve pixels from an image should avoid calling
387 * this method directly since that operation could result in problems
388 * with retrieving the requested pixels.
389 * @param hints a set of hints used to process the pixels
390 */
391 public void setHints(int hints) {
392 return;
393 }
394
395 /**
396 * The setProperties method is part of the ImageConsumer API which
397 * this class must implement to retrieve the pixels.
398 * <p>
399 * Note: This method is intended to be called by the ImageProducer
400 * of the Image whose pixels are being grabbed. Developers using
401 * this class to retrieve pixels from an image should avoid calling
402 * this method directly since that operation could result in problems
403 * with retrieving the requested pixels.
404 * @param props the list of properties
405 */
406 public void setProperties(Hashtable<?, ?> props) {
407 return;
408 }
409
410 /**
411 * The setColorModel method is part of the ImageConsumer API which
412 * this class must implement to retrieve the pixels.
413 * <p>
414 * Note: This method is intended to be called by the ImageProducer
415 * of the Image whose pixels are being grabbed. Developers using
416 * this class to retrieve pixels from an image should avoid calling
417 * this method directly since that operation could result in problems
418 * with retrieving the requested pixels.
419 * @param model the specified <code>ColorModel</code>
420 * @see #getColorModel
421 */
422 public void setColorModel(ColorModel model) {
423 return;
424 }
425
426 private void convertToRGB() {
427 int size = dstW * dstH;
428 int newpixels[] = new int[size];
429 if (bytePixels != null) {
430 for (int i = 0; i < size; i++) {
431 newpixels[i] = imageModel.getRGB(bytePixels[i] & 0xff);
432 }
433 } else if (intPixels != null) {
434 for (int i = 0; i < size; i++) {
435 newpixels[i] = imageModel.getRGB(intPixels[i]);
436 }
437 }
438 bytePixels = null;
439 intPixels = newpixels;
440 dstScan = dstW;
441 dstOff = 0;
442 imageModel = ColorModel.getRGBdefault();
443 }
444
445 /**
446 * The setPixels method is part of the ImageConsumer API which
447 * this class must implement to retrieve the pixels.
448 * <p>
449 * Note: This method is intended to be called by the ImageProducer
450 * of the Image whose pixels are being grabbed. Developers using
451 * this class to retrieve pixels from an image should avoid calling
452 * this method directly since that operation could result in problems
453 * with retrieving the requested pixels.
454 * @param srcX the X coordinate of the upper-left corner
455 * of the area of pixels to be set
456 * @param srcY the Y coordinate of the upper-left corner
457 * of the area of pixels to be set
458 * @param srcW the width of the area of pixels
459 * @param srcH the height of the area of pixels
460 * @param model the specified <code>ColorModel</code>
461 * @param pixels the array of pixels
462 * @param srcOff the offset into the pixels array
463 * @param srcScan the distance from one row of pixels to the next
464 * in the pixels array
465 * @see #getPixels
466 */
467 public void setPixels(int srcX, int srcY, int srcW, int srcH,
468 ColorModel model, byte pixels[], int srcOff, int srcScan) {
469 if (srcY < dstY) {
470 int diff = dstY - srcY;
471 if (diff >= srcH) {
472 return;
473 }
474 srcOff += srcScan * diff;
475 srcY += diff;
476 srcH -= diff;
477 }
478 if (srcY + srcH > dstY + dstH) {
479 srcH = (dstY + dstH) - srcY;
480 if (srcH <= 0) {
481 return;
482 }
483 }
484 if (srcX < dstX) {
485 int diff = dstX - srcX;
486 if (diff >= srcW) {
487 return;
488 }
489 srcOff += diff;
490 srcX += diff;
491 srcW -= diff;
492 }
493 if (srcX + srcW > dstX + dstW) {
494 srcW = (dstX + dstW) - srcX;
495 if (srcW <= 0) {
496 return;
497 }
498 }
499 int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX);
500 if (intPixels == null) {
501 if (bytePixels == null) {
502 bytePixels = new byte[dstW * dstH];
503 dstScan = dstW;
504 dstOff = 0;
505 imageModel = model;
506 } else if (imageModel != model) {
507 convertToRGB();
508 }
509 if (bytePixels != null) {
510 for (int h = srcH; h > 0; h--) {
511 System.arraycopy(pixels, srcOff, bytePixels,
512 dstPtr, srcW);
513 srcOff += srcScan;
514 dstPtr += dstScan;
515 }
516 }
517 }
518 if (intPixels != null) {
519 int dstRem = dstScan - srcW;
520 int srcRem = srcScan - srcW;
521 for (int h = srcH; h > 0; h--) {
522 for (int w = srcW; w > 0; w--) {
523 intPixels[dstPtr++] = model
524 .getRGB(pixels[srcOff++] & 0xff);
525 }
526 srcOff += srcRem;
527 dstPtr += dstRem;
528 }
529 }
530 flags |= ImageObserver.SOMEBITS;
531 }
532
533 /**
534 * The setPixels method is part of the ImageConsumer API which
535 * this class must implement to retrieve the pixels.
536 * <p>
537 * Note: This method is intended to be called by the ImageProducer
538 * of the Image whose pixels are being grabbed. Developers using
539 * this class to retrieve pixels from an image should avoid calling
540 * this method directly since that operation could result in problems
541 * with retrieving the requested pixels.
542 * @param srcX the X coordinate of the upper-left corner
543 * of the area of pixels to be set
544 * @param srcY the Y coordinate of the upper-left corner
545 * of the area of pixels to be set
546 * @param srcW the width of the area of pixels
547 * @param srcH the height of the area of pixels
548 * @param model the specified <code>ColorModel</code>
549 * @param pixels the array of pixels
550 * @param srcOff the offset into the pixels array
551 * @param srcScan the distance from one row of pixels to the next
552 * in the pixels array
553 * @see #getPixels
554 */
555 public void setPixels(int srcX, int srcY, int srcW, int srcH,
556 ColorModel model, int pixels[], int srcOff, int srcScan) {
557 if (srcY < dstY) {
558 int diff = dstY - srcY;
559 if (diff >= srcH) {
560 return;
561 }
562 srcOff += srcScan * diff;
563 srcY += diff;
564 srcH -= diff;
565 }
566 if (srcY + srcH > dstY + dstH) {
567 srcH = (dstY + dstH) - srcY;
568 if (srcH <= 0) {
569 return;
570 }
571 }
572 if (srcX < dstX) {
573 int diff = dstX - srcX;
574 if (diff >= srcW) {
575 return;
576 }
577 srcOff += diff;
578 srcX += diff;
579 srcW -= diff;
580 }
581 if (srcX + srcW > dstX + dstW) {
582 srcW = (dstX + dstW) - srcX;
583 if (srcW <= 0) {
584 return;
585 }
586 }
587 if (intPixels == null) {
588 if (bytePixels == null) {
589 intPixels = new int[dstW * dstH];
590 dstScan = dstW;
591 dstOff = 0;
592 imageModel = model;
593 } else {
594 convertToRGB();
595 }
596 }
597 int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX);
598 if (imageModel == model) {
599 for (int h = srcH; h > 0; h--) {
600 System.arraycopy(pixels, srcOff, intPixels, dstPtr,
601 srcW);
602 srcOff += srcScan;
603 dstPtr += dstScan;
604 }
605 } else {
606 if (imageModel != ColorModel.getRGBdefault()) {
607 convertToRGB();
608 }
609 int dstRem = dstScan - srcW;
610 int srcRem = srcScan - srcW;
611 for (int h = srcH; h > 0; h--) {
612 for (int w = srcW; w > 0; w--) {
613 intPixels[dstPtr++] = model
614 .getRGB(pixels[srcOff++]);
615 }
616 srcOff += srcRem;
617 dstPtr += dstRem;
618 }
619 }
620 flags |= ImageObserver.SOMEBITS;
621 }
622
623 /**
624 * The imageComplete method is part of the ImageConsumer API which
625 * this class must implement to retrieve the pixels.
626 * <p>
627 * Note: This method is intended to be called by the ImageProducer
628 * of the Image whose pixels are being grabbed. Developers using
629 * this class to retrieve pixels from an image should avoid calling
630 * this method directly since that operation could result in problems
631 * with retrieving the requested pixels.
632 * @param status the status of image loading
633 */
634 public synchronized void imageComplete(int status) {
635 grabbing = false;
636 switch (status) {
637 default:
638 case IMAGEERROR:
639 flags |= ImageObserver.ERROR | ImageObserver.ABORT;
640 break;
641 case IMAGEABORTED:
642 flags |= ImageObserver.ABORT;
643 break;
644 case STATICIMAGEDONE:
645 flags |= ImageObserver.ALLBITS;
646 break;
647 case SINGLEFRAMEDONE:
648 flags |= ImageObserver.FRAMEBITS;
649 break;
650 }
651 producer.removeConsumer(this );
652 notifyAll();
653 }
654
655 /**
656 * Returns the status of the pixels. The ImageObserver flags
657 * representing the available pixel information are returned.
658 * This method and {@link #getStatus() getStatus} have the
659 * same implementation, but <code>getStatus</code> is the
660 * preferred method because it conforms to the convention of
661 * naming information-retrieval methods with the form
662 * "getXXX".
663 * @return the bitwise OR of all relevant ImageObserver flags
664 * @see ImageObserver
665 * @see #getStatus()
666 */
667 public synchronized int status() {
668 return flags;
669 }
670 }
|