GNU Classpath (0.20) | |
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 116: * @param x the x coordinate of the grab rectangle's top-left pixel, 117: * specified relative to the top-left corner of the image produced 118: * by <code>ip</code> 119: * @param y the y coordinate of the grab rectangle's top-left pixel, 120: * specified relative to the top-left corner of the image produced 121: * by <code>ip</code> 122: * @param w the width of the grab rectangle, in pixels 123: * @param h the height of the grab rectangle, in pixels 124: * @param pix the array in which to store grabbed RGB pixel data 125: * @param off the offset into the <code>pix</code> array at which to 126: * start storing RGB data 127: * @param scansize a set of <code>scansize</code> consecutive 128: * elements in the <code>pix</code> array represents one row of 129: * pixels in the grab rectangle 130: */ 131: public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, 132: int pix[], int off, int scansize) 133: { 134: if (ip == null) 135: throw new NullPointerException("The ImageProducer must not be null."); 136: 137: this.ip = ip; 138: this.x = x; 139: this.y = y; 140: this.width = w; 141: this.height = h; 142: this.offset = off; 143: this.scansize = scansize; 144: 145: int_pixel_buffer = pix; 146: // Initialize the byte array in case ip sends us byte-formatted 147: // pixel data. 148: byte_pixel_buffer = new byte[pix.length * 4]; 149: } 150: 151: /** 152: * Construct a PixelGrabber that will retrieve data from a given 153: * Image. 154: * 155: * The RGB data will be retrieved from a rectangular region 156: * <code>(x, y, w, h)</code> within the image. The data will be 157: * stored in an internal array which can be accessed by calling 158: * <code>getPixels</code>. The data for a pixel (m, n) in the grab 159: * rectangle will be stored in the returned array at index 160: * <code>(n - y) * scansize + (m - x) + off</code>. 161: * If forceRGB is false, then the returned data will be not be 162: * converted to RGB from its format in <code>img</code>. 163: * 164: * If <code>w</code> is negative, the width of the grab region will 165: * be from x to the right edge of the image. Likewise, if 166: * <code>h</code> is negative, the height of the grab region will be 167: * from y to the bottom edge of the image. 168: * 169: * @param img the Image from which to grab pixels 170: * @param x the x coordinate, relative to <code>img</code>'s 171: * top-left corner, of the grab rectangle's top-left pixel 172: * @param y the y coordinate, relative to <code>img</code>'s 173: * top-left corner, of the grab rectangle's top-left pixel 174: * @param w the width of the grab rectangle, in pixels 175: * @param h the height of the grab rectangle, in pixels 176: * @param forceRGB true to force conversion of the rectangular 177: * region's pixel data to RGB 178: */ 179: public PixelGrabber(Image img, 180: int x, int y, 181: int w, int h, 182: boolean forceRGB) 183: { 184: this.ip = img.getSource(); 185: 186: if (this.ip == null) 187: throw new NullPointerException("The ImageProducer must not be null."); 188: 189: this.x = x; 190: this.y = y; 191: width = w; 192: height = h; 193: // If width or height is negative, postpone pixel buffer 194: // initialization until setDimensions is called back by ip. 195: if (width >= 0 && height >= 0) 196: { 197: int_pixel_buffer = new int[width * height]; 198: byte_pixel_buffer = new byte[width * height]; 199: } 200: this.forceRGB = forceRGB; 201: } 202: 203: /** 204: * Start grabbing pixels. 205: * 206: * Spawns an image production thread that calls back to this 207: * PixelGrabber's ImageConsumer methods. 208: */ 209: public synchronized void startGrabbing() 210: { 211: // Make sure we're not already grabbing. 212: if (grabbing == false) 213: { 214: grabbing = true; 215: grabberThread = new Thread () 216: { 217: public void run () 218: { 219: try 220: { 221: ip.startProduction (PixelGrabber.this); 222: } 223: catch (Exception ex) 224: { 225: ex.printStackTrace(); 226: imageComplete(ImageConsumer.IMAGEABORTED); 227: } 228: } 229: }; 230: grabberThread.start (); 231: } 232: } 233: 234: /** 235: * Abort pixel grabbing. 236: */ 237: public synchronized void abortGrabbing() 238: { 239: if (grabbing) 240: { 241: // Interrupt the grabbing thread. 242: Thread moribund = grabberThread; 243: grabberThread = null; 244: moribund.interrupt(); 245: 246: imageComplete (ImageConsumer.IMAGEABORTED); 247: } 248: } 249: 250: /** 251: * Have our Image or ImageProducer start sending us pixels via our 252: * ImageConsumer methods and wait for all pixels in the grab 253: * rectangle to be delivered. 254: * 255: * @return true if successful, false on abort or error 256: * 257: * @throws InterruptedException if interrupted by another thread. 258: */ 259: public synchronized boolean grabPixels() throws InterruptedException 260: { 261: return grabPixels(0); 262: } 263: 264: /** 265: * grabPixels's behavior depends on the value of <code>ms</code>. 266: * 267: * If ms < 0, return true if all pixels from the source image have 268: * been delivered, false otherwise. Do not wait. 269: * 270: * If ms >= 0 then we request that our Image or ImageProducer start 271: * delivering pixels to us via our ImageConsumer methods. 272: * 273: * If ms > 0, wait at most <code>ms</code> milliseconds for 274: * delivery of all pixels within the grab rectangle. 275: * 276: * If ms == 0, wait until all pixels have been delivered. 277: * 278: * @return true if all pixels from the source image have been 279: * delivered, false otherwise 280: * 281: * @throws InterruptedException if this thread is interrupted while 282: * we are waiting for pixels to be delivered 283: */ 284: public synchronized boolean grabPixels(long ms) throws InterruptedException 285: { 286: if (ms < 0) 287: return ((observerStatus & (ImageObserver.FRAMEBITS 288: | ImageObserver.ALLBITS)) != 0); 289: 290: // Spawn a new ImageProducer thread to send us the image data via 291: // our ImageConsumer methods. 292: startGrabbing(); 293: 294: if (ms > 0) 295: { 296: long stop_time = System.currentTimeMillis() + ms; 297: long time_remaining; 298: while (grabbing) 299: { 300: time_remaining = stop_time - System.currentTimeMillis(); 301: if (time_remaining <= 0) 302: break; 303: wait (time_remaining); 304: } 305: abortGrabbing (); 306: } 307: else 308: wait (); 309: 310: // If consumerStatus is non-zero then the image is done loading or 311: // an error has occurred. 312: if (consumerStatus != 0) 313: return setObserverStatus (); 314: 315: return ((observerStatus & (ImageObserver.FRAMEBITS 316: | ImageObserver.ALLBITS)) != 0); 317: } 318: 319: // Set observer status flags based on the current consumer status 320: // flags. Return true if the consumer flags indicate that the 321: // image was loaded successfully, or false otherwise. 322: private synchronized boolean setObserverStatus () 323: { 324: boolean retval = false; 325: 326: if ((consumerStatus & IMAGEERROR) != 0) 327: observerStatus |= ImageObserver.ERROR; 328: 329: if ((consumerStatus & IMAGEABORTED) != 0) 330: observerStatus |= ImageObserver.ABORT; 331: 332: if ((consumerStatus & STATICIMAGEDONE) != 0) 333: { 334: observerStatus |= ImageObserver.ALLBITS; 335: retval = true; 336: } 337: 338: if ((consumerStatus & SINGLEFRAMEDONE) != 0) 339: { 340: observerStatus |= ImageObserver.FRAMEBITS; 341: retval = true; 342: } 343: 344: return retval; 345: } 346: 347: /** 348: * @return the status of the pixel grabbing thread, represented by a 349: * bitwise OR of ImageObserver flags 350: */ 351: public synchronized int getStatus() 352: { 353: return observerStatus; 354: } 355: 356: /** 357: * @return the width of the grab rectangle in pixels, or a negative 358: * number if the ImageProducer has not yet called our setDimensions 359: * method 360: */ 361: public synchronized int getWidth() 362: { 363: return width; 364: } 365: 366: /** 367: * @return the height of the grab rectangle in pixels, or a negative 368: * number if the ImageProducer has not yet called our setDimensions 369: * method 370: */ 371: public synchronized int getHeight() 372: { 373: return height; 374: } 375: 376: /** 377: * @return a byte array of pixel data if ImageProducer delivered 378: * pixel data using the byte[] variant of setPixels, or an int array 379: * otherwise 380: */ 381: public synchronized Object getPixels() 382: { 383: if (ints_delivered) 384: return int_pixel_buffer; 385: else if (bytes_delivered) 386: return byte_pixel_buffer; 387: else 388: return null; 389: } 390: 391: /** 392: * @return the ColorModel currently being used for the majority of 393: * pixel data conversions 394: */ 395: public synchronized ColorModel getColorModel() 396: { 397: return model; 398: } 399: 400: /** 401: * Our <code>ImageProducer</code> calls this method to indicate the 402: * size of the image being produced. 403: * 404: * setDimensions is an ImageConsumer method. None of PixelGrabber's 405: * ImageConsumer methods should be called by code that instantiates 406: * a PixelGrabber. They are only made public so they can be called 407: * by the PixelGrabber's ImageProducer. 408: * 409: * @param width the width of the image 410: * @param height the height of the image 411: */ 412: public synchronized void setDimensions(int width, int height) 413: { 414: // Our width wasn't set when we were constructed. Set our width 415: // so that the grab region includes all pixels from x to the right 416: // edge of the source image. 417: if (this.width < 0) 418: this.width = width - x; 419: 420: // Our height wasn't set when we were constructed. Set our height 421: // so that the grab region includes all pixels from y to the 422: // bottom edge of the source image. 423: if (this.height < 0) 424: this.height = height - y; 425: 426: if (scansize < 0) 427: scansize = this.width; 428: 429: if (int_pixel_buffer == null) 430: int_pixel_buffer = new int[this.width * this.height]; 431: 432: if (byte_pixel_buffer == null) 433: byte_pixel_buffer = new byte[this.width * this.height]; 434: } 435: 436: /** 437: * Our <code>ImageProducer</code> may call this method to send us a 438: * list of its image's properties. 439: * 440: * setProperties is an ImageConsumer method. None of PixelGrabber's 441: * ImageConsumer methods should be called by code that instantiates 442: * a PixelGrabber. They are only made public so they can be called 443: * by the PixelGrabber's ImageProducer. 444: * 445: * @param props a list of properties associated with the image being 446: * produced 447: */ 448: public synchronized void setProperties(Hashtable props) 449: { 450: this.props = props; 451: } 452: 453: /** 454: * Our ImageProducer will call <code>setColorModel</code> to 455: * indicate the model used by the majority of calls to 456: * <code>setPixels</code>. Each call to <code>setPixels</code> 457: * could however indicate a different <code>ColorModel</code>. 458: * 459: * setColorModel is an ImageConsumer method. None of PixelGrabber's 460: * ImageConsumer methods should be called by code that instantiates 461: * a PixelGrabber. They are only made public so they can be called 462: * by the PixelGrabber's ImageProducer. 463: * 464: * @param model the color model to be used most often by setPixels 465: * 466: * @see ColorModel 467: */ 468: public synchronized void setColorModel(ColorModel model) 469: { 470: this.model = model; 471: } 472: 473: /** 474: * Our <code>ImageProducer</code> may call this method with a 475: * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, 476: * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, 477: * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>. 478: * 479: * setHints is an ImageConsumer method. None of PixelGrabber's 480: * ImageConsumer methods should be called by code that instantiates 481: * a PixelGrabber. They are only made public so they can be called 482: * by the PixelGrabber's ImageProducer. 483: * 484: * @param flags a bit mask of hints 485: */ 486: public synchronized void setHints(int flags) 487: { 488: hints = flags; 489: } 490: 491: /** 492: * Our ImageProducer calls setPixels to deliver a subset of its 493: * pixels. 494: * 495: * Each element of the pixels array represents one pixel. The 496: * pixel data is formatted according to the color model model. 497: * The x and y parameters are the coordinates of the rectangular 498: * region of pixels being delivered to this ImageConsumer, 499: * specified relative to the top left corner of the image being 500: * produced. Likewise, w and h are the pixel region's dimensions. 501: * 502: * @param x x coordinate of pixel block 503: * @param y y coordinate of pixel block 504: * @param w width of pixel block 505: * @param h height of pixel block 506: * @param model color model used to interpret pixel data 507: * @param pixels pixel block data 508: * @param offset offset into pixels array 509: * @param scansize width of one row in the pixel block 510: */ 511: public synchronized void setPixels(int x, int y, int w, int h, 512: ColorModel model, byte[] pixels, 513: int offset, int scansize) 514: { 515: ColorModel currentModel; 516: if (model != null) 517: currentModel = model; 518: else 519: currentModel = this.model; 520: 521: for(int yp = y; yp < (y + h); yp++) 522: { 523: for(int xp = x; xp < (x + w); xp++) 524: { 525: // Check if the coordinates (xp, yp) are within the 526: // pixel block that we are grabbing. 527: if(xp >= this.x 528: && yp >= this.y 529: && xp < (this.x + this.width) 530: && yp < (this.y + this.height)) 531: { 532: int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; 533: int p = (yp - y) * scansize + (xp - x) + offset; 534: if (forceRGB) 535: { 536: ints_delivered = true; 537: 538: int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF); 539: } 540: else 541: { 542: bytes_delivered = true; 543: 544: byte_pixel_buffer[i] = pixels[p]; 545: } 546: } 547: } 548: } 549: } 550: 551: /** 552: * Our ImageProducer calls setPixels to deliver a subset of its 553: * pixels. 554: * 555: * Each element of the pixels array represents one pixel. The 556: * pixel data is formatted according to the color model model. 557: * The x and y parameters are the coordinates of the rectangular 558: * region of pixels being delivered to this ImageConsumer, 559: * specified relative to the top left corner of the image being 560: * produced. Likewise, w and h are the pixel region's dimensions. 561: * 562: * @param x x coordinate of pixel block 563: * @param y y coordinate of pixel block 564: * @param w width of pixel block 565: * @param h height of pixel block 566: * @param model color model used to interpret pixel data 567: * @param pixels pixel block data 568: * @param offset offset into pixels array 569: * @param scansize width of one row in the pixel block 570: */ 571: public synchronized void setPixels(int x, int y, int w, int h, 572: ColorModel model, int[] pixels, 573: int offset, int scansize) 574: { 575: ColorModel currentModel; 576: if (model != null) 577: currentModel = model; 578: else 579: currentModel = this.model; 580: 581: ints_delivered = true; 582: 583: for(int yp = y; yp < (y + h); yp++) 584: { 585: for(int xp = x; xp < (x + w); xp++) 586: { 587: // Check if the coordinates (xp, yp) are within the 588: // pixel block that we are grabbing. 589: if(xp >= this.x 590: && yp >= this.y 591: && xp < (this.x + this.width) 592: && yp < (this.y + this.height)) 593: { 594: int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; 595: int p = (yp - y) * scansize + (xp - x) + offset; 596: if (forceRGB) 597: int_pixel_buffer[i] = currentModel.getRGB (pixels[p]); 598: else 599: int_pixel_buffer[i] = pixels[p]; 600: } 601: } 602: } 603: } 604: 605: /** 606: * Our <code>ImageProducer</code> calls this method to inform us 607: * that a single frame or the entire image is complete. The method 608: * is also used to inform us of an error in loading or producing the 609: * image. 610: * 611: * @param status the status of image production, represented by a 612: * bitwise OR of ImageConsumer flags 613: */ 614: public synchronized void imageComplete(int status) 615: { 616: consumerStatus = status; 617: setObserverStatus (); 618: grabbing = false; 619: if (ip != null) 620: ip.removeConsumer (this); 621: 622: notifyAll (); 623: } 624: 625: /** 626: * @return the return value of getStatus 627: * 628: * @specnote The newer getStatus should be used in place of status. 629: */ 630: public synchronized int status() 631: { 632: return getStatus(); 633: } 634: }
GNU Classpath (0.20) |