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