1. /*
  2. * @(#)PixelGrabber.java 1.14 01/11/29
  3. *
  4. * Copyright 2002 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.14, 11/29/01
  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. * @see ColorModel#getRGBdefault
  106. * @param img the image to retrieve pixels from
  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. */
  120. public PixelGrabber(ImageProducer ip, int x, int y, int w, int h,
  121. int[] pix, int off, int scansize) {
  122. producer = ip;
  123. dstX = x;
  124. dstY = y;
  125. dstW = w;
  126. dstH = h;
  127. dstOff = off;
  128. dstScan = scansize;
  129. intPixels = pix;
  130. imageModel = ColorModel.getRGBdefault();
  131. }
  132. /**
  133. * Create a PixelGrabber object to grab the (x, y, w, h) rectangular
  134. * section of pixels from the specified image. The pixels are
  135. * accumulated in the original ColorModel if the same ColorModel
  136. * is used for every call to setPixels, otherwise the pixels are
  137. * accumulated in the default RGB ColorModel. If the forceRGB
  138. * parameter is true, then the pixels will be accumulated in the
  139. * default RGB ColorModel anywya. A buffer is allocated by the
  140. * PixelGrabber to hold the pixels in either case. If (w < 0) or
  141. * (h < 0), then they will default to the remaining width and
  142. * height of the source data when that information is delivered.
  143. * @param img the image to retrieve the image data from
  144. * @param x the x coordinate of the upper left corner of the rectangle
  145. * of pixels to retrieve from the image, relative to the default
  146. * (unscaled) size of the image
  147. * @param y the y coordinate of the upper left corner of the rectangle
  148. * of pixels to retrieve from the image
  149. * @param w the width of the rectangle of pixels to retrieve
  150. * @param h the height of the rectangle of pixels to retrieve
  151. * @param forceRGB true if the pixels should always be converted to
  152. * the default RGB ColorModel
  153. */
  154. public PixelGrabber(Image img, int x, int y, int w, int h,
  155. boolean forceRGB)
  156. {
  157. producer = img.getSource();
  158. dstX = x;
  159. dstY = y;
  160. dstW = w;
  161. dstH = h;
  162. if (forceRGB) {
  163. imageModel = ColorModel.getRGBdefault();
  164. }
  165. }
  166. /**
  167. * Request the PixelGrabber to start fetching the pixels.
  168. */
  169. public synchronized void startGrabbing() {
  170. if ((flags & DONEBITS) != 0) {
  171. return;
  172. }
  173. if (!grabbing) {
  174. grabbing = true;
  175. flags &= ~(ImageObserver.ABORT);
  176. producer.startProduction(this);
  177. }
  178. }
  179. /**
  180. * Request the PixelGrabber to abort the image fetch.
  181. */
  182. public synchronized void abortGrabbing() {
  183. imageComplete(IMAGEABORTED);
  184. }
  185. /**
  186. * Request the Image or ImageProducer to start delivering pixels and
  187. * wait for all of the pixels in the rectangle of interest to be
  188. * delivered.
  189. * @return true if the pixels were successfully grabbed, false on
  190. * abort, error or timeout
  191. * @exception InterruptedException
  192. * Another thread has interrupted this thread.
  193. */
  194. public boolean grabPixels() throws InterruptedException {
  195. return grabPixels(0);
  196. }
  197. /**
  198. * Request the Image or ImageProducer to start delivering pixels and
  199. * wait for all of the pixels in the rectangle of interest to be
  200. * delivered or until the specified timeout has elapsed.
  201. * @param ms the number of milliseconds to wait for the image pixels
  202. * to arrive before timing out
  203. * @return true if the pixels were successfully grabbed, false on
  204. * abort, error or timeout
  205. * @exception InterruptedException
  206. * Another thread has interrupted this thread.
  207. */
  208. public synchronized boolean grabPixels(long ms)
  209. throws InterruptedException
  210. {
  211. if ((flags & DONEBITS) != 0) {
  212. return (flags & GRABBEDBITS) != 0;
  213. }
  214. long end = ms + System.currentTimeMillis();
  215. if (!grabbing) {
  216. grabbing = true;
  217. flags &= ~(ImageObserver.ABORT);
  218. producer.startProduction(this);
  219. }
  220. while (grabbing) {
  221. long timeout;
  222. if (ms == 0) {
  223. timeout = 0;
  224. } else {
  225. timeout = end - System.currentTimeMillis();
  226. if (timeout <= 0) {
  227. break;
  228. }
  229. }
  230. wait(timeout);
  231. }
  232. return (flags & GRABBEDBITS) != 0;
  233. }
  234. /**
  235. * Return the status of the pixels. The ImageObserver flags
  236. * representing the available pixel information are returned.
  237. * @see ImageObserver
  238. * @return the bitwise OR of all relevant ImageObserver flags
  239. */
  240. public synchronized int getStatus() {
  241. return flags;
  242. }
  243. /**
  244. * Get the width of the pixel buffer (after adjusting for image width).
  245. * If no width was specified for the rectangle of pixels to grab then
  246. * then this information will only be available after the image has
  247. * delivered the dimensions.
  248. * @return the final width used for the pixel buffer or -1 if the width
  249. * is not yet known
  250. * @see #getStatus
  251. */
  252. public synchronized int getWidth() {
  253. return (dstW < 0) ? -1 : dstW;
  254. }
  255. /**
  256. * Get the height of the pixel buffer (after adjusting for image height).
  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 height used for the pixel buffer or -1 if the height
  261. * is not yet known
  262. * @see #getStatus
  263. */
  264. public synchronized int getHeight() {
  265. return (dstH < 0) ? -1 : dstH;
  266. }
  267. /**
  268. * Get the pixel buffer. If the PixelGrabber was not constructed
  269. * with an explicit pixel buffer to hold the pixels then this method
  270. * will return null until the size and format of the image data is
  271. * known.
  272. * Since the PixelGrabber may fall back on accumulating the data
  273. * in the default RGB ColorModel at any time if the source image
  274. * uses more than one ColorModel to deliver the data, the array
  275. * object returned by this method may change over time until the
  276. * image grab is complete.
  277. * @return either a byte array or an int array
  278. * @see #getStatus
  279. */
  280. public synchronized Object getPixels() {
  281. return (bytePixels == null)
  282. ? ((Object) intPixels)
  283. : ((Object) bytePixels);
  284. }
  285. /**
  286. * Get the ColorModel for the pixels stored in the array. If the
  287. * PixelGrabber was constructed with an explicit pixel buffer then
  288. * this method will always return the default RGB ColorModel,
  289. * otherwise it may return null until the ColorModel used by the
  290. * ImageProducer is known.
  291. * Since the PixelGrabber may fall back on accumulating the data
  292. * in the default RGB ColorModel at any time if the source image
  293. * uses more than one ColorModel to deliver the data, the ColorModel
  294. * object returned by this method may change over time until the
  295. * image grab is complete and may not reflect any of the ColorModel
  296. * objects that was used by the ImageProducer to deliver the pixels.
  297. * @return the ColorModel object used for storing the pixels
  298. * @see #getStatus
  299. * @see ColorModel#getRGBdefault
  300. */
  301. public synchronized ColorModel getColorModel() {
  302. return imageModel;
  303. }
  304. /**
  305. * The setDimensions method is part of the ImageConsumer API which
  306. * this class must implement to retrieve the pixels.
  307. * <p>
  308. * Note: This method is intended to be called by the ImageProducer
  309. * of the Image whose pixels are being grabbed. Developers using
  310. * this class to retrieve pixels from an image should avoid calling
  311. * this method directly since that operation could result in problems
  312. * with retrieving the requested pixels.
  313. */
  314. public void setDimensions(int width, int height) {
  315. if (dstW < 0) {
  316. dstW = width - dstX;
  317. }
  318. if (dstH < 0) {
  319. dstH = height - dstY;
  320. }
  321. if (dstW <= 0 || dstH <= 0) {
  322. imageComplete(STATICIMAGEDONE);
  323. } else if (intPixels == null &&
  324. imageModel == ColorModel.getRGBdefault()) {
  325. intPixels = new int[dstW * dstH];
  326. dstScan = dstW;
  327. dstOff = 0;
  328. }
  329. flags |= (ImageObserver.WIDTH | ImageObserver.HEIGHT);
  330. }
  331. /**
  332. * The setHints method is part of the ImageConsumer API which
  333. * this class must implement to retrieve the pixels.
  334. * <p>
  335. * Note: This method is intended to be called by the ImageProducer
  336. * of the Image whose pixels are being grabbed. Developers using
  337. * this class to retrieve pixels from an image should avoid calling
  338. * this method directly since that operation could result in problems
  339. * with retrieving the requested pixels.
  340. */
  341. public void setHints(int hints) {
  342. return;
  343. }
  344. /**
  345. * The setProperties method is part of the ImageConsumer API which
  346. * this class must implement to retrieve the pixels.
  347. * <p>
  348. * Note: This method is intended to be called by the ImageProducer
  349. * of the Image whose pixels are being grabbed. Developers using
  350. * this class to retrieve pixels from an image should avoid calling
  351. * this method directly since that operation could result in problems
  352. * with retrieving the requested pixels.
  353. */
  354. public void setProperties(Hashtable props) {
  355. return;
  356. }
  357. /**
  358. * The setColorModel method is part of the ImageConsumer API which
  359. * this class must implement to retrieve the pixels.
  360. * <p>
  361. * Note: This method is intended to be called by the ImageProducer
  362. * of the Image whose pixels are being grabbed. Developers using
  363. * this class to retrieve pixels from an image should avoid calling
  364. * this method directly since that operation could result in problems
  365. * with retrieving the requested pixels.
  366. */
  367. public void setColorModel(ColorModel model) {
  368. return;
  369. }
  370. private void convertToRGB() {
  371. int size = dstW * dstH;
  372. int newpixels[] = new int[size];
  373. if (bytePixels != null) {
  374. for (int i = 0; i < size; i++) {
  375. newpixels[i] = imageModel.getRGB(bytePixels[i] & 0xff);
  376. }
  377. } else if (intPixels != null) {
  378. for (int i = 0; i < size; i++) {
  379. newpixels[i] = imageModel.getRGB(intPixels[i]);
  380. }
  381. }
  382. bytePixels = null;
  383. intPixels = newpixels;
  384. dstScan = dstW;
  385. dstOff = 0;
  386. imageModel = ColorModel.getRGBdefault();
  387. }
  388. /**
  389. * The setPixels method is part of the ImageConsumer API which
  390. * this class must implement to retrieve the pixels.
  391. * <p>
  392. * Note: This method is intended to be called by the ImageProducer
  393. * of the Image whose pixels are being grabbed. Developers using
  394. * this class to retrieve pixels from an image should avoid calling
  395. * this method directly since that operation could result in problems
  396. * with retrieving the requested pixels.
  397. */
  398. public void setPixels(int srcX, int srcY, int srcW, int srcH,
  399. ColorModel model,
  400. byte pixels[], int srcOff, int srcScan) {
  401. if (srcY < dstY) {
  402. int diff = dstY - srcY;
  403. if (diff >= srcH) {
  404. return;
  405. }
  406. srcOff += srcScan * diff;
  407. srcY += diff;
  408. srcH -= diff;
  409. }
  410. if (srcY + srcH > dstY + dstH) {
  411. srcH = (dstY + dstH) - srcY;
  412. if (srcH <= 0) {
  413. return;
  414. }
  415. }
  416. if (srcX < dstX) {
  417. int diff = dstX - srcX;
  418. if (diff >= srcW) {
  419. return;
  420. }
  421. srcOff += diff;
  422. srcX += diff;
  423. srcW -= diff;
  424. }
  425. if (srcX + srcW > dstX + dstW) {
  426. srcW = (dstX + dstW) - srcX;
  427. if (srcW <= 0) {
  428. return;
  429. }
  430. }
  431. int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX);
  432. if (intPixels == null) {
  433. if (bytePixels == null) {
  434. bytePixels = new byte[dstW * dstH];
  435. dstScan = dstW;
  436. dstOff = 0;
  437. imageModel = model;
  438. } else if (imageModel != model) {
  439. convertToRGB();
  440. }
  441. if (bytePixels != null) {
  442. for (int h = srcH; h > 0; h--) {
  443. System.arraycopy(pixels, srcOff, bytePixels, dstPtr, srcW);
  444. srcOff += srcScan;
  445. dstPtr += dstScan;
  446. }
  447. }
  448. }
  449. if (intPixels != null) {
  450. int dstRem = dstScan - srcW;
  451. int srcRem = srcScan - srcW;
  452. for (int h = srcH; h > 0; h--) {
  453. for (int w = srcW; w > 0; w--) {
  454. intPixels[dstPtr++] = model.getRGB(pixels[srcOff++]&0xff);
  455. }
  456. srcOff += srcRem;
  457. dstPtr += dstRem;
  458. }
  459. }
  460. flags |= ImageObserver.SOMEBITS;
  461. }
  462. /**
  463. * The setPixels method is part of the ImageConsumer API which
  464. * this class must implement to retrieve the pixels.
  465. * <p>
  466. * Note: This method is intended to be called by the ImageProducer
  467. * of the Image whose pixels are being grabbed. Developers using
  468. * this class to retrieve pixels from an image should avoid calling
  469. * this method directly since that operation could result in problems
  470. * with retrieving the requested pixels.
  471. */
  472. public void setPixels(int srcX, int srcY, int srcW, int srcH,
  473. ColorModel model,
  474. int pixels[], int srcOff, int srcScan) {
  475. if (srcY < dstY) {
  476. int diff = dstY - srcY;
  477. if (diff >= srcH) {
  478. return;
  479. }
  480. srcOff += srcScan * diff;
  481. srcY += diff;
  482. srcH -= diff;
  483. }
  484. if (srcY + srcH > dstY + dstH) {
  485. srcH = (dstY + dstH) - srcY;
  486. if (srcH <= 0) {
  487. return;
  488. }
  489. }
  490. if (srcX < dstX) {
  491. int diff = dstX - srcX;
  492. if (diff >= srcW) {
  493. return;
  494. }
  495. srcOff += diff;
  496. srcX += diff;
  497. srcW -= diff;
  498. }
  499. if (srcX + srcW > dstX + dstW) {
  500. srcW = (dstX + dstW) - srcX;
  501. if (srcW <= 0) {
  502. return;
  503. }
  504. }
  505. if (intPixels == null) {
  506. if (bytePixels == null) {
  507. intPixels = new int[dstW * dstH];
  508. dstScan = dstW;
  509. dstOff = 0;
  510. imageModel = model;
  511. } else {
  512. convertToRGB();
  513. }
  514. }
  515. int dstPtr = dstOff + (srcY - dstY) * dstScan + (srcX - dstX);
  516. if (imageModel == model) {
  517. for (int h = srcH; h > 0; h--) {
  518. System.arraycopy(pixels, srcOff, intPixels, dstPtr, srcW);
  519. srcOff += srcScan;
  520. dstPtr += dstScan;
  521. }
  522. } else {
  523. if (imageModel != ColorModel.getRGBdefault()) {
  524. convertToRGB();
  525. }
  526. int dstRem = dstScan - srcW;
  527. int srcRem = srcScan - srcW;
  528. for (int h = srcH; h > 0; h--) {
  529. for (int w = srcW; w > 0; w--) {
  530. intPixels[dstPtr++] = model.getRGB(pixels[srcOff++]);
  531. }
  532. srcOff += srcRem;
  533. dstPtr += dstRem;
  534. }
  535. }
  536. flags |= ImageObserver.SOMEBITS;
  537. }
  538. /**
  539. * The imageComplete method is part of the ImageConsumer API which
  540. * this class must implement to retrieve the pixels.
  541. * <p>
  542. * Note: This method is intended to be called by the ImageProducer
  543. * of the Image whose pixels are being grabbed. Developers using
  544. * this class to retrieve pixels from an image should avoid calling
  545. * this method directly since that operation could result in problems
  546. * with retrieving the requested pixels.
  547. */
  548. public synchronized void imageComplete(int status) {
  549. grabbing = false;
  550. switch (status) {
  551. default:
  552. case IMAGEERROR:
  553. flags |= ImageObserver.ERROR | ImageObserver.ABORT;
  554. break;
  555. case IMAGEABORTED:
  556. flags |= ImageObserver.ABORT;
  557. break;
  558. case STATICIMAGEDONE:
  559. flags |= ImageObserver.ALLBITS;
  560. break;
  561. case SINGLEFRAMEDONE:
  562. flags |= ImageObserver.FRAMEBITS;
  563. break;
  564. }
  565. producer.removeConsumer(this);
  566. notifyAll();
  567. }
  568. /**
  569. * DEPRECATED: Replaced by getStatus().
  570. */
  571. public synchronized int status() {
  572. return flags;
  573. }
  574. }