1. /*
  2. * @(#)MemoryImageSource.java 1.33 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.awt.image.ImageConsumer;
  9. import java.awt.image.ImageProducer;
  10. import java.awt.image.ColorModel;
  11. import java.util.Hashtable;
  12. import java.util.Vector;
  13. import java.util.Enumeration;
  14. /**
  15. * This class is an implementation of the ImageProducer interface which
  16. * uses an array to produce pixel values for an Image. Here is an example
  17. * which calculates a 100x100 image representing a fade from black to blue
  18. * along the X axis and a fade from black to red along the Y axis:
  19. * <pre>
  20. *
  21. * int w = 100;
  22. * int h = 100;
  23. * int pix[] = new int[w * h];
  24. * int index = 0;
  25. * for (int y = 0; y < h; y++) {
  26. * int red = (y * 255) / (h - 1);
  27. * for (int x = 0; x < w; x++) {
  28. * int blue = (x * 255) / (w - 1);
  29. * pix[index++] = (255 << 24) | (red << 16) | blue;
  30. * }
  31. * }
  32. * Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
  33. *
  34. * </pre>
  35. * The MemoryImageSource is also capable of managing a memory image which
  36. * varies over time to allow animation or custom rendering. Here is an
  37. * example showing how to set up the animation source and signal changes
  38. * in the data (adapted from the MemoryAnimationSourceDemo by Garth Dickie):
  39. * <pre>
  40. *
  41. * int pixels[];
  42. * MemoryImageSource source;
  43. *
  44. * public void init() {
  45. * int width = 50;
  46. * int height = 50;
  47. * int size = width * height;
  48. * pixels = new int[size];
  49. *
  50. * int value = getBackground().getRGB();
  51. * for (int i = 0; i < size; i++) {
  52. * pixels[i] = value;
  53. * }
  54. *
  55. * source = new MemoryImageSource(width, height, pixels, 0, width);
  56. * source.setAnimated(true);
  57. * image = createImage(source);
  58. * }
  59. *
  60. * public void run() {
  61. * Thread me = Thread.currentThread( );
  62. * me.setPriority(Thread.MIN_PRIORITY);
  63. *
  64. * while (true) {
  65. * try {
  66. * thread.sleep(10);
  67. * } catch( InterruptedException e ) {
  68. * return;
  69. * }
  70. *
  71. * // Modify the values in the pixels array at (x, y, w, h)
  72. *
  73. * // Send the new data to the interested ImageConsumers
  74. * source.newPixels(x, y, w, h);
  75. * }
  76. * }
  77. *
  78. * </pre>
  79. *
  80. * @see ImageProducer
  81. *
  82. * @version 1.33 07/16/04
  83. * @author Jim Graham
  84. * @author Animation capabilities inspired by the
  85. * MemoryAnimationSource class written by Garth Dickie
  86. */
  87. public class MemoryImageSource implements ImageProducer {
  88. int width;
  89. int height;
  90. ColorModel model;
  91. Object pixels;
  92. int pixeloffset;
  93. int pixelscan;
  94. Hashtable properties;
  95. Vector theConsumers = new Vector();
  96. boolean animating;
  97. boolean fullbuffers;
  98. /**
  99. * Constructs an ImageProducer object which uses an array of bytes
  100. * to produce data for an Image object.
  101. * @param w the width of the rectangle of pixels
  102. * @param h the height of the rectangle of pixels
  103. * @param cm the specified <code>ColorModel</code>
  104. * @param pix an array of pixels
  105. * @param off the offset into the array of where to store the
  106. * first pixel
  107. * @param scan the distance from one row of pixels to the next in
  108. * the array
  109. * @see java.awt.Component#createImage
  110. */
  111. public MemoryImageSource(int w, int h, ColorModel cm,
  112. byte[] pix, int off, int scan) {
  113. initialize(w, h, cm, (Object) pix, off, scan, null);
  114. }
  115. /**
  116. * Constructs an ImageProducer object which uses an array of bytes
  117. * to produce data for an Image object.
  118. * @param w the width of the rectangle of pixels
  119. * @param h the height of the rectangle of pixels
  120. * @param cm the specified <code>ColorModel</code>
  121. * @param pix an array of pixels
  122. * @param off the offset into the array of where to store the
  123. * first pixel
  124. * @param scan the distance from one row of pixels to the next in
  125. * the array
  126. * @param props a list of properties that the <code>ImageProducer</code>
  127. * uses to process an image
  128. * @see java.awt.Component#createImage
  129. */
  130. public MemoryImageSource(int w, int h, ColorModel cm,
  131. byte[] pix, int off, int scan,
  132. Hashtable<?,?> props)
  133. {
  134. initialize(w, h, cm, (Object) pix, off, scan, props);
  135. }
  136. /**
  137. * Constructs an ImageProducer object which uses an array of integers
  138. * to produce data for an Image object.
  139. * @param w the width of the rectangle of pixels
  140. * @param h the height of the rectangle of pixels
  141. * @param cm the specified <code>ColorModel</code>
  142. * @param pix an array of pixels
  143. * @param off the offset into the array of where to store the
  144. * first pixel
  145. * @param scan the distance from one row of pixels to the next in
  146. * the array
  147. * @see java.awt.Component#createImage
  148. */
  149. public MemoryImageSource(int w, int h, ColorModel cm,
  150. int[] pix, int off, int scan) {
  151. initialize(w, h, cm, (Object) pix, off, scan, null);
  152. }
  153. /**
  154. * Constructs an ImageProducer object which uses an array of integers
  155. * to produce data for an Image object.
  156. * @param w the width of the rectangle of pixels
  157. * @param h the height of the rectangle of pixels
  158. * @param cm the specified <code>ColorModel</code>
  159. * @param pix an array of pixels
  160. * @param off the offset into the array of where to store the
  161. * first pixel
  162. * @param scan the distance from one row of pixels to the next in
  163. * the array
  164. * @param props a list of properties that the <code>ImageProducer</code>
  165. * uses to process an image
  166. * @see java.awt.Component#createImage
  167. */
  168. public MemoryImageSource(int w, int h, ColorModel cm,
  169. int[] pix, int off, int scan,
  170. Hashtable<?,?> props)
  171. {
  172. initialize(w, h, cm, (Object) pix, off, scan, props);
  173. }
  174. private void initialize(int w, int h, ColorModel cm,
  175. Object pix, int off, int scan, Hashtable props) {
  176. width = w;
  177. height = h;
  178. model = cm;
  179. pixels = pix;
  180. pixeloffset = off;
  181. pixelscan = scan;
  182. if (props == null) {
  183. props = new Hashtable();
  184. }
  185. properties = props;
  186. }
  187. /**
  188. * Constructs an ImageProducer object which uses an array of integers
  189. * in the default RGB ColorModel to produce data for an Image object.
  190. * @param w the width of the rectangle of pixels
  191. * @param h the height of the rectangle of pixels
  192. * @param pix an array of pixels
  193. * @param off the offset into the array of where to store the
  194. * first pixel
  195. * @param scan the distance from one row of pixels to the next in
  196. * the array
  197. * @see java.awt.Component#createImage
  198. * @see ColorModel#getRGBdefault
  199. */
  200. public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
  201. initialize(w, h, ColorModel.getRGBdefault(),
  202. (Object) pix, off, scan, null);
  203. }
  204. /**
  205. * Constructs an ImageProducer object which uses an array of integers
  206. * in the default RGB ColorModel to produce data for an Image object.
  207. * @param w the width of the rectangle of pixels
  208. * @param h the height of the rectangle of pixels
  209. * @param pix an array of pixels
  210. * @param off the offset into the array of where to store the
  211. * first pixel
  212. * @param scan the distance from one row of pixels to the next in
  213. * the array
  214. * @param props a list of properties that the <code>ImageProducer</code>
  215. * uses to process an image
  216. * @see java.awt.Component#createImage
  217. * @see ColorModel#getRGBdefault
  218. */
  219. public MemoryImageSource(int w, int h, int pix[], int off, int scan,
  220. Hashtable<?,?> props)
  221. {
  222. initialize(w, h, ColorModel.getRGBdefault(),
  223. (Object) pix, off, scan, props);
  224. }
  225. /**
  226. * Adds an ImageConsumer to the list of consumers interested in
  227. * data for this image.
  228. * @param ic the specified <code>ImageConsumer</code>
  229. * @throws NullPointerException if the specified
  230. * <code>ImageConsumer</code> is null
  231. * @see ImageConsumer
  232. */
  233. public synchronized void addConsumer(ImageConsumer ic) {
  234. if (theConsumers.contains(ic)) {
  235. return;
  236. }
  237. theConsumers.addElement(ic);
  238. try {
  239. initConsumer(ic);
  240. sendPixels(ic, 0, 0, width, height);
  241. if (isConsumer(ic)) {
  242. ic.imageComplete(animating
  243. ? ImageConsumer.SINGLEFRAMEDONE
  244. : ImageConsumer.STATICIMAGEDONE);
  245. if (!animating && isConsumer(ic)) {
  246. ic.imageComplete(ImageConsumer.IMAGEERROR);
  247. removeConsumer(ic);
  248. }
  249. }
  250. } catch (Exception e) {
  251. if (isConsumer(ic)) {
  252. ic.imageComplete(ImageConsumer.IMAGEERROR);
  253. }
  254. }
  255. }
  256. /**
  257. * Determines if an ImageConsumer is on the list of consumers currently
  258. * interested in data for this image.
  259. * @param ic the specified <code>ImageConsumer</code>
  260. * @return <code>true</code> if the <code>ImageConsumer</code>
  261. * is on the list; <code>false</code> otherwise.
  262. * @see ImageConsumer
  263. */
  264. public synchronized boolean isConsumer(ImageConsumer ic) {
  265. return theConsumers.contains(ic);
  266. }
  267. /**
  268. * Removes an ImageConsumer from the list of consumers interested in
  269. * data for this image.
  270. * @param ic the specified <code>ImageConsumer</code>
  271. * @see ImageConsumer
  272. */
  273. public synchronized void removeConsumer(ImageConsumer ic) {
  274. theConsumers.removeElement(ic);
  275. }
  276. /**
  277. * Adds an ImageConsumer to the list of consumers interested in
  278. * data for this image and immediately starts delivery of the
  279. * image data through the ImageConsumer interface.
  280. * @param ic the specified <code>ImageConsumer</code>
  281. * image data through the ImageConsumer interface.
  282. * @see ImageConsumer
  283. */
  284. public void startProduction(ImageConsumer ic) {
  285. addConsumer(ic);
  286. }
  287. /**
  288. * Requests that a given ImageConsumer have the image data delivered
  289. * one more time in top-down, left-right order.
  290. * @param ic the specified <code>ImageConsumer</code>
  291. * @see ImageConsumer
  292. */
  293. public void requestTopDownLeftRightResend(ImageConsumer ic) {
  294. // Ignored. The data is either single frame and already in TDLR
  295. // format or it is multi-frame and TDLR resends aren't critical.
  296. }
  297. /**
  298. * Changes this memory image into a multi-frame animation or a
  299. * single-frame static image depending on the animated parameter.
  300. * <p>This method should be called immediately after the
  301. * MemoryImageSource is constructed and before an image is
  302. * created with it to ensure that all ImageConsumers will
  303. * receive the correct multi-frame data. If an ImageConsumer
  304. * is added to this ImageProducer before this flag is set then
  305. * that ImageConsumer will see only a snapshot of the pixel
  306. * data that was available when it connected.
  307. * @param animated <code>true</code> if the image is a
  308. * multi-frame animation
  309. */
  310. public synchronized void setAnimated(boolean animated) {
  311. this.animating = animated;
  312. if (!animating) {
  313. Enumeration enum_ = theConsumers.elements();
  314. while (enum_.hasMoreElements()) {
  315. ImageConsumer ic = (ImageConsumer) enum_.nextElement();
  316. ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
  317. if (isConsumer(ic)) {
  318. ic.imageComplete(ImageConsumer.IMAGEERROR);
  319. }
  320. }
  321. theConsumers.removeAllElements();
  322. }
  323. }
  324. /**
  325. * Specifies whether this animated memory image should always be
  326. * updated by sending the complete buffer of pixels whenever
  327. * there is a change.
  328. * This flag is ignored if the animation flag is not turned on
  329. * through the setAnimated() method.
  330. * <p>This method should be called immediately after the
  331. * MemoryImageSource is constructed and before an image is
  332. * created with it to ensure that all ImageConsumers will
  333. * receive the correct pixel delivery hints.
  334. * @param fullbuffers <code>true</code> if the complete pixel
  335. * buffer should always
  336. * be sent
  337. * @see #setAnimated
  338. */
  339. public synchronized void setFullBufferUpdates(boolean fullbuffers) {
  340. if (this.fullbuffers == fullbuffers) {
  341. return;
  342. }
  343. this.fullbuffers = fullbuffers;
  344. if (animating) {
  345. Enumeration enum_ = theConsumers.elements();
  346. while (enum_.hasMoreElements()) {
  347. ImageConsumer ic = (ImageConsumer) enum_.nextElement();
  348. ic.setHints(fullbuffers
  349. ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  350. ImageConsumer.COMPLETESCANLINES)
  351. : ImageConsumer.RANDOMPIXELORDER);
  352. }
  353. }
  354. }
  355. /**
  356. * Sends a whole new buffer of pixels to any ImageConsumers that
  357. * are currently interested in the data for this image and notify
  358. * them that an animation frame is complete.
  359. * This method only has effect if the animation flag has been
  360. * turned on through the setAnimated() method.
  361. * @see #newPixels(int, int, int, int, boolean)
  362. * @see ImageConsumer
  363. * @see #setAnimated
  364. */
  365. public void newPixels() {
  366. newPixels(0, 0, width, height, true);
  367. }
  368. /**
  369. * Sends a rectangular region of the buffer of pixels to any
  370. * ImageConsumers that are currently interested in the data for
  371. * this image and notify them that an animation frame is complete.
  372. * This method only has effect if the animation flag has been
  373. * turned on through the setAnimated() method.
  374. * If the full buffer update flag was turned on with the
  375. * setFullBufferUpdates() method then the rectangle parameters
  376. * will be ignored and the entire buffer will always be sent.
  377. * @param x the x coordinate of the upper left corner of the rectangle
  378. * of pixels to be sent
  379. * @param y the y coordinate of the upper left corner of the rectangle
  380. * of pixels to be sent
  381. * @param w the width of the rectangle of pixels to be sent
  382. * @param h the height of the rectangle of pixels to be sent
  383. * @see #newPixels(int, int, int, int, boolean)
  384. * @see ImageConsumer
  385. * @see #setAnimated
  386. * @see #setFullBufferUpdates
  387. */
  388. public synchronized void newPixels(int x, int y, int w, int h) {
  389. newPixels(x, y, w, h, true);
  390. }
  391. /**
  392. * Sends a rectangular region of the buffer of pixels to any
  393. * ImageConsumers that are currently interested in the data for
  394. * this image.
  395. * If the framenotify parameter is true then the consumers are
  396. * also notified that an animation frame is complete.
  397. * This method only has effect if the animation flag has been
  398. * turned on through the setAnimated() method.
  399. * If the full buffer update flag was turned on with the
  400. * setFullBufferUpdates() method then the rectangle parameters
  401. * will be ignored and the entire buffer will always be sent.
  402. * @param x the x coordinate of the upper left corner of the rectangle
  403. * of pixels to be sent
  404. * @param y the y coordinate of the upper left corner of the rectangle
  405. * of pixels to be sent
  406. * @param w the width of the rectangle of pixels to be sent
  407. * @param h the height of the rectangle of pixels to be sent
  408. * @param framenotify <code>true</code> if the consumers should be sent a
  409. * {@link ImageConsumer#SINGLEFRAMEDONE SINGLEFRAMEDONE} notification
  410. * @see ImageConsumer
  411. * @see #setAnimated
  412. * @see #setFullBufferUpdates
  413. */
  414. public synchronized void newPixels(int x, int y, int w, int h,
  415. boolean framenotify) {
  416. if (animating) {
  417. if (fullbuffers) {
  418. x = y = 0;
  419. w = width;
  420. h = height;
  421. } else {
  422. if (x < 0) {
  423. w += x;
  424. x = 0;
  425. }
  426. if (x + w > width) {
  427. w = width - x;
  428. }
  429. if (y < 0) {
  430. h += y;
  431. y = 0;
  432. }
  433. if (y + h > height) {
  434. h = height - y;
  435. }
  436. }
  437. if ((w <= 0 || h <= 0) && !framenotify) {
  438. return;
  439. }
  440. Enumeration enum_ = theConsumers.elements();
  441. while (enum_.hasMoreElements()) {
  442. ImageConsumer ic = (ImageConsumer) enum_.nextElement();
  443. if (w > 0 && h > 0) {
  444. sendPixels(ic, x, y, w, h);
  445. }
  446. if (framenotify && isConsumer(ic)) {
  447. ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
  448. }
  449. }
  450. }
  451. }
  452. /**
  453. * Changes to a new byte array to hold the pixels for this image.
  454. * If the animation flag has been turned on through the setAnimated()
  455. * method, then the new pixels will be immediately delivered to any
  456. * ImageConsumers that are currently interested in the data for
  457. * this image.
  458. * @param newpix the new pixel array
  459. * @param newmodel the specified <code>ColorModel</code>
  460. * @param offset the offset into the array
  461. * @param scansize the distance from one row of pixels to the next in
  462. * the array
  463. * @see #newPixels(int, int, int, int, boolean)
  464. * @see #setAnimated
  465. */
  466. public synchronized void newPixels(byte[] newpix, ColorModel newmodel,
  467. int offset, int scansize) {
  468. this.pixels = newpix;
  469. this.model = newmodel;
  470. this.pixeloffset = offset;
  471. this.pixelscan = scansize;
  472. newPixels();
  473. }
  474. /**
  475. * Changes to a new int array to hold the pixels for this image.
  476. * If the animation flag has been turned on through the setAnimated()
  477. * method, then the new pixels will be immediately delivered to any
  478. * ImageConsumers that are currently interested in the data for
  479. * this image.
  480. * @param newpix the new pixel array
  481. * @param newmodel the specified <code>ColorModel</code>
  482. * @param offset the offset into the array
  483. * @param scansize the distance from one row of pixels to the next in
  484. * the array
  485. * @see #newPixels(int, int, int, int, boolean)
  486. * @see #setAnimated
  487. */
  488. public synchronized void newPixels(int[] newpix, ColorModel newmodel,
  489. int offset, int scansize) {
  490. this.pixels = newpix;
  491. this.model = newmodel;
  492. this.pixeloffset = offset;
  493. this.pixelscan = scansize;
  494. newPixels();
  495. }
  496. private void initConsumer(ImageConsumer ic) {
  497. if (isConsumer(ic)) {
  498. ic.setDimensions(width, height);
  499. }
  500. if (isConsumer(ic)) {
  501. ic.setProperties(properties);
  502. }
  503. if (isConsumer(ic)) {
  504. ic.setColorModel(model);
  505. }
  506. if (isConsumer(ic)) {
  507. ic.setHints(animating
  508. ? (fullbuffers
  509. ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  510. ImageConsumer.COMPLETESCANLINES)
  511. : ImageConsumer.RANDOMPIXELORDER)
  512. : (ImageConsumer.TOPDOWNLEFTRIGHT |
  513. ImageConsumer.COMPLETESCANLINES |
  514. ImageConsumer.SINGLEPASS |
  515. ImageConsumer.SINGLEFRAME));
  516. }
  517. }
  518. private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
  519. int off = pixeloffset + pixelscan * y + x;
  520. if (isConsumer(ic)) {
  521. if (pixels instanceof byte[]) {
  522. ic.setPixels(x, y, w, h, model,
  523. ((byte[]) pixels), off, pixelscan);
  524. } else {
  525. ic.setPixels(x, y, w, h, model,
  526. ((int[]) pixels), off, pixelscan);
  527. }
  528. }
  529. }
  530. }