View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.awt.image;
27  
28  import java.util.Hashtable;
29  import java.awt.image.ImageProducer;
30  import java.awt.image.ImageConsumer;
31  import java.awt.image.ColorModel;
32  import java.awt.Image;
33  
34  /**
35   * The PixelGrabber class implements an ImageConsumer which can be attached
36   * to an Image or ImageProducer object to retrieve a subset of the pixels
37   * in that image.  Here is an example:
38   * <pre>{@code
39   *
40   * public void handlesinglepixel(int x, int y, int pixel) {
41   *      int alpha = (pixel >> 24) & 0xff;
42   *      int red   = (pixel >> 16) & 0xff;
43   *      int green = (pixel >>  8) & 0xff;
44   *      int blue  = (pixel      ) & 0xff;
45   *      // Deal with the pixel as necessary...
46   * }
47   *
48   * public void handlepixels(Image img, int x, int y, int w, int h) {
49   *      int[] pixels = new int[w * h];
50   *      PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w);
51   *      try {
52   *          pg.grabPixels();
53   *      } catch (InterruptedException e) {
54   *          System.err.println("interrupted waiting for pixels!");
55   *          return;
56   *      }
57   *      if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
58   *          System.err.println("image fetch aborted or errored");
59   *          return;
60   *      }
61   *      for (int j = 0; j < h; j++) {
62   *          for (int i = 0; i < w; i++) {
63   *              handlesinglepixel(x+i, y+j, pixels[j * w + i]);
64   *          }
65   *      }
66   * }
67   *
68   * }</pre>
69   *
70   * @see ColorModel#getRGBdefault
71   *
72   * @author      Jim Graham
73   */
74  public class PixelGrabber implements ImageConsumer {
75      ImageProducer producer;
76  
77      int dstX;
78      int dstY;
79      int dstW;
80      int dstH;
81  
82      ColorModel imageModel;
83      byte[] bytePixels;
84      int[] intPixels;
85      int dstOff;
86      int dstScan;
87  
88      private boolean grabbing;
89      private int flags;
90  
91      private static final int GRABBEDBITS = (ImageObserver.FRAMEBITS
92                                              | ImageObserver.ALLBITS);
93      private static final int DONEBITS = (GRABBEDBITS
94                                           | ImageObserver.ERROR);
95  
96      /**
97       * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
98       * section of pixels from the specified image into the given array.
99       * The pixels are stored into the array in the default RGB ColorModel.
100      * The RGB data for pixel (i, j) where (i, j) is inside the rectangle
101      * (x, y, w, h) is stored in the array at
102      * <tt>pix[(j - y) * scansize + (i - x) + off]</tt>.
103      * @see ColorModel#getRGBdefault
104      * @param img the image to retrieve pixels from
105      * @param x the x coordinate of the upper left corner of the rectangle
106      * of pixels to retrieve from the image, relative to the default
107      * (unscaled) size of the image
108      * @param y the y coordinate of the upper left corner of the rectangle
109      * of pixels to retrieve from the image
110      * @param w the width of the rectangle of pixels to retrieve
111      * @param h the height of the rectangle of pixels to retrieve
112      * @param pix the array of integers which are to be used to hold the
113      * RGB pixels retrieved from the image
114      * @param off the offset into the array of where to store the first pixel
115      * @param scansize the distance from one row of pixels to the next in
116      * the array
117      */
118     public PixelGrabber(Image img, int x, int y, int w, int h,
119                         int[] pix, int off, int scansize) {
120         this(img.getSource(), x, y, w, h, pix, off, scansize);
121     }
122 
123     /**
124      * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
125      * section of pixels from the image produced by the specified
126      * ImageProducer into the given array.
127      * The pixels are stored into the array in the default RGB ColorModel.
128      * The RGB data for pixel (i, j) where (i, j) is inside the rectangle
129      * (x, y, w, h) is stored in the array at
130      * <tt>pix[(j - y) * scansize + (i - x) + off]</tt>.
131      * @param ip the <code>ImageProducer</code> that produces the
132      * image from which to retrieve pixels
133      * @param x the x coordinate of the upper left corner of the rectangle
134      * of pixels to retrieve from the image, relative to the default
135      * (unscaled) size of the image
136      * @param y the y coordinate of the upper left corner of the rectangle
137      * of pixels to retrieve from the image
138      * @param w the width of the rectangle of pixels to retrieve
139      * @param h the height of the rectangle of pixels to retrieve
140      * @param pix the array of integers which are to be used to hold the
141      * RGB pixels retrieved from the image
142      * @param off the offset into the array of where to store the first pixel
143      * @param scansize the distance from one row of pixels to the next in
144      * the array
145      * @see ColorModel#getRGBdefault
146      */
147     public PixelGrabber(ImageProducer ip, int x, int y, int w, int h,
148                         int[] pix, int off, int scansize) {
149         producer = ip;
150         dstX = x;
151         dstY = y;
152         dstW = w;
153         dstH = h;
154         dstOff = off;
155         dstScan = scansize;
156         intPixels = pix;
157         imageModel = ColorModel.getRGBdefault();
158     }
159 
160     /**
161      * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
162      * section of pixels from the specified image.  The pixels are
163      * accumulated in the original ColorModel if the same ColorModel
164      * is used for every call to setPixels, otherwise the pixels are
165      * accumulated in the default RGB ColorModel.  If the forceRGB
166      * parameter is true, then the pixels will be accumulated in the
167      * default RGB ColorModel anyway.  A buffer is allocated by the
168      * PixelGrabber to hold the pixels in either case.  If {@code (w < 0)} or
169      * {@code (h < 0)}, then they will default to the remaining width and
170      * height of the source data when that information is delivered.
171      * @param img the image to retrieve the image data from
172      * @param x the x coordinate of the upper left corner of the rectangle
173      * of pixels to retrieve from the image, relative to the default
174      * (unscaled) size of the image
175      * @param y the y coordinate of the upper left corner of the rectangle
176      * of pixels to retrieve from the image
177      * @param w the width of the rectangle of pixels to retrieve
178      * @param h the height of the rectangle of pixels to retrieve
179      * @param forceRGB true if the pixels should always be converted to
180      * the default RGB ColorModel
181      */
182     public PixelGrabber(Image img, int x, int y, int w, int h,
183                         boolean forceRGB)
184     {
185         producer = img.getSource();
186         dstX = x;
187         dstY = y;
188         dstW = w;
189         dstH = h;
190         if (forceRGB) {
191             imageModel = ColorModel.getRGBdefault();
192         }
193     }
194 
195     /**
196      * Request the PixelGrabber to start fetching the pixels.
197      */
198     public synchronized void startGrabbing() {
199         if ((flags & DONEBITS) != 0) {
200             return;
201         }
202         if (!grabbing) {
203             grabbing = true;
204             flags &= ~(ImageObserver.ABORT);
205             producer.startProduction(this);
206         }
207     }
208 
209     /**
210      * Request the PixelGrabber to abort the image fetch.
211      */
212     public synchronized void abortGrabbing() {
213         imageComplete(IMAGEABORTED);
214     }
215 
216     /**
217      * Request the Image or ImageProducer to start delivering pixels and
218      * wait for all of the pixels in the rectangle of interest to be
219      * delivered.
220      * @return true if the pixels were successfully grabbed, false on
221      * abort, error or timeout
222      * @exception InterruptedException
223      *            Another thread has interrupted this thread.
224      */
225     public boolean grabPixels() throws InterruptedException {
226         return grabPixels(0);
227     }
228 
229     /**
230      * Request the Image or ImageProducer to start delivering pixels and
231      * wait for all of the pixels in the rectangle of interest to be
232      * delivered or until the specified timeout has elapsed.  This method
233      * behaves in the following ways, depending on the value of
234      * <code>ms</code>:
235      * <ul>
236      * <li> If {@code ms == 0}, waits until all pixels are delivered
237      * <li> If {@code ms > 0}, waits until all pixels are delivered
238      * as timeout expires.
239      * <li> If {@code ms < 0}, returns <code>true</code> if all pixels
240      * are grabbed, <code>false</code> otherwise and does not wait.
241      * </ul>
242      * @param ms the number of milliseconds to wait for the image pixels
243      * to arrive before timing out
244      * @return true if the pixels were successfully grabbed, false on
245      * abort, error or timeout
246      * @exception InterruptedException
247      *            Another thread has interrupted this thread.
248      */
249     public synchronized boolean grabPixels(long ms)
250         throws InterruptedException
251     {
252         if ((flags & DONEBITS) != 0) {
253             return (flags & GRABBEDBITS) != 0;
254         }
255         long end = ms + System.currentTimeMillis();
256         if (!grabbing) {
257             grabbing = true;
258             flags &= ~(ImageObserver.ABORT);
259             producer.startProduction(this);
260         }
261         while (grabbing) {
262             long timeout;
263             if (ms == 0) {
264                 timeout = 0;
265             } else {
266                 timeout = end - System.currentTimeMillis();
267                 if (timeout <= 0) {
268                     break;
269                 }
270             }
271             wait(timeout);
272         }
273         return (flags & GRABBEDBITS) != 0;
274     }
275 
276     /**
277      * Return the status of the pixels.  The ImageObserver flags
278      * representing the available pixel information are returned.
279      * @return the bitwise OR of all relevant ImageObserver flags
280      * @see ImageObserver
281      */
282     public synchronized int getStatus() {
283         return flags;
284     }
285 
286     /**
287      * Get the width of the pixel buffer (after adjusting for image width).
288      * If no width was specified for the rectangle of pixels to grab then
289      * then this information will only be available after the image has
290      * delivered the dimensions.
291      * @return the final width used for the pixel buffer or -1 if the width
292      * is not yet known
293      * @see #getStatus
294      */
295     public synchronized int getWidth() {
296         return (dstW < 0) ? -1 : dstW;
297     }
298 
299     /**
300      * Get the height of the pixel buffer (after adjusting for image height).
301      * If no width was specified for the rectangle of pixels to grab then
302      * then this information will only be available after the image has
303      * delivered the dimensions.
304      * @return the final height used for the pixel buffer or -1 if the height
305      * is not yet known
306      * @see #getStatus
307      */
308     public synchronized int getHeight() {
309         return (dstH < 0) ? -1 : dstH;
310     }
311 
312     /**
313      * Get the pixel buffer.  If the PixelGrabber was not constructed
314      * with an explicit pixel buffer to hold the pixels then this method
315      * will return null until the size and format of the image data is
316      * known.
317      * Since the PixelGrabber may fall back on accumulating the data
318      * in the default RGB ColorModel at any time if the source image
319      * uses more than one ColorModel to deliver the data, the array
320      * object returned by this method may change over time until the
321      * image grab is complete.
322      * @return either a byte array or an int array
323      * @see #getStatus
324      * @see #setPixels(int, int, int, int, ColorModel, byte[], int, int)
325      * @see #setPixels(int, int, int, int, ColorModel, int[], int, int)
326      */
327     public synchronized Object getPixels() {
328         return (bytePixels == null)
329             ? ((Object) intPixels)
330             : ((Object) bytePixels);
331     }
332 
333     /**
334      * Get the ColorModel for the pixels stored in the array.  If the
335      * PixelGrabber was constructed with an explicit pixel buffer then
336      * this method will always return the default RGB ColorModel,
337      * otherwise it may return null until the ColorModel used by the
338      * ImageProducer is known.
339      * Since the PixelGrabber may fall back on accumulating the data
340      * in the default RGB ColorModel at any time if the source image
341      * uses more than one ColorModel to deliver the data, the ColorModel
342      * object returned by this method may change over time until the
343      * image grab is complete and may not reflect any of the ColorModel
344      * objects that was used by the ImageProducer to deliver the pixels.
345      * @return the ColorModel object used for storing the pixels
346      * @see #getStatus
347      * @see ColorModel#getRGBdefault
348      * @see #setColorModel(ColorModel)
349      */
350     public synchronized ColorModel getColorModel() {
351         return imageModel;
352     }
353 
354     /**
355      * The setDimensions method is part of the ImageConsumer API which
356      * this class must implement to retrieve the pixels.
357      * <p>
358      * Note: This method is intended to be called by the ImageProducer
359      * of the Image whose pixels are being grabbed.  Developers using
360      * this class to retrieve pixels from an image should avoid calling
361      * this method directly since that operation could result in problems
362      * with retrieving the requested pixels.
363      * @param width the width of the dimension
364      * @param height the height of the dimension
365      */
366     public void setDimensions(int width, int height) {
367         if (dstW < 0) {
368             dstW = width - dstX;
369         }
370         if (dstH < 0) {
371             dstH = height - dstY;
372         }
373         if (dstW <= 0 || dstH <= 0) {
374             imageComplete(STATICIMAGEDONE);
375         } else if (intPixels == null &&
376                    imageModel == ColorModel.getRGBdefault()) {
377             intPixels = new int[dstW * dstH];
378             dstScan = dstW;
379             dstOff = 0;
380         }
381         flags |= (ImageObserver.WIDTH | ImageObserver.HEIGHT);
382     }
383 
384     /**
385      * The setHints method is part of the ImageConsumer API which
386      * this class must implement to retrieve the pixels.
387      * <p>
388      * Note: This method is intended to be called by the ImageProducer
389      * of the Image whose pixels are being grabbed.  Developers using
390      * this class to retrieve pixels from an image should avoid calling
391      * this method directly since that operation could result in problems
392      * with retrieving the requested pixels.
393      * @param hints a set of hints used to process the pixels
394      */
395     public void setHints(int hints) {
396         return;
397     }
398 
399     /**
400      * The setProperties method is part of the ImageConsumer API which
401      * this class must implement to retrieve the pixels.
402      * <p>
403      * Note: This method is intended to be called by the ImageProducer
404      * of the Image whose pixels are being grabbed.  Developers using
405      * this class to retrieve pixels from an image should avoid calling
406      * this method directly since that operation could result in problems
407      * with retrieving the requested pixels.
408      * @param props the list of properties
409      */
410     public void setProperties(Hashtable<?,?> props) {
411         return;
412     }
413 
414     /**
415      * The setColorModel method is part of the ImageConsumer API which
416      * this class must implement to retrieve the pixels.
417      * <p>
418      * Note: This method is intended to be called by the ImageProducer
419      * of the Image whose pixels are being grabbed.  Developers using
420      * this class to retrieve pixels from an image should avoid calling
421      * this method directly since that operation could result in problems
422      * with retrieving the requested pixels.
423      * @param model the specified <code>ColorModel</code>
424      * @see #getColorModel
425      */
426     public void setColorModel(ColorModel model) {
427         return;
428     }
429 
430     private void convertToRGB() {
431         int size = dstW * dstH;
432         int newpixels[] = new int[size];
433         if (bytePixels != null) {
434             for (int i = 0; i < size; i++) {
435                 newpixels[i] = imageModel.getRGB(bytePixels[i] & 0xff);
436             }
437         } else if (intPixels != null) {
438             for (int i = 0; i < size; i++) {
439                 newpixels[i] = imageModel.getRGB(intPixels[i]);
440             }
441         }
442         bytePixels = null;
443         intPixels = newpixels;
444         dstScan = dstW;
445         dstOff = 0;
446         imageModel = ColorModel.getRGBdefault();
447     }
448 
449     /**
450      * The setPixels method is part of the ImageConsumer API which
451      * this class must implement to retrieve the pixels.
452      * <p>
453      * Note: This method is intended to be called by the ImageProducer
454      * of the Image whose pixels are being grabbed.  Developers using
455      * this class to retrieve pixels from an image should avoid calling
456      * this method directly since that operation could result in problems
457      * with retrieving the requested pixels.
458      * @param srcX the X coordinate of the upper-left corner
459      *        of the area of pixels to be set
460      * @param srcY the Y coordinate of the upper-left corner
461      *        of the area of pixels to be set
462      * @param srcW the width of the area of pixels
463      * @param srcH the height of the area of pixels
464      * @param model the specified <code>ColorModel</code>
465      * @param pixels the array of pixels
466      * @param srcOff the offset into the pixels array
467      * @param srcScan the distance from one row of pixels to the next
468      *        in the pixels array
469      * @see #getPixels
470      */
471     public void setPixels(int srcX, int srcY, int srcW, int srcH,
472                           ColorModel model,
473                           byte pixels[], int srcOff, int srcScan) {
474         if (srcY < dstY) {
475             int diff = dstY - srcY;
476             if (diff >= srcH) {
477                 return;
478             }
479             srcOff += srcScan * diff;
480             srcY += diff;
481             srcH -= diff;
482         }
483         if (srcY + srcH > dstY + dstH) {
484             srcH = (dstY + dstH) - srcY;
485             if (srcH <= 0) {
486                 return;
487             }
488         }
489         if (srcX < dstX) {
490             int diff = dstX - srcX;
491             if (diff >= srcW) {
492                 return;
493             }
494             srcOff += diff;
495             srcX += diff;
496             srcW -= diff;
497         }
498         if (srcX + srcW > dstX + dstW) {
499             srcW = (dstX + dstW) - srcX;
500             if (srcW <= 0) {
501                 return;
502             }
503         }
504         int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX);
505         if (intPixels == null) {
506             if (bytePixels == null) {
507                 bytePixels = new byte[dstW * dstH];
508                 dstScan = dstW;
509                 dstOff = 0;
510                 imageModel = model;
511             } else if (imageModel != model) {
512                 convertToRGB();
513             }
514             if (bytePixels != null) {
515                 for (int h = srcH; h > 0; h--) {
516                     System.arraycopy(pixels, srcOff, bytePixels, dstPtr, srcW);
517                     srcOff += srcScan;
518                     dstPtr += dstScan;
519                 }
520             }
521         }
522         if (intPixels != null) {
523             int dstRem = dstScan - srcW;
524             int srcRem = srcScan - srcW;
525             for (int h = srcH; h > 0; h--) {
526                 for (int w = srcW; w > 0; w--) {
527                     intPixels[dstPtr++] = model.getRGB(pixels[srcOff++]&0xff);
528                 }
529                 srcOff += srcRem;
530                 dstPtr += dstRem;
531             }
532         }
533         flags |= ImageObserver.SOMEBITS;
534     }
535 
536     /**
537      * The setPixels method is part of the ImageConsumer API which
538      * this class must implement to retrieve the pixels.
539      * <p>
540      * Note: This method is intended to be called by the ImageProducer
541      * of the Image whose pixels are being grabbed.  Developers using
542      * this class to retrieve pixels from an image should avoid calling
543      * this method directly since that operation could result in problems
544      * with retrieving the requested pixels.
545      * @param srcX the X coordinate of the upper-left corner
546      *        of the area of pixels to be set
547      * @param srcY the Y coordinate of the upper-left corner
548      *        of the area of pixels to be set
549      * @param srcW the width of the area of pixels
550      * @param srcH the height of the area of pixels
551      * @param model the specified <code>ColorModel</code>
552      * @param pixels the array of pixels
553      * @param srcOff the offset into the pixels array
554      * @param srcScan the distance from one row of pixels to the next
555      *        in the pixels array
556      * @see #getPixels
557      */
558     public void setPixels(int srcX, int srcY, int srcW, int srcH,
559                           ColorModel model,
560                           int pixels[], int srcOff, int srcScan) {
561         if (srcY < dstY) {
562             int diff = dstY - srcY;
563             if (diff >= srcH) {
564                 return;
565             }
566             srcOff += srcScan * diff;
567             srcY += diff;
568             srcH -= diff;
569         }
570         if (srcY + srcH > dstY + dstH) {
571             srcH = (dstY + dstH) - srcY;
572             if (srcH <= 0) {
573                 return;
574             }
575         }
576         if (srcX < dstX) {
577             int diff = dstX - srcX;
578             if (diff >= srcW) {
579                 return;
580             }
581             srcOff += diff;
582             srcX += diff;
583             srcW -= diff;
584         }
585         if (srcX + srcW > dstX + dstW) {
586             srcW = (dstX + dstW) - srcX;
587             if (srcW <= 0) {
588                 return;
589             }
590         }
591         if (intPixels == null) {
592             if (bytePixels == null) {
593                 intPixels = new int[dstW * dstH];
594                 dstScan = dstW;
595                 dstOff = 0;
596                 imageModel = model;
597             } else {
598                 convertToRGB();
599             }
600         }
601         int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX);
602         if (imageModel == model) {
603             for (int h = srcH; h > 0; h--) {
604                 System.arraycopy(pixels, srcOff, intPixels, dstPtr, srcW);
605                 srcOff += srcScan;
606                 dstPtr += dstScan;
607             }
608         } else {
609             if (imageModel != ColorModel.getRGBdefault()) {
610                 convertToRGB();
611             }
612             int dstRem = dstScan - srcW;
613             int srcRem = srcScan - srcW;
614             for (int h = srcH; h > 0; h--) {
615                 for (int w = srcW; w > 0; w--) {
616                     intPixels[dstPtr++] = model.getRGB(pixels[srcOff++]);
617                 }
618                 srcOff += srcRem;
619                 dstPtr += dstRem;
620             }
621         }
622         flags |= ImageObserver.SOMEBITS;
623     }
624 
625     /**
626      * The imageComplete method is part of the ImageConsumer API which
627      * this class must implement to retrieve the pixels.
628      * <p>
629      * Note: This method is intended to be called by the ImageProducer
630      * of the Image whose pixels are being grabbed.  Developers using
631      * this class to retrieve pixels from an image should avoid calling
632      * this method directly since that operation could result in problems
633      * with retrieving the requested pixels.
634      * @param status the status of image loading
635      */
636     public synchronized void imageComplete(int status) {
637         grabbing = false;
638         switch (status) {
639         default:
640         case IMAGEERROR:
641             flags |= ImageObserver.ERROR | ImageObserver.ABORT;
642             break;
643         case IMAGEABORTED:
644             flags |= ImageObserver.ABORT;
645             break;
646         case STATICIMAGEDONE:
647             flags |= ImageObserver.ALLBITS;
648             break;
649         case SINGLEFRAMEDONE:
650             flags |= ImageObserver.FRAMEBITS;
651             break;
652         }
653         producer.removeConsumer(this);
654         notifyAll();
655     }
656 
657     /**
658      * Returns the status of the pixels.  The ImageObserver flags
659      * representing the available pixel information are returned.
660      * This method and {@link #getStatus() getStatus} have the
661      * same implementation, but <code>getStatus</code> is the
662      * preferred method because it conforms to the convention of
663      * naming information-retrieval methods with the form
664      * "getXXX".
665      * @return the bitwise OR of all relevant ImageObserver flags
666      * @see ImageObserver
667      * @see #getStatus()
668      */
669     public synchronized int status() {
670         return flags;
671     }
672 }