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 */ PixelGrabber(Image img, int x, int y, int w, int h, int pix[], int off, int scansize)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 */ PixelGrabber(ImageProducer ip, int x, int y, int w, int h, int pix[], int off, int scansize)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 */ PixelGrabber(Image img, int x, int y, int w, int h, boolean forceRGB)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 */ startGrabbing()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 */ abortGrabbing()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 */ grabPixels()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 */ grabPixels(long ms)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. setObserverStatus()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 */ getStatus()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 */ getWidth()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 */ getHeight()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 */ getPixels()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 */ getColorModel()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 */ setDimensions(int width, int height)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 */ setProperties(Hashtable<?,?> props)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 */ setColorModel(ColorModel model)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 */ setHints(int flags)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 */ setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int offset, int scansize)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 */ setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int offset, int scansize)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 */ imageComplete(int status)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 */ status()627 public synchronized int status() 628 { 629 return getStatus(); 630 } 631 } 632