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