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