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