Frames | No Frames |
1: /* PixelGrabber.java -- retrieve a subset of an image's data 2: Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.awt.image; 40: 41: import java.awt.Image; 42: import java.util.Hashtable; 43: 44: /** 45: * PixelGrabber is an ImageConsumer that extracts a rectangular region 46: * of pixels from an Image. 47: */ 48: public class PixelGrabber implements ImageConsumer 49: { 50: int x, y, offset; 51: int width = -1; 52: int height = -1; 53: int scansize = -1; 54: boolean forceRGB = true; 55: 56: ColorModel model = ColorModel.getRGBdefault(); 57: int hints; 58: Hashtable<?,?> props; 59: 60: int int_pixel_buffer[]; 61: boolean ints_delivered = false; 62: byte byte_pixel_buffer[]; 63: boolean bytes_delivered = false; 64: 65: ImageProducer ip; 66: int observerStatus; 67: int consumerStatus; 68: 69: private Thread grabberThread; 70: boolean grabbing = false; 71: 72: /** 73: * Construct a PixelGrabber that will retrieve RGB data from a given 74: * Image. 75: * 76: * The RGB data will be retrieved from a rectangular region 77: * <code>(x, y, w, h)</code> within the image. The data will be 78: * stored in the provided <code>pix</code> array, which must have 79: * been initialized to a size of at least <code>w * h</code>. The 80: * data for a pixel (m, n) in the grab rectangle will be stored at 81: * <code>pix[(n - y) * scansize + (m - x) + off]</code>. 82: * 83: * @param img the Image from which to grab pixels 84: * @param x the x coordinate, relative to <code>img</code>'s 85: * top-left corner, of the grab rectangle's top-left pixel 86: * @param y the y coordinate, relative to <code>img</code>'s 87: * top-left corner, of the grab rectangle's top-left pixel 88: * @param w the width of the grab rectangle, in pixels 89: * @param h the height of the grab rectangle, in pixels 90: * @param pix the array in which to store grabbed RGB pixel data 91: * @param off the offset into the <code>pix</code> array at which to 92: * start storing RGB data 93: * @param scansize a set of <code>scansize</code> consecutive 94: * elements in the <code>pix</code> array represents one row of 95: * pixels in the grab rectangle 96: */ 97: public PixelGrabber(Image img, int x, int y, int w, int h, 98: int pix[], int off, int scansize) 99: { 100: this (img.getSource(), x, y, w, h, pix, off, scansize); 101: } 102: 103: /** 104: * Construct a PixelGrabber that will retrieve RGB data from a given 105: * ImageProducer. 106: * 107: * The RGB data will be retrieved from a rectangular region 108: * <code>(x, y, w, h)</code> within the image produced by 109: * <code>ip</code>. The data will be stored in the provided 110: * <code>pix</code> array, which must have been initialized to a 111: * size of at least <code>w * h</code>. The data for a pixel (m, n) 112: * in the grab rectangle will be stored at 113: * <code>pix[(n - y) * scansize + (m - x) + off]</code>. 114: * 115: * @param ip the ImageProducer from which to grab pixels. This can 116: * be null. 117: * @param x the x coordinate of the grab rectangle's top-left pixel, 118: * specified relative to the top-left corner of the image produced 119: * by <code>ip</code> 120: * @param y the y coordinate of the grab rectangle's top-left pixel, 121: * specified relative to the top-left corner of the image produced 122: * by <code>ip</code> 123: * @param w the width of the grab rectangle, in pixels 124: * @param h the height of the grab rectangle, in pixels 125: * @param pix the array in which to store grabbed RGB pixel data 126: * @param off the offset into the <code>pix</code> array at which to 127: * start storing RGB data 128: * @param scansize a set of <code>scansize</code> consecutive 129: * elements in the <code>pix</code> array represents one row of 130: * pixels in the grab rectangle 131: */ 132: public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, 133: int pix[], int off, int scansize) 134: { 135: this.ip = ip; 136: this.x = x; 137: this.y = y; 138: this.width = w; 139: this.height = h; 140: this.offset = off; 141: this.scansize = scansize; 142: 143: int_pixel_buffer = pix; 144: // Initialize the byte array in case ip sends us byte-formatted 145: // pixel data. 146: byte_pixel_buffer = new byte[pix.length * 4]; 147: } 148: 149: /** 150: * Construct a PixelGrabber that will retrieve data from a given 151: * Image. 152: * 153: * The RGB data will be retrieved from a rectangular region 154: * <code>(x, y, w, h)</code> within the image. The data will be 155: * stored in an internal array which can be accessed by calling 156: * <code>getPixels</code>. The data for a pixel (m, n) in the grab 157: * rectangle will be stored in the returned array at index 158: * <code>(n - y) * scansize + (m - x) + off</code>. 159: * If forceRGB is false, then the returned data will be not be 160: * converted to RGB from its format in <code>img</code>. 161: * 162: * If <code>w</code> is negative, the width of the grab region will 163: * be from x to the right edge of the image. Likewise, if 164: * <code>h</code> is negative, the height of the grab region will be 165: * from y to the bottom edge of the image. 166: * 167: * @param img the Image from which to grab pixels 168: * @param x the x coordinate, relative to <code>img</code>'s 169: * top-left corner, of the grab rectangle's top-left pixel 170: * @param y the y coordinate, relative to <code>img</code>'s 171: * top-left corner, of the grab rectangle's top-left pixel 172: * @param w the width of the grab rectangle, in pixels 173: * @param h the height of the grab rectangle, in pixels 174: * @param forceRGB true to force conversion of the rectangular 175: * region's pixel data to RGB 176: */ 177: public PixelGrabber(Image img, 178: int x, int y, 179: int w, int h, 180: boolean forceRGB) 181: { 182: this.ip = img.getSource(); 183: 184: if (this.ip == null) 185: throw new NullPointerException("The ImageProducer must not be null."); 186: 187: this.x = x; 188: this.y = y; 189: width = w; 190: height = h; 191: // If width or height is negative, postpone pixel buffer 192: // initialization until setDimensions is called back by ip. 193: if (width >= 0 && height >= 0) 194: { 195: int_pixel_buffer = new int[width * height]; 196: byte_pixel_buffer = new byte[width * height]; 197: } 198: this.forceRGB = forceRGB; 199: } 200: 201: /** 202: * Start grabbing pixels. 203: * 204: * Spawns an image production thread that calls back to this 205: * PixelGrabber's ImageConsumer methods. 206: */ 207: public synchronized void startGrabbing() 208: { 209: // Make sure we're not already grabbing. 210: if (grabbing == false) 211: { 212: grabbing = true; 213: grabberThread = new Thread () 214: { 215: public void run () 216: { 217: try 218: { 219: ip.startProduction (PixelGrabber.this); 220: } 221: catch (Exception ex) 222: { 223: imageComplete(ImageConsumer.IMAGEABORTED); 224: } 225: } 226: }; 227: grabberThread.start (); 228: } 229: } 230: 231: /** 232: * Abort pixel grabbing. 233: */ 234: public synchronized void abortGrabbing() 235: { 236: if (grabbing) 237: { 238: // Interrupt the grabbing thread. 239: Thread moribund = grabberThread; 240: grabberThread = null; 241: moribund.interrupt(); 242: 243: imageComplete (ImageConsumer.IMAGEABORTED); 244: } 245: } 246: 247: /** 248: * Have our Image or ImageProducer start sending us pixels via our 249: * ImageConsumer methods and wait for all pixels in the grab 250: * rectangle to be delivered. 251: * 252: * @return true if successful, false on abort or error 253: * 254: * @throws InterruptedException if interrupted by another thread. 255: */ 256: public synchronized boolean grabPixels() throws InterruptedException 257: { 258: return grabPixels(0); 259: } 260: 261: /** 262: * grabPixels's behavior depends on the value of <code>ms</code>. 263: * 264: * If ms < 0, return true if all pixels from the source image have 265: * been delivered, false otherwise. Do not wait. 266: * 267: * If ms >= 0 then we request that our Image or ImageProducer start 268: * delivering pixels to us via our ImageConsumer methods. 269: * 270: * If ms > 0, wait at most <code>ms</code> milliseconds for 271: * delivery of all pixels within the grab rectangle. 272: * 273: * If ms == 0, wait until all pixels have been delivered. 274: * 275: * @return true if all pixels from the source image have been 276: * delivered, false otherwise 277: * 278: * @throws InterruptedException if this thread is interrupted while 279: * we are waiting for pixels to be delivered 280: */ 281: public synchronized boolean grabPixels(long ms) throws InterruptedException 282: { 283: if (ms < 0) 284: return ((observerStatus & (ImageObserver.FRAMEBITS 285: | ImageObserver.ALLBITS)) != 0); 286: 287: // Spawn a new ImageProducer thread to send us the image data via 288: // our ImageConsumer methods. 289: startGrabbing(); 290: 291: if (ms > 0) 292: { 293: long stop_time = System.currentTimeMillis() + ms; 294: long time_remaining; 295: while (grabbing) 296: { 297: time_remaining = stop_time - System.currentTimeMillis(); 298: if (time_remaining <= 0) 299: break; 300: wait (time_remaining); 301: } 302: abortGrabbing (); 303: } 304: else 305: wait (); 306: 307: // If consumerStatus is non-zero then the image is done loading or 308: // an error has occurred. 309: if (consumerStatus != 0) 310: return setObserverStatus (); 311: 312: return ((observerStatus & (ImageObserver.FRAMEBITS 313: | ImageObserver.ALLBITS)) != 0); 314: } 315: 316: // Set observer status flags based on the current consumer status 317: // flags. Return true if the consumer flags indicate that the 318: // image was loaded successfully, or false otherwise. 319: private synchronized boolean setObserverStatus () 320: { 321: boolean retval = false; 322: 323: if ((consumerStatus & IMAGEERROR) != 0) 324: observerStatus |= ImageObserver.ERROR; 325: 326: if ((consumerStatus & IMAGEABORTED) != 0) 327: observerStatus |= ImageObserver.ABORT; 328: 329: if ((consumerStatus & STATICIMAGEDONE) != 0) 330: { 331: observerStatus |= ImageObserver.ALLBITS; 332: retval = true; 333: } 334: 335: if ((consumerStatus & SINGLEFRAMEDONE) != 0) 336: { 337: observerStatus |= ImageObserver.FRAMEBITS; 338: retval = true; 339: } 340: 341: return retval; 342: } 343: 344: /** 345: * @return the status of the pixel grabbing thread, represented by a 346: * bitwise OR of ImageObserver flags 347: */ 348: public synchronized int getStatus() 349: { 350: return observerStatus; 351: } 352: 353: /** 354: * @return the width of the grab rectangle in pixels, or a negative 355: * number if the ImageProducer has not yet called our setDimensions 356: * method 357: */ 358: public synchronized int getWidth() 359: { 360: return width; 361: } 362: 363: /** 364: * @return the height of the grab rectangle in pixels, or a negative 365: * number if the ImageProducer has not yet called our setDimensions 366: * method 367: */ 368: public synchronized int getHeight() 369: { 370: return height; 371: } 372: 373: /** 374: * @return a byte array of pixel data if ImageProducer delivered 375: * pixel data using the byte[] variant of setPixels, or an int array 376: * otherwise 377: */ 378: public synchronized Object getPixels() 379: { 380: if (ints_delivered) 381: return int_pixel_buffer; 382: else if (bytes_delivered) 383: return byte_pixel_buffer; 384: else 385: return null; 386: } 387: 388: /** 389: * @return the ColorModel currently being used for the majority of 390: * pixel data conversions 391: */ 392: public synchronized ColorModel getColorModel() 393: { 394: return model; 395: } 396: 397: /** 398: * Our <code>ImageProducer</code> calls this method to indicate the 399: * size of the image being produced. 400: * 401: * setDimensions is an ImageConsumer method. None of PixelGrabber's 402: * ImageConsumer methods should be called by code that instantiates 403: * a PixelGrabber. They are only made public so they can be called 404: * by the PixelGrabber's ImageProducer. 405: * 406: * @param width the width of the image 407: * @param height the height of the image 408: */ 409: public synchronized void setDimensions(int width, int height) 410: { 411: // Our width wasn't set when we were constructed. Set our width 412: // so that the grab region includes all pixels from x to the right 413: // edge of the source image. 414: if (this.width < 0) 415: this.width = width - x; 416: 417: // Our height wasn't set when we were constructed. Set our height 418: // so that the grab region includes all pixels from y to the 419: // bottom edge of the source image. 420: if (this.height < 0) 421: this.height = height - y; 422: 423: if (scansize < 0) 424: scansize = this.width; 425: 426: if (int_pixel_buffer == null) 427: int_pixel_buffer = new int[this.width * this.height]; 428: 429: if (byte_pixel_buffer == null) 430: byte_pixel_buffer = new byte[this.width * this.height]; 431: } 432: 433: /** 434: * Our <code>ImageProducer</code> may call this method to send us a 435: * list of its image's properties. 436: * 437: * setProperties is an ImageConsumer method. None of PixelGrabber's 438: * ImageConsumer methods should be called by code that instantiates 439: * a PixelGrabber. They are only made public so they can be called 440: * by the PixelGrabber's ImageProducer. 441: * 442: * @param props a list of properties associated with the image being 443: * produced 444: */ 445: public synchronized void setProperties(Hashtable<?,?> props) 446: { 447: this.props = props; 448: } 449: 450: /** 451: * Our ImageProducer will call <code>setColorModel</code> to 452: * indicate the model used by the majority of calls to 453: * <code>setPixels</code>. Each call to <code>setPixels</code> 454: * could however indicate a different <code>ColorModel</code>. 455: * 456: * setColorModel is an ImageConsumer method. None of PixelGrabber's 457: * ImageConsumer methods should be called by code that instantiates 458: * a PixelGrabber. They are only made public so they can be called 459: * by the PixelGrabber's ImageProducer. 460: * 461: * @param model the color model to be used most often by setPixels 462: * 463: * @see ColorModel 464: */ 465: public synchronized void setColorModel(ColorModel model) 466: { 467: this.model = model; 468: } 469: 470: /** 471: * Our <code>ImageProducer</code> may call this method with a 472: * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, 473: * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, 474: * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>. 475: * 476: * setHints is an ImageConsumer method. None of PixelGrabber's 477: * ImageConsumer methods should be called by code that instantiates 478: * a PixelGrabber. They are only made public so they can be called 479: * by the PixelGrabber's ImageProducer. 480: * 481: * @param flags a bit mask of hints 482: */ 483: public synchronized void setHints(int flags) 484: { 485: hints = flags; 486: } 487: 488: /** 489: * Our ImageProducer calls setPixels to deliver a subset of its 490: * pixels. 491: * 492: * Each element of the pixels array represents one pixel. The 493: * pixel data is formatted according to the color model model. 494: * The x and y parameters are the coordinates of the rectangular 495: * region of pixels being delivered to this ImageConsumer, 496: * specified relative to the top left corner of the image being 497: * produced. Likewise, w and h are the pixel region's dimensions. 498: * 499: * @param x x coordinate of pixel block 500: * @param y y coordinate of pixel block 501: * @param w width of pixel block 502: * @param h height of pixel block 503: * @param model color model used to interpret pixel data 504: * @param pixels pixel block data 505: * @param offset offset into pixels array 506: * @param scansize width of one row in the pixel block 507: */ 508: public synchronized void setPixels(int x, int y, int w, int h, 509: ColorModel model, byte[] pixels, 510: int offset, int scansize) 511: { 512: ColorModel currentModel; 513: if (model != null) 514: currentModel = model; 515: else 516: currentModel = this.model; 517: 518: for(int yp = y; yp < (y + h); yp++) 519: { 520: for(int xp = x; xp < (x + w); xp++) 521: { 522: // Check if the coordinates (xp, yp) are within the 523: // pixel block that we are grabbing. 524: if(xp >= this.x 525: && yp >= this.y 526: && xp < (this.x + this.width) 527: && yp < (this.y + this.height)) 528: { 529: int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; 530: int p = (yp - y) * scansize + (xp - x) + offset; 531: if (forceRGB) 532: { 533: ints_delivered = true; 534: 535: int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF); 536: } 537: else 538: { 539: bytes_delivered = true; 540: 541: byte_pixel_buffer[i] = pixels[p]; 542: } 543: } 544: } 545: } 546: } 547: 548: /** 549: * Our ImageProducer calls setPixels to deliver a subset of its 550: * pixels. 551: * 552: * Each element of the pixels array represents one pixel. The 553: * pixel data is formatted according to the color model model. 554: * The x and y parameters are the coordinates of the rectangular 555: * region of pixels being delivered to this ImageConsumer, 556: * specified relative to the top left corner of the image being 557: * produced. Likewise, w and h are the pixel region's dimensions. 558: * 559: * @param x x coordinate of pixel block 560: * @param y y coordinate of pixel block 561: * @param w width of pixel block 562: * @param h height of pixel block 563: * @param model color model used to interpret pixel data 564: * @param pixels pixel block data 565: * @param offset offset into pixels array 566: * @param scansize width of one row in the pixel block 567: */ 568: public synchronized void setPixels(int x, int y, int w, int h, 569: ColorModel model, int[] pixels, 570: int offset, int scansize) 571: { 572: ColorModel currentModel; 573: if (model != null) 574: currentModel = model; 575: else 576: currentModel = this.model; 577: 578: ints_delivered = true; 579: 580: for(int yp = y; yp < (y + h); yp++) 581: { 582: for(int xp = x; xp < (x + w); xp++) 583: { 584: // Check if the coordinates (xp, yp) are within the 585: // pixel block that we are grabbing. 586: if(xp >= this.x 587: && yp >= this.y 588: && xp < (this.x + this.width) 589: && yp < (this.y + this.height)) 590: { 591: int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; 592: int p = (yp - y) * scansize + (xp - x) + offset; 593: if (forceRGB) 594: int_pixel_buffer[i] = currentModel.getRGB (pixels[p]); 595: else 596: int_pixel_buffer[i] = pixels[p]; 597: } 598: } 599: } 600: } 601: 602: /** 603: * Our <code>ImageProducer</code> calls this method to inform us 604: * that a single frame or the entire image is complete. The method 605: * is also used to inform us of an error in loading or producing the 606: * image. 607: * 608: * @param status the status of image production, represented by a 609: * bitwise OR of ImageConsumer flags 610: */ 611: public synchronized void imageComplete(int status) 612: { 613: consumerStatus = status; 614: setObserverStatus (); 615: grabbing = false; 616: if (ip != null) 617: ip.removeConsumer (this); 618: 619: notifyAll (); 620: } 621: 622: /** 623: * @return the return value of getStatus 624: * 625: * @specnote The newer getStatus should be used in place of status. 626: */ 627: public synchronized int status() 628: { 629: return getStatus(); 630: } 631: }