| /* PixelGrabber.java -- retrieve a subset of an image's data | |
| Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.awt.image; | |
| import java.awt.Image; | |
| import java.util.Hashtable; | |
| /** | |
| * PixelGrabber is an ImageConsumer that extracts a rectangular region | |
| * of pixels from an Image. | |
| */ | |
| public class PixelGrabber implements ImageConsumer | |
| { | |
| int x, y, offset; | |
| int width = -1; | |
| int height = -1; | |
| int scansize = -1; | |
| boolean forceRGB = true; | |
| ColorModel model = ColorModel.getRGBdefault(); | |
| int hints; | |
| Hashtable props; | |
| int int_pixel_buffer[]; | |
| boolean ints_delivered = false; | |
| byte byte_pixel_buffer[]; | |
| boolean bytes_delivered = false; | |
| ImageProducer ip; | |
| int observerStatus; | |
| int consumerStatus; | |
| private Thread grabberThread; | |
| boolean grabbing = false; | |
| /** | |
| * Construct a PixelGrabber that will retrieve RGB data from a given | |
| * Image. | |
| * | |
| * The RGB data will be retrieved from a rectangular region | |
| * <code>(x, y, w, h)</code> within the image. The data will be | |
| * stored in the provided <code>pix</code> array, which must have | |
| * been initialized to a size of at least <code>w * h</code>. The | |
| * data for a pixel (m, n) in the grab rectangle will be stored at | |
| * <code>pix[(n - y) * scansize + (m - x) + off]</code>. | |
| * | |
| * @param img the Image from which to grab pixels | |
| * @param x the x coordinate, relative to <code>img</code>'s | |
| * top-left corner, of the grab rectangle's top-left pixel | |
| * @param y the y coordinate, relative to <code>img</code>'s | |
| * top-left corner, of the grab rectangle's top-left pixel | |
| * @param w the width of the grab rectangle, in pixels | |
| * @param h the height of the grab rectangle, in pixels | |
| * @param pix the array in which to store grabbed RGB pixel data | |
| * @param off the offset into the <code>pix</code> array at which to | |
| * start storing RGB data | |
| * @param scansize a set of <code>scansize</code> consecutive | |
| * elements in the <code>pix</code> array represents one row of | |
| * pixels in the grab rectangle | |
| */ | |
| public PixelGrabber(Image img, int x, int y, int w, int h, | |
| int pix[], int off, int scansize) | |
| { | |
| this (img.getSource(), x, y, w, h, pix, off, scansize); | |
| } | |
| /** | |
| * Construct a PixelGrabber that will retrieve RGB data from a given | |
| * ImageProducer. | |
| * | |
| * The RGB data will be retrieved from a rectangular region | |
| * <code>(x, y, w, h)</code> within the image produced by | |
| * <code>ip</code>. The data will be stored in the provided | |
| * <code>pix</code> array, which must have been initialized to a | |
| * size of at least <code>w * h</code>. The data for a pixel (m, n) | |
| * in the grab rectangle will be stored at | |
| * <code>pix[(n - y) * scansize + (m - x) + off]</code>. | |
| * | |
| * @param ip the ImageProducer from which to grab pixels. This can | |
| * be null. | |
| * @param x the x coordinate of the grab rectangle's top-left pixel, | |
| * specified relative to the top-left corner of the image produced | |
| * by <code>ip</code> | |
| * @param y the y coordinate of the grab rectangle's top-left pixel, | |
| * specified relative to the top-left corner of the image produced | |
| * by <code>ip</code> | |
| * @param w the width of the grab rectangle, in pixels | |
| * @param h the height of the grab rectangle, in pixels | |
| * @param pix the array in which to store grabbed RGB pixel data | |
| * @param off the offset into the <code>pix</code> array at which to | |
| * start storing RGB data | |
| * @param scansize a set of <code>scansize</code> consecutive | |
| * elements in the <code>pix</code> array represents one row of | |
| * pixels in the grab rectangle | |
| */ | |
| public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, | |
| int pix[], int off, int scansize) | |
| { | |
| this.ip = ip; | |
| this.x = x; | |
| this.y = y; | |
| this.width = w; | |
| this.height = h; | |
| this.offset = off; | |
| this.scansize = scansize; | |
| int_pixel_buffer = pix; | |
| // Initialize the byte array in case ip sends us byte-formatted | |
| // pixel data. | |
| byte_pixel_buffer = new byte[pix.length * 4]; | |
| } | |
| /** | |
| * Construct a PixelGrabber that will retrieve data from a given | |
| * Image. | |
| * | |
| * The RGB data will be retrieved from a rectangular region | |
| * <code>(x, y, w, h)</code> within the image. The data will be | |
| * stored in an internal array which can be accessed by calling | |
| * <code>getPixels</code>. The data for a pixel (m, n) in the grab | |
| * rectangle will be stored in the returned array at index | |
| * <code>(n - y) * scansize + (m - x) + off</code>. | |
| * If forceRGB is false, then the returned data will be not be | |
| * converted to RGB from its format in <code>img</code>. | |
| * | |
| * If <code>w</code> is negative, the width of the grab region will | |
| * be from x to the right edge of the image. Likewise, if | |
| * <code>h</code> is negative, the height of the grab region will be | |
| * from y to the bottom edge of the image. | |
| * | |
| * @param img the Image from which to grab pixels | |
| * @param x the x coordinate, relative to <code>img</code>'s | |
| * top-left corner, of the grab rectangle's top-left pixel | |
| * @param y the y coordinate, relative to <code>img</code>'s | |
| * top-left corner, of the grab rectangle's top-left pixel | |
| * @param w the width of the grab rectangle, in pixels | |
| * @param h the height of the grab rectangle, in pixels | |
| * @param forceRGB true to force conversion of the rectangular | |
| * region's pixel data to RGB | |
| */ | |
| public PixelGrabber(Image img, | |
| int x, int y, | |
| int w, int h, | |
| boolean forceRGB) | |
| { | |
| this.ip = img.getSource(); | |
| if (this.ip == null) | |
| throw new NullPointerException("The ImageProducer must not be null."); | |
| this.x = x; | |
| this.y = y; | |
| width = w; | |
| height = h; | |
| // If width or height is negative, postpone pixel buffer | |
| // initialization until setDimensions is called back by ip. | |
| if (width >= 0 && height >= 0) | |
| { | |
| int_pixel_buffer = new int[width * height]; | |
| byte_pixel_buffer = new byte[width * height]; | |
| } | |
| this.forceRGB = forceRGB; | |
| } | |
| /** | |
| * Start grabbing pixels. | |
| * | |
| * Spawns an image production thread that calls back to this | |
| * PixelGrabber's ImageConsumer methods. | |
| */ | |
| public synchronized void startGrabbing() | |
| { | |
| // Make sure we're not already grabbing. | |
| if (grabbing == false) | |
| { | |
| grabbing = true; | |
| grabberThread = new Thread () | |
| { | |
| public void run () | |
| { | |
| try | |
| { | |
| ip.startProduction (PixelGrabber.this); | |
| } | |
| catch (Exception ex) | |
| { | |
| imageComplete(ImageConsumer.IMAGEABORTED); | |
| } | |
| } | |
| }; | |
| grabberThread.start (); | |
| } | |
| } | |
| /** | |
| * Abort pixel grabbing. | |
| */ | |
| public synchronized void abortGrabbing() | |
| { | |
| if (grabbing) | |
| { | |
| // Interrupt the grabbing thread. | |
| Thread moribund = grabberThread; | |
| grabberThread = null; | |
| moribund.interrupt(); | |
| imageComplete (ImageConsumer.IMAGEABORTED); | |
| } | |
| } | |
| /** | |
| * Have our Image or ImageProducer start sending us pixels via our | |
| * ImageConsumer methods and wait for all pixels in the grab | |
| * rectangle to be delivered. | |
| * | |
| * @return true if successful, false on abort or error | |
| * | |
| * @throws InterruptedException if interrupted by another thread. | |
| */ | |
| public synchronized boolean grabPixels() throws InterruptedException | |
| { | |
| return grabPixels(0); | |
| } | |
| /** | |
| * grabPixels's behavior depends on the value of <code>ms</code>. | |
| * | |
| * If ms < 0, return true if all pixels from the source image have | |
| * been delivered, false otherwise. Do not wait. | |
| * | |
| * If ms >= 0 then we request that our Image or ImageProducer start | |
| * delivering pixels to us via our ImageConsumer methods. | |
| * | |
| * If ms > 0, wait at most <code>ms</code> milliseconds for | |
| * delivery of all pixels within the grab rectangle. | |
| * | |
| * If ms == 0, wait until all pixels have been delivered. | |
| * | |
| * @return true if all pixels from the source image have been | |
| * delivered, false otherwise | |
| * | |
| * @throws InterruptedException if this thread is interrupted while | |
| * we are waiting for pixels to be delivered | |
| */ | |
| public synchronized boolean grabPixels(long ms) throws InterruptedException | |
| { | |
| if (ms < 0) | |
| return ((observerStatus & (ImageObserver.FRAMEBITS | |
| | ImageObserver.ALLBITS)) != 0); | |
| // Spawn a new ImageProducer thread to send us the image data via | |
| // our ImageConsumer methods. | |
| startGrabbing(); | |
| if (ms > 0) | |
| { | |
| long stop_time = System.currentTimeMillis() + ms; | |
| long time_remaining; | |
| while (grabbing) | |
| { | |
| time_remaining = stop_time - System.currentTimeMillis(); | |
| if (time_remaining <= 0) | |
| break; | |
| wait (time_remaining); | |
| } | |
| abortGrabbing (); | |
| } | |
| else | |
| wait (); | |
| // If consumerStatus is non-zero then the image is done loading or | |
| // an error has occurred. | |
| if (consumerStatus != 0) | |
| return setObserverStatus (); | |
| return ((observerStatus & (ImageObserver.FRAMEBITS | |
| | ImageObserver.ALLBITS)) != 0); | |
| } | |
| // Set observer status flags based on the current consumer status | |
| // flags. Return true if the consumer flags indicate that the | |
| // image was loaded successfully, or false otherwise. | |
| private synchronized boolean setObserverStatus () | |
| { | |
| boolean retval = false; | |
| if ((consumerStatus & IMAGEERROR) != 0) | |
| observerStatus |= ImageObserver.ERROR; | |
| if ((consumerStatus & IMAGEABORTED) != 0) | |
| observerStatus |= ImageObserver.ABORT; | |
| if ((consumerStatus & STATICIMAGEDONE) != 0) | |
| { | |
| observerStatus |= ImageObserver.ALLBITS; | |
| retval = true; | |
| } | |
| if ((consumerStatus & SINGLEFRAMEDONE) != 0) | |
| { | |
| observerStatus |= ImageObserver.FRAMEBITS; | |
| retval = true; | |
| } | |
| return retval; | |
| } | |
| /** | |
| * @return the status of the pixel grabbing thread, represented by a | |
| * bitwise OR of ImageObserver flags | |
| */ | |
| public synchronized int getStatus() | |
| { | |
| return observerStatus; | |
| } | |
| /** | |
| * @return the width of the grab rectangle in pixels, or a negative | |
| * number if the ImageProducer has not yet called our setDimensions | |
| * method | |
| */ | |
| public synchronized int getWidth() | |
| { | |
| return width; | |
| } | |
| /** | |
| * @return the height of the grab rectangle in pixels, or a negative | |
| * number if the ImageProducer has not yet called our setDimensions | |
| * method | |
| */ | |
| public synchronized int getHeight() | |
| { | |
| return height; | |
| } | |
| /** | |
| * @return a byte array of pixel data if ImageProducer delivered | |
| * pixel data using the byte[] variant of setPixels, or an int array | |
| * otherwise | |
| */ | |
| public synchronized Object getPixels() | |
| { | |
| if (ints_delivered) | |
| return int_pixel_buffer; | |
| else if (bytes_delivered) | |
| return byte_pixel_buffer; | |
| else | |
| return null; | |
| } | |
| /** | |
| * @return the ColorModel currently being used for the majority of | |
| * pixel data conversions | |
| */ | |
| public synchronized ColorModel getColorModel() | |
| { | |
| return model; | |
| } | |
| /** | |
| * Our <code>ImageProducer</code> calls this method to indicate the | |
| * size of the image being produced. | |
| * | |
| * setDimensions is an ImageConsumer method. None of PixelGrabber's | |
| * ImageConsumer methods should be called by code that instantiates | |
| * a PixelGrabber. They are only made public so they can be called | |
| * by the PixelGrabber's ImageProducer. | |
| * | |
| * @param width the width of the image | |
| * @param height the height of the image | |
| */ | |
| public synchronized void setDimensions(int width, int height) | |
| { | |
| // Our width wasn't set when we were constructed. Set our width | |
| // so that the grab region includes all pixels from x to the right | |
| // edge of the source image. | |
| if (this.width < 0) | |
| this.width = width - x; | |
| // Our height wasn't set when we were constructed. Set our height | |
| // so that the grab region includes all pixels from y to the | |
| // bottom edge of the source image. | |
| if (this.height < 0) | |
| this.height = height - y; | |
| if (scansize < 0) | |
| scansize = this.width; | |
| if (int_pixel_buffer == null) | |
| int_pixel_buffer = new int[this.width * this.height]; | |
| if (byte_pixel_buffer == null) | |
| byte_pixel_buffer = new byte[this.width * this.height]; | |
| } | |
| /** | |
| * Our <code>ImageProducer</code> may call this method to send us a | |
| * list of its image's properties. | |
| * | |
| * setProperties is an ImageConsumer method. None of PixelGrabber's | |
| * ImageConsumer methods should be called by code that instantiates | |
| * a PixelGrabber. They are only made public so they can be called | |
| * by the PixelGrabber's ImageProducer. | |
| * | |
| * @param props a list of properties associated with the image being | |
| * produced | |
| */ | |
| public synchronized void setProperties(Hashtable props) | |
| { | |
| this.props = props; | |
| } | |
| /** | |
| * Our ImageProducer will call <code>setColorModel</code> to | |
| * indicate the model used by the majority of calls to | |
| * <code>setPixels</code>. Each call to <code>setPixels</code> | |
| * could however indicate a different <code>ColorModel</code>. | |
| * | |
| * setColorModel is an ImageConsumer method. None of PixelGrabber's | |
| * ImageConsumer methods should be called by code that instantiates | |
| * a PixelGrabber. They are only made public so they can be called | |
| * by the PixelGrabber's ImageProducer. | |
| * | |
| * @param model the color model to be used most often by setPixels | |
| * | |
| * @see ColorModel | |
| */ | |
| public synchronized void setColorModel(ColorModel model) | |
| { | |
| this.model = model; | |
| } | |
| /** | |
| * Our <code>ImageProducer</code> may call this method with a | |
| * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, | |
| * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, | |
| * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>. | |
| * | |
| * setHints is an ImageConsumer method. None of PixelGrabber's | |
| * ImageConsumer methods should be called by code that instantiates | |
| * a PixelGrabber. They are only made public so they can be called | |
| * by the PixelGrabber's ImageProducer. | |
| * | |
| * @param flags a bit mask of hints | |
| */ | |
| public synchronized void setHints(int flags) | |
| { | |
| hints = flags; | |
| } | |
| /** | |
| * Our ImageProducer calls setPixels to deliver a subset of its | |
| * pixels. | |
| * | |
| * Each element of the pixels array represents one pixel. The | |
| * pixel data is formatted according to the color model model. | |
| * The x and y parameters are the coordinates of the rectangular | |
| * region of pixels being delivered to this ImageConsumer, | |
| * specified relative to the top left corner of the image being | |
| * produced. Likewise, w and h are the pixel region's dimensions. | |
| * | |
| * @param x x coordinate of pixel block | |
| * @param y y coordinate of pixel block | |
| * @param w width of pixel block | |
| * @param h height of pixel block | |
| * @param model color model used to interpret pixel data | |
| * @param pixels pixel block data | |
| * @param offset offset into pixels array | |
| * @param scansize width of one row in the pixel block | |
| */ | |
| public synchronized void setPixels(int x, int y, int w, int h, | |
| ColorModel model, byte[] pixels, | |
| int offset, int scansize) | |
| { | |
| ColorModel currentModel; | |
| if (model != null) | |
| currentModel = model; | |
| else | |
| currentModel = this.model; | |
| for(int yp = y; yp < (y + h); yp++) | |
| { | |
| for(int xp = x; xp < (x + w); xp++) | |
| { | |
| // Check if the coordinates (xp, yp) are within the | |
| // pixel block that we are grabbing. | |
| if(xp >= this.x | |
| && yp >= this.y | |
| && xp < (this.x + this.width) | |
| && yp < (this.y + this.height)) | |
| { | |
| int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; | |
| int p = (yp - y) * scansize + (xp - x) + offset; | |
| if (forceRGB) | |
| { | |
| ints_delivered = true; | |
| int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF); | |
| } | |
| else | |
| { | |
| bytes_delivered = true; | |
| byte_pixel_buffer[i] = pixels[p]; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Our ImageProducer calls setPixels to deliver a subset of its | |
| * pixels. | |
| * | |
| * Each element of the pixels array represents one pixel. The | |
| * pixel data is formatted according to the color model model. | |
| * The x and y parameters are the coordinates of the rectangular | |
| * region of pixels being delivered to this ImageConsumer, | |
| * specified relative to the top left corner of the image being | |
| * produced. Likewise, w and h are the pixel region's dimensions. | |
| * | |
| * @param x x coordinate of pixel block | |
| * @param y y coordinate of pixel block | |
| * @param w width of pixel block | |
| * @param h height of pixel block | |
| * @param model color model used to interpret pixel data | |
| * @param pixels pixel block data | |
| * @param offset offset into pixels array | |
| * @param scansize width of one row in the pixel block | |
| */ | |
| public synchronized void setPixels(int x, int y, int w, int h, | |
| ColorModel model, int[] pixels, | |
| int offset, int scansize) | |
| { | |
| ColorModel currentModel; | |
| if (model != null) | |
| currentModel = model; | |
| else | |
| currentModel = this.model; | |
| ints_delivered = true; | |
| for(int yp = y; yp < (y + h); yp++) | |
| { | |
| for(int xp = x; xp < (x + w); xp++) | |
| { | |
| // Check if the coordinates (xp, yp) are within the | |
| // pixel block that we are grabbing. | |
| if(xp >= this.x | |
| && yp >= this.y | |
| && xp < (this.x + this.width) | |
| && yp < (this.y + this.height)) | |
| { | |
| int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; | |
| int p = (yp - y) * scansize + (xp - x) + offset; | |
| if (forceRGB) | |
| int_pixel_buffer[i] = currentModel.getRGB (pixels[p]); | |
| else | |
| int_pixel_buffer[i] = pixels[p]; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Our <code>ImageProducer</code> calls this method to inform us | |
| * that a single frame or the entire image is complete. The method | |
| * is also used to inform us of an error in loading or producing the | |
| * image. | |
| * | |
| * @param status the status of image production, represented by a | |
| * bitwise OR of ImageConsumer flags | |
| */ | |
| public synchronized void imageComplete(int status) | |
| { | |
| consumerStatus = status; | |
| setObserverStatus (); | |
| grabbing = false; | |
| if (ip != null) | |
| ip.removeConsumer (this); | |
| notifyAll (); | |
| } | |
| /** | |
| * @return the return value of getStatus | |
| * | |
| * @specnote The newer getStatus should be used in place of status. | |
| */ | |
| public synchronized int status() | |
| { | |
| return getStatus(); | |
| } | |
| } |