| /* 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(); |
| } |
| } |