1 /* 2 * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt.image; 27 28 import java.util.Hashtable; 29 import java.awt.image.ImageProducer; 30 import java.awt.image.ImageConsumer; 31 import java.awt.image.ColorModel; 32 import java.awt.Image; 33 34 /** 35 * The PixelGrabber class implements an ImageConsumer which can be attached 36 * to an Image or ImageProducer object to retrieve a subset of the pixels 37 * in that image. Here is an example: 38 * <pre>{@code 39 * 40 * public void handlesinglepixel(int x, int y, int pixel) { 41 * int alpha = (pixel >> 24) & 0xff; 42 * int red = (pixel >> 16) & 0xff; 43 * int green = (pixel >> 8) & 0xff; 44 * int blue = (pixel ) & 0xff; 45 * // Deal with the pixel as necessary... 46 * } 47 * 48 * public void handlepixels(Image img, int x, int y, int w, int h) { 49 * int[] pixels = new int[w * h]; 50 * PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w); 51 * try { 52 * pg.grabPixels(); 53 * } catch (InterruptedException e) { 54 * System.err.println("interrupted waiting for pixels!"); 55 * return; 56 * } 57 * if ((pg.getStatus() & ImageObserver.ABORT) != 0) { 58 * System.err.println("image fetch aborted or errored"); 59 * return; 60 * } 61 * for (int j = 0; j < h; j++) { 62 * for (int i = 0; i < w; i++) { 63 * handlesinglepixel(x+i, y+j, pixels[j * w + i]); 64 * } 65 * } 66 * } 67 * 68 * }</pre> 69 * 70 * @see ColorModel#getRGBdefault 71 * 72 * @author Jim Graham 73 */ 74 public class PixelGrabber implements ImageConsumer { 75 ImageProducer producer; 76 77 int dstX; 78 int dstY; 79 int dstW; 80 int dstH; 81 82 ColorModel imageModel; 83 byte[] bytePixels; 84 int[] intPixels; 85 int dstOff; 86 int dstScan; 87 88 private boolean grabbing; 89 private int flags; 90 91 private static final int GRABBEDBITS = (ImageObserver.FRAMEBITS 92 | ImageObserver.ALLBITS); 93 private static final int DONEBITS = (GRABBEDBITS 94 | ImageObserver.ERROR); 95 96 /** 97 * Create a PixelGrabber object to grab the (x, y, w, h) rectangular 98 * section of pixels from the specified image into the given array. 99 * The pixels are stored into the array in the default RGB ColorModel. 100 * The RGB data for pixel (i, j) where (i, j) is inside the rectangle 101 * (x, y, w, h) is stored in the array at 102 * {@code pix[(j - y) * scansize + (i - x) + off]}. 103 * @see ColorModel#getRGBdefault 104 * @param img the image to retrieve pixels from 105 * @param x the x coordinate of the upper left corner of the rectangle 106 * of pixels to retrieve from the image, relative to the default 107 * (unscaled) size of the image 108 * @param y the y coordinate of the upper left corner of the rectangle 109 * of pixels to retrieve from the image 110 * @param w the width of the rectangle of pixels to retrieve 111 * @param h the height of the rectangle of pixels to retrieve 112 * @param pix the array of integers which are to be used to hold the 113 * RGB pixels retrieved from the image 114 * @param off the offset into the array of where to store the first pixel 115 * @param scansize the distance from one row of pixels to the next in 116 * the array 117 */ 118 public PixelGrabber(Image img, int x, int y, int w, int h, 119 int[] pix, int off, int scansize) { 120 this(img.getSource(), x, y, w, h, pix, off, scansize); 121 } 122 123 /** 124 * Create a PixelGrabber object to grab the (x, y, w, h) rectangular 125 * section of pixels from the image produced by the specified 126 * ImageProducer into the given array. 127 * The pixels are stored into the array in the default RGB ColorModel. 128 * The RGB data for pixel (i, j) where (i, j) is inside the rectangle 129 * (x, y, w, h) is stored in the array at 130 * {@code pix[(j - y) * scansize + (i - x) + off]}. 131 * @param ip the {@code ImageProducer} that produces the 132 * image from which to retrieve pixels 133 * @param x the x coordinate of the upper left corner of the rectangle 134 * of pixels to retrieve from the image, relative to the default 135 * (unscaled) size of the image 136 * @param y the y coordinate of the upper left corner of the rectangle 137 * of pixels to retrieve from the image 138 * @param w the width of the rectangle of pixels to retrieve 139 * @param h the height of the rectangle of pixels to retrieve 140 * @param pix the array of integers which are to be used to hold the 141 * RGB pixels retrieved from the image 142 * @param off the offset into the array of where to store the first pixel 143 * @param scansize the distance from one row of pixels to the next in 144 * the array 145 * @see ColorModel#getRGBdefault 146 */ 147 public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, 148 int[] pix, int off, int scansize) { 149 producer = ip; 150 dstX = x; 151 dstY = y; 152 dstW = w; 153 dstH = h; 154 dstOff = off; 155 dstScan = scansize; 156 intPixels = pix; 157 imageModel = ColorModel.getRGBdefault(); 158 } 159 160 /** 161 * Create a PixelGrabber object to grab the (x, y, w, h) rectangular 162 * section of pixels from the specified image. The pixels are 163 * accumulated in the original ColorModel if the same ColorModel 164 * is used for every call to setPixels, otherwise the pixels are 165 * accumulated in the default RGB ColorModel. If the forceRGB 166 * parameter is true, then the pixels will be accumulated in the 167 * default RGB ColorModel anyway. A buffer is allocated by the 168 * PixelGrabber to hold the pixels in either case. If {@code (w < 0)} or 169 * {@code (h < 0)}, then they will default to the remaining width and 170 * height of the source data when that information is delivered. 171 * @param img the image to retrieve the image data from 172 * @param x the x coordinate of the upper left corner of the rectangle 173 * of pixels to retrieve from the image, relative to the default 174 * (unscaled) size of the image 175 * @param y the y coordinate of the upper left corner of the rectangle 176 * of pixels to retrieve from the image 177 * @param w the width of the rectangle of pixels to retrieve 178 * @param h the height of the rectangle of pixels to retrieve 179 * @param forceRGB true if the pixels should always be converted to 180 * the default RGB ColorModel 181 */ 182 public PixelGrabber(Image img, int x, int y, int w, int h, 183 boolean forceRGB) 184 { 185 producer = img.getSource(); 186 dstX = x; 187 dstY = y; 188 dstW = w; 189 dstH = h; 190 if (forceRGB) { 191 imageModel = ColorModel.getRGBdefault(); 192 } 193 } 194 195 /** 196 * Request the PixelGrabber to start fetching the pixels. 197 */ 198 public synchronized void startGrabbing() { 199 if ((flags & DONEBITS) != 0) { 200 return; 201 } 202 if (!grabbing) { 203 grabbing = true; 204 flags &= ~(ImageObserver.ABORT); 205 producer.startProduction(this); 206 } 207 } 208 209 /** 210 * Request the PixelGrabber to abort the image fetch. 211 */ 212 public synchronized void abortGrabbing() { 213 imageComplete(IMAGEABORTED); 214 } 215 216 /** 217 * Request the Image or ImageProducer to start delivering pixels and 218 * wait for all of the pixels in the rectangle of interest to be 219 * delivered. 220 * @return true if the pixels were successfully grabbed, false on 221 * abort, error or timeout 222 * @exception InterruptedException 223 * Another thread has interrupted this thread. 224 */ 225 public boolean grabPixels() throws InterruptedException { 226 return grabPixels(0); 227 } 228 229 /** 230 * Request the Image or ImageProducer to start delivering pixels and 231 * wait for all of the pixels in the rectangle of interest to be 232 * delivered or until the specified timeout has elapsed. This method 233 * behaves in the following ways, depending on the value of 234 * {@code ms}: 235 * <ul> 236 * <li> If {@code ms == 0}, waits until all pixels are delivered 237 * <li> If {@code ms > 0}, waits until all pixels are delivered 238 * as timeout expires. 239 * <li> If {@code ms < 0}, returns {@code true} if all pixels 240 * are grabbed, {@code false} otherwise and does not wait. 241 * </ul> 242 * @param ms the number of milliseconds to wait for the image pixels 243 * to arrive before timing out 244 * @return true if the pixels were successfully grabbed, false on 245 * abort, error or timeout 246 * @exception InterruptedException 247 * Another thread has interrupted this thread. 248 */ 249 public synchronized boolean grabPixels(long ms) 250 throws InterruptedException 251 { 252 if ((flags & DONEBITS) != 0) { 253 return (flags & GRABBEDBITS) != 0; 254 } 255 long end = ms + System.currentTimeMillis(); 256 if (!grabbing) { 257 grabbing = true; 258 flags &= ~(ImageObserver.ABORT); 259 producer.startProduction(this); 260 } 261 while (grabbing) { 262 long timeout; 263 if (ms == 0) { 264 timeout = 0; 265 } else { 266 timeout = end - System.currentTimeMillis(); 267 if (timeout <= 0) { 268 break; 269 } 270 } 271 wait(timeout); 272 } 273 return (flags & GRABBEDBITS) != 0; 274 } 275 276 /** 277 * Return the status of the pixels. The ImageObserver flags 278 * representing the available pixel information are returned. 279 * @return the bitwise OR of all relevant ImageObserver flags 280 * @see ImageObserver 281 */ 282 public synchronized int getStatus() { 283 return flags; 284 } 285 286 /** 287 * Get the width of the pixel buffer (after adjusting for image width). 288 * If no width was specified for the rectangle of pixels to grab then 289 * then this information will only be available after the image has 290 * delivered the dimensions. 291 * @return the final width used for the pixel buffer or -1 if the width 292 * is not yet known 293 * @see #getStatus 294 */ 295 public synchronized int getWidth() { 296 return (dstW < 0) ? -1 : dstW; 297 } 298 299 /** 300 * Get the height of the pixel buffer (after adjusting for image height). 301 * If no width was specified for the rectangle of pixels to grab then 302 * then this information will only be available after the image has 303 * delivered the dimensions. 304 * @return the final height used for the pixel buffer or -1 if the height 305 * is not yet known 306 * @see #getStatus 307 */ 308 public synchronized int getHeight() { 309 return (dstH < 0) ? -1 : dstH; 310 } 311 312 /** 313 * Get the pixel buffer. If the PixelGrabber was not constructed 314 * with an explicit pixel buffer to hold the pixels then this method 315 * will return null until the size and format of the image data is 316 * known. 317 * Since the PixelGrabber may fall back on accumulating the data 318 * in the default RGB ColorModel at any time if the source image 319 * uses more than one ColorModel to deliver the data, the array 320 * object returned by this method may change over time until the 321 * image grab is complete. 322 * @return either a byte array or an int array 323 * @see #getStatus 324 * @see #setPixels(int, int, int, int, ColorModel, byte[], int, int) 325 * @see #setPixels(int, int, int, int, ColorModel, int[], int, int) 326 */ 327 public synchronized Object getPixels() { 328 return (bytePixels == null) 329 ? ((Object) intPixels) 330 : ((Object) bytePixels); 331 } 332 333 /** 334 * Get the ColorModel for the pixels stored in the array. If the 335 * PixelGrabber was constructed with an explicit pixel buffer then 336 * this method will always return the default RGB ColorModel, 337 * otherwise it may return null until the ColorModel used by the 338 * ImageProducer is known. 339 * Since the PixelGrabber may fall back on accumulating the data 340 * in the default RGB ColorModel at any time if the source image 341 * uses more than one ColorModel to deliver the data, the ColorModel 342 * object returned by this method may change over time until the 343 * image grab is complete and may not reflect any of the ColorModel 344 * objects that was used by the ImageProducer to deliver the pixels. 345 * @return the ColorModel object used for storing the pixels 346 * @see #getStatus 347 * @see ColorModel#getRGBdefault 348 * @see #setColorModel(ColorModel) 349 */ 350 public synchronized ColorModel getColorModel() { 351 return imageModel; 352 } 353 354 /** 355 * The setDimensions method is part of the ImageConsumer API which 356 * this class must implement to retrieve the pixels. 357 * <p> 358 * Note: This method is intended to be called by the ImageProducer 359 * of the Image whose pixels are being grabbed. Developers using 360 * this class to retrieve pixels from an image should avoid calling 361 * this method directly since that operation could result in problems 362 * with retrieving the requested pixels. 363 * @param width the width of the dimension 364 * @param height the height of the dimension 365 */ 366 public void setDimensions(int width, int height) { 367 if (dstW < 0) { 368 dstW = width - dstX; 369 } 370 if (dstH < 0) { 371 dstH = height - dstY; 372 } 373 if (dstW <= 0 || dstH <= 0) { 374 imageComplete(STATICIMAGEDONE); 375 } else if (intPixels == null && 376 imageModel == ColorModel.getRGBdefault()) { 377 intPixels = new int[dstW * dstH]; 378 dstScan = dstW; 379 dstOff = 0; 380 } 381 flags |= (ImageObserver.WIDTH | ImageObserver.HEIGHT); 382 } 383 384 /** 385 * The setHints method is part of the ImageConsumer API which 386 * this class must implement to retrieve the pixels. 387 * <p> 388 * Note: This method is intended to be called by the ImageProducer 389 * of the Image whose pixels are being grabbed. Developers using 390 * this class to retrieve pixels from an image should avoid calling 391 * this method directly since that operation could result in problems 392 * with retrieving the requested pixels. 393 * @param hints a set of hints used to process the pixels 394 */ 395 public void setHints(int hints) { 396 return; 397 } 398 399 /** 400 * The setProperties method is part of the ImageConsumer API which 401 * this class must implement to retrieve the pixels. 402 * <p> 403 * Note: This method is intended to be called by the ImageProducer 404 * of the Image whose pixels are being grabbed. Developers using 405 * this class to retrieve pixels from an image should avoid calling 406 * this method directly since that operation could result in problems 407 * with retrieving the requested pixels. 408 * @param props the list of properties 409 */ 410 public void setProperties(Hashtable<?,?> props) { 411 return; 412 } 413 414 /** 415 * The setColorModel method is part of the ImageConsumer API which 416 * this class must implement to retrieve the pixels. 417 * <p> 418 * Note: This method is intended to be called by the ImageProducer 419 * of the Image whose pixels are being grabbed. Developers using 420 * this class to retrieve pixels from an image should avoid calling 421 * this method directly since that operation could result in problems 422 * with retrieving the requested pixels. 423 * @param model the specified {@code ColorModel} 424 * @see #getColorModel 425 */ 426 public void setColorModel(ColorModel model) { 427 return; 428 } 429 430 private void convertToRGB() { 431 int size = dstW * dstH; 432 int[] newpixels = new int[size]; 433 if (bytePixels != null) { 434 for (int i = 0; i < size; i++) { 435 newpixels[i] = imageModel.getRGB(bytePixels[i] & 0xff); 436 } 437 } else if (intPixels != null) { 438 for (int i = 0; i < size; i++) { 439 newpixels[i] = imageModel.getRGB(intPixels[i]); 440 } 441 } 442 bytePixels = null; 443 intPixels = newpixels; 444 dstScan = dstW; 445 dstOff = 0; 446 imageModel = ColorModel.getRGBdefault(); 447 } 448 449 /** 450 * The setPixels method is part of the ImageConsumer API which 451 * this class must implement to retrieve the pixels. 452 * <p> 453 * Note: This method is intended to be called by the ImageProducer 454 * of the Image whose pixels are being grabbed. Developers using 455 * this class to retrieve pixels from an image should avoid calling 456 * this method directly since that operation could result in problems 457 * with retrieving the requested pixels. 458 * @param srcX the X coordinate of the upper-left corner 459 * of the area of pixels to be set 460 * @param srcY the Y coordinate of the upper-left corner 461 * of the area of pixels to be set 462 * @param srcW the width of the area of pixels 463 * @param srcH the height of the area of pixels 464 * @param model the specified {@code ColorModel} 465 * @param pixels the array of pixels 466 * @param srcOff the offset into the pixels array 467 * @param srcScan the distance from one row of pixels to the next 468 * in the pixels array 469 * @see #getPixels 470 */ 471 public void setPixels(int srcX, int srcY, int srcW, int srcH, 472 ColorModel model, 473 byte[] pixels, int srcOff, int srcScan) { 474 if (srcY < dstY) { 475 int diff = dstY - srcY; 476 if (diff >= srcH) { 477 return; 478 } 479 srcOff += srcScan * diff; 480 srcY += diff; 481 srcH -= diff; 482 } 483 if (srcY + srcH > dstY + dstH) { 484 srcH = (dstY + dstH) - srcY; 485 if (srcH <= 0) { 486 return; 487 } 488 } 489 if (srcX < dstX) { 490 int diff = dstX - srcX; 491 if (diff >= srcW) { 492 return; 493 } 494 srcOff += diff; 495 srcX += diff; 496 srcW -= diff; 497 } 498 if (srcX + srcW > dstX + dstW) { 499 srcW = (dstX + dstW) - srcX; 500 if (srcW <= 0) { 501 return; 502 } 503 } 504 int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX); 505 if (intPixels == null) { 506 if (bytePixels == null) { 507 bytePixels = new byte[dstW * dstH]; 508 dstScan = dstW; 509 dstOff = 0; 510 imageModel = model; 511 } else if (imageModel != model) { 512 convertToRGB(); 513 } 514 if (bytePixels != null) { 515 for (int h = srcH; h > 0; h--) { 516 System.arraycopy(pixels, srcOff, bytePixels, dstPtr, srcW); 517 srcOff += srcScan; 518 dstPtr += dstScan; 519 } 520 } 521 } 522 if (intPixels != null) { 523 int dstRem = dstScan - srcW; 524 int srcRem = srcScan - srcW; 525 for (int h = srcH; h > 0; h--) { 526 for (int w = srcW; w > 0; w--) { 527 intPixels[dstPtr++] = model.getRGB(pixels[srcOff++]&0xff); 528 } 529 srcOff += srcRem; 530 dstPtr += dstRem; 531 } 532 } 533 flags |= ImageObserver.SOMEBITS; 534 } 535 536 /** 537 * The setPixels method is part of the ImageConsumer API which 538 * this class must implement to retrieve the pixels. 539 * <p> 540 * Note: This method is intended to be called by the ImageProducer 541 * of the Image whose pixels are being grabbed. Developers using 542 * this class to retrieve pixels from an image should avoid calling 543 * this method directly since that operation could result in problems 544 * with retrieving the requested pixels. 545 * @param srcX the X coordinate of the upper-left corner 546 * of the area of pixels to be set 547 * @param srcY the Y coordinate of the upper-left corner 548 * of the area of pixels to be set 549 * @param srcW the width of the area of pixels 550 * @param srcH the height of the area of pixels 551 * @param model the specified {@code ColorModel} 552 * @param pixels the array of pixels 553 * @param srcOff the offset into the pixels array 554 * @param srcScan the distance from one row of pixels to the next 555 * in the pixels array 556 * @see #getPixels 557 */ 558 public void setPixels(int srcX, int srcY, int srcW, int srcH, 559 ColorModel model, 560 int[] pixels, int srcOff, int srcScan) { 561 if (srcY < dstY) { 562 int diff = dstY - srcY; 563 if (diff >= srcH) { 564 return; 565 } 566 srcOff += srcScan * diff; 567 srcY += diff; 568 srcH -= diff; 569 } 570 if (srcY + srcH > dstY + dstH) { 571 srcH = (dstY + dstH) - srcY; 572 if (srcH <= 0) { 573 return; 574 } 575 } 576 if (srcX < dstX) { 577 int diff = dstX - srcX; 578 if (diff >= srcW) { 579 return; 580 } 581 srcOff += diff; 582 srcX += diff; 583 srcW -= diff; 584 } 585 if (srcX + srcW > dstX + dstW) { 586 srcW = (dstX + dstW) - srcX; 587 if (srcW <= 0) { 588 return; 589 } 590 } 591 if (intPixels == null) { 592 if (bytePixels == null) { 593 intPixels = new int[dstW * dstH]; 594 dstScan = dstW; 595 dstOff = 0; 596 imageModel = model; 597 } else { 598 convertToRGB(); 599 } 600 } 601 int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX); 602 if (imageModel == model) { 603 for (int h = srcH; h > 0; h--) { 604 System.arraycopy(pixels, srcOff, intPixels, dstPtr, srcW); 605 srcOff += srcScan; 606 dstPtr += dstScan; 607 } 608 } else { 609 if (imageModel != ColorModel.getRGBdefault()) { 610 convertToRGB(); 611 } 612 int dstRem = dstScan - srcW; 613 int srcRem = srcScan - srcW; 614 for (int h = srcH; h > 0; h--) { 615 for (int w = srcW; w > 0; w--) { 616 intPixels[dstPtr++] = model.getRGB(pixels[srcOff++]); 617 } 618 srcOff += srcRem; 619 dstPtr += dstRem; 620 } 621 } 622 flags |= ImageObserver.SOMEBITS; 623 } 624 625 /** 626 * The imageComplete method is part of the ImageConsumer API which 627 * this class must implement to retrieve the pixels. 628 * <p> 629 * Note: This method is intended to be called by the ImageProducer 630 * of the Image whose pixels are being grabbed. Developers using 631 * this class to retrieve pixels from an image should avoid calling 632 * this method directly since that operation could result in problems 633 * with retrieving the requested pixels. 634 * @param status the status of image loading 635 */ 636 public synchronized void imageComplete(int status) { 637 grabbing = false; 638 switch (status) { 639 default: 640 case IMAGEERROR: 641 flags |= ImageObserver.ERROR | ImageObserver.ABORT; 642 break; 643 case IMAGEABORTED: 644 flags |= ImageObserver.ABORT; 645 break; 646 case STATICIMAGEDONE: 647 flags |= ImageObserver.ALLBITS; 648 break; 649 case SINGLEFRAMEDONE: 650 flags |= ImageObserver.FRAMEBITS; 651 break; 652 } 653 producer.removeConsumer(this); 654 notifyAll(); 655 } 656 657 /** 658 * Returns the status of the pixels. The ImageObserver flags 659 * representing the available pixel information are returned. 660 * This method and {@link #getStatus() getStatus} have the 661 * same implementation, but {@code getStatus} is the 662 * preferred method because it conforms to the convention of 663 * naming information-retrieval methods with the form 664 * "getXXX". 665 * @return the bitwise OR of all relevant ImageObserver flags 666 * @see ImageObserver 667 * @see #getStatus() 668 */ 669 public synchronized int status() { 670 return flags; 671 } 672 }