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 * {@code pix[(j - y) * scansize + (i - x) + off]}.
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 * {@code pix[(j - y) * scansize + (i - x) + off]}.
131 * @param ip the {@code ImageProducer} 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}:
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} if all pixels
240 * are grabbed, {@code false} 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}
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}
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}
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} 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 }
673