1. /*
  2. * @(#)MemoryImageSource.java 1.28 03/01/23
  3. *
  4. * Copyright 2003 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.28 01/23/03
  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, Hashtable props) {
  132. initialize(w, h, cm, (Object) pix, off, scan, props);
  133. }
  134. /**
  135. * Constructs an ImageProducer object which uses an array of integers
  136. * to produce data for an Image object.
  137. * @param w the width of the rectangle of pixels
  138. * @param h the height of the rectangle of pixels
  139. * @param cm the specified <code>ColorModel</code>
  140. * @param pix an array of pixels
  141. * @param off the offset into the array of where to store the
  142. * first pixel
  143. * @param scan the distance from one row of pixels to the next in
  144. * the array
  145. * @see java.awt.Component#createImage
  146. */
  147. public MemoryImageSource(int w, int h, ColorModel cm,
  148. int[] pix, int off, int scan) {
  149. initialize(w, h, cm, (Object) pix, off, scan, null);
  150. }
  151. /**
  152. * Constructs an ImageProducer object which uses an array of integers
  153. * to produce data for an Image object.
  154. * @param w the width of the rectangle of pixels
  155. * @param h the height of the rectangle of pixels
  156. * @param cm the specified <code>ColorModel</code>
  157. * @param pix an array of pixels
  158. * @param off the offset into the array of where to store the
  159. * first pixel
  160. * @param scan the distance from one row of pixels to the next in
  161. * the array
  162. * @param props a list of properties that the <code>ImageProducer</code>
  163. * uses to process an image
  164. * @see java.awt.Component#createImage
  165. */
  166. public MemoryImageSource(int w, int h, ColorModel cm,
  167. int[] pix, int off, int scan, Hashtable props) {
  168. initialize(w, h, cm, (Object) pix, off, scan, props);
  169. }
  170. private void initialize(int w, int h, ColorModel cm,
  171. Object pix, int off, int scan, Hashtable props) {
  172. width = w;
  173. height = h;
  174. model = cm;
  175. pixels = pix;
  176. pixeloffset = off;
  177. pixelscan = scan;
  178. if (props == null) {
  179. props = new Hashtable();
  180. }
  181. properties = props;
  182. }
  183. /**
  184. * Constructs an ImageProducer object which uses an array of integers
  185. * in the default RGB ColorModel to produce data for an Image object.
  186. * @param w the width of the rectangle of pixels
  187. * @param h the height of the rectangle of pixels
  188. * @param pix an array of pixels
  189. * @param off the offset into the array of where to store the
  190. * first pixel
  191. * @param scan the distance from one row of pixels to the next in
  192. * the array
  193. * @see java.awt.Component#createImage
  194. * @see ColorModel#getRGBdefault
  195. */
  196. public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
  197. initialize(w, h, ColorModel.getRGBdefault(),
  198. (Object) pix, off, scan, null);
  199. }
  200. /**
  201. * Constructs an ImageProducer object which uses an array of integers
  202. * in the default RGB ColorModel to produce data for an Image object.
  203. * @param w the width of the rectangle of pixels
  204. * @param h the height of the rectangle of pixels
  205. * @param pix an array of pixels
  206. * @param off the offset into the array of where to store the
  207. * first pixel
  208. * @param scan the distance from one row of pixels to the next in
  209. * the array
  210. * @param props a list of properties that the <code>ImageProducer</code>
  211. * uses to process an image
  212. * @see java.awt.Component#createImage
  213. * @see ColorModel#getRGBdefault
  214. */
  215. public MemoryImageSource(int w, int h, int pix[], int off, int scan,
  216. Hashtable props) {
  217. initialize(w, h, ColorModel.getRGBdefault(),
  218. (Object) pix, off, scan, props);
  219. }
  220. /**
  221. * Adds an ImageConsumer to the list of consumers interested in
  222. * data for this image.
  223. * @param ic the specified <code>ImageConsumer</code>
  224. * @see ImageConsumer
  225. */
  226. public synchronized void addConsumer(ImageConsumer ic) {
  227. if (theConsumers.contains(ic)) {
  228. return;
  229. }
  230. theConsumers.addElement(ic);
  231. try {
  232. initConsumer(ic);
  233. sendPixels(ic, 0, 0, width, height);
  234. if (isConsumer(ic)) {
  235. ic.imageComplete(animating
  236. ? ImageConsumer.SINGLEFRAMEDONE
  237. : ImageConsumer.STATICIMAGEDONE);
  238. if (!animating && isConsumer(ic)) {
  239. ic.imageComplete(ImageConsumer.IMAGEERROR);
  240. removeConsumer(ic);
  241. }
  242. }
  243. } catch (Exception e) {
  244. if (isConsumer(ic)) {
  245. ic.imageComplete(ImageConsumer.IMAGEERROR);
  246. }
  247. }
  248. }
  249. /**
  250. * Determines if an ImageConsumer is on the list of consumers currently
  251. * interested in data for this image.
  252. * @param ic the specified <code>ImageConsumer</code>
  253. * @return <code>true</code> if the <code>ImageConsumer</code>
  254. * is on the list; <code>false</code> otherwise.
  255. * @see ImageConsumer
  256. */
  257. public synchronized boolean isConsumer(ImageConsumer ic) {
  258. return theConsumers.contains(ic);
  259. }
  260. /**
  261. * Removes an ImageConsumer from the list of consumers interested in
  262. * data for this image.
  263. * @param ic the specified <code>ImageConsumer</code>
  264. * @see ImageConsumer
  265. */
  266. public synchronized void removeConsumer(ImageConsumer ic) {
  267. theConsumers.removeElement(ic);
  268. }
  269. /**
  270. * Adds an ImageConsumer to the list of consumers interested in
  271. * data for this image and immediately starts delivery of the
  272. * image data through the ImageConsumer interface.
  273. * @param ic the specified <code>ImageConsumer</code>
  274. * image data through the ImageConsumer interface.
  275. * @see ImageConsumer
  276. */
  277. public void startProduction(ImageConsumer ic) {
  278. addConsumer(ic);
  279. }
  280. /**
  281. * Requests that a given ImageConsumer have the image data delivered
  282. * one more time in top-down, left-right order.
  283. * @param ic the specified <code>ImageConsumer</code>
  284. * @see ImageConsumer
  285. */
  286. public void requestTopDownLeftRightResend(ImageConsumer ic) {
  287. // Ignored. The data is either single frame and already in TDLR
  288. // format or it is multi-frame and TDLR resends aren't critical.
  289. }
  290. /**
  291. * Changes this memory image into a multi-frame animation or a
  292. * single-frame static image depending on the animated parameter.
  293. * <p>This method should be called immediately after the
  294. * MemoryImageSource is constructed and before an image is
  295. * created with it to ensure that all ImageConsumers will
  296. * receive the correct multi-frame data. If an ImageConsumer
  297. * is added to this ImageProducer before this flag is set then
  298. * that ImageConsumer will see only a snapshot of the pixel
  299. * data that was available when it connected.
  300. * @param animated <code>true</code> if the image is a
  301. * multi-frame animation
  302. */
  303. public synchronized void setAnimated(boolean animated) {
  304. this.animating = animated;
  305. if (!animating) {
  306. Enumeration enum = theConsumers.elements();
  307. while (enum.hasMoreElements()) {
  308. ImageConsumer ic = (ImageConsumer) enum.nextElement();
  309. ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
  310. if (isConsumer(ic)) {
  311. ic.imageComplete(ImageConsumer.IMAGEERROR);
  312. }
  313. }
  314. theConsumers.removeAllElements();
  315. }
  316. }
  317. /**
  318. * Specifies whether this animated memory image should always be
  319. * updated by sending the complete buffer of pixels whenever
  320. * there is a change.
  321. * This flag is ignored if the animation flag is not turned on
  322. * through the setAnimated() method.
  323. * <p>This method should be called immediately after the
  324. * MemoryImageSource is constructed and before an image is
  325. * created with it to ensure that all ImageConsumers will
  326. * receive the correct pixel delivery hints.
  327. * @param fullbuffers <code>true</code> if the complete pixel
  328. * buffer should always
  329. * be sent
  330. * @see #setAnimated
  331. */
  332. public synchronized void setFullBufferUpdates(boolean fullbuffers) {
  333. if (this.fullbuffers == fullbuffers) {
  334. return;
  335. }
  336. this.fullbuffers = fullbuffers;
  337. if (animating) {
  338. Enumeration enum = theConsumers.elements();
  339. while (enum.hasMoreElements()) {
  340. ImageConsumer ic = (ImageConsumer) enum.nextElement();
  341. ic.setHints(fullbuffers
  342. ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  343. ImageConsumer.COMPLETESCANLINES)
  344. : ImageConsumer.RANDOMPIXELORDER);
  345. }
  346. }
  347. }
  348. /**
  349. * Sends a whole new buffer of pixels to any ImageConsumers that
  350. * are currently interested in the data for this image and notify
  351. * them that an animation frame is complete.
  352. * This method only has effect if the animation flag has been
  353. * turned on through the setAnimated() method.
  354. * @see #newPixels(int, int, int, int, boolean)
  355. * @see ImageConsumer
  356. * @see #setAnimated
  357. */
  358. public void newPixels() {
  359. newPixels(0, 0, width, height, true);
  360. }
  361. /**
  362. * Sends a rectangular region of the buffer of pixels to any
  363. * ImageConsumers that are currently interested in the data for
  364. * this image and notify them that an animation frame is complete.
  365. * This method only has effect if the animation flag has been
  366. * turned on through the setAnimated() method.
  367. * If the full buffer update flag was turned on with the
  368. * setFullBufferUpdates() method then the rectangle parameters
  369. * will be ignored and the entire buffer will always be sent.
  370. * @param x the x coordinate of the upper left corner of the rectangle
  371. * of pixels to be sent
  372. * @param y the y coordinate of the upper left corner of the rectangle
  373. * of pixels to be sent
  374. * @param w the width of the rectangle of pixels to be sent
  375. * @param h the height of the rectangle of pixels to be sent
  376. * @see #newPixels(int, int, int, int, boolean)
  377. * @see ImageConsumer
  378. * @see #setAnimated
  379. * @see #setFullBufferUpdates
  380. */
  381. public synchronized void newPixels(int x, int y, int w, int h) {
  382. newPixels(x, y, w, h, true);
  383. }
  384. /**
  385. * Sends a rectangular region of the buffer of pixels to any
  386. * ImageConsumers that are currently interested in the data for
  387. * this image.
  388. * If the framenotify parameter is true then the consumers are
  389. * also notified that an animation frame is complete.
  390. * This method only has effect if the animation flag has been
  391. * turned on through the setAnimated() method.
  392. * If the full buffer update flag was turned on with the
  393. * setFullBufferUpdates() method then the rectangle parameters
  394. * will be ignored and the entire buffer will always be sent.
  395. * @param x the x coordinate of the upper left corner of the rectangle
  396. * of pixels to be sent
  397. * @param y the y coordinate of the upper left corner of the rectangle
  398. * of pixels to be sent
  399. * @param w the width of the rectangle of pixels to be sent
  400. * @param h the height of the rectangle of pixels to be sent
  401. * @param framenotify <code>true</code> if the consumers should be sent a
  402. * {@link ImageConsumer#SINGLEFRAMEDONE SINGLEFRAMEDONE} notification
  403. * @see ImageConsumer
  404. * @see #setAnimated
  405. * @see #setFullBufferUpdates
  406. */
  407. public synchronized void newPixels(int x, int y, int w, int h,
  408. boolean framenotify) {
  409. if (animating) {
  410. if (fullbuffers) {
  411. x = y = 0;
  412. w = width;
  413. h = height;
  414. } else {
  415. if (x < 0) {
  416. w += x;
  417. x = 0;
  418. }
  419. if (x + w > width) {
  420. w = width - x;
  421. }
  422. if (y < 0) {
  423. h += y;
  424. y = 0;
  425. }
  426. if (y + h > height) {
  427. h = height - y;
  428. }
  429. }
  430. if ((w <= 0 || h <= 0) && !framenotify) {
  431. return;
  432. }
  433. Enumeration enum = theConsumers.elements();
  434. while (enum.hasMoreElements()) {
  435. ImageConsumer ic = (ImageConsumer) enum.nextElement();
  436. if (w > 0 && h > 0) {
  437. sendPixels(ic, x, y, w, h);
  438. }
  439. if (framenotify && isConsumer(ic)) {
  440. ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
  441. }
  442. }
  443. }
  444. }
  445. /**
  446. * Changes to a new byte array to hold the pixels for this image.
  447. * If the animation flag has been turned on through the setAnimated()
  448. * method, then the new pixels will be immediately delivered to any
  449. * ImageConsumers that are currently interested in the data for
  450. * this image.
  451. * @param newpix the new pixel array
  452. * @param newmodel the specified <code>ColorModel</code>
  453. * @param offset the offset into the array
  454. * @param scansize the distance from one row of pixels to the next in
  455. * the array
  456. * @see #newPixels(int, int, int, int, boolean)
  457. * @see #setAnimated
  458. */
  459. public synchronized void newPixels(byte[] newpix, ColorModel newmodel,
  460. int offset, int scansize) {
  461. this.pixels = newpix;
  462. this.model = newmodel;
  463. this.pixeloffset = offset;
  464. this.pixelscan = scansize;
  465. newPixels();
  466. }
  467. /**
  468. * Changes to a new int array to hold the pixels for this image.
  469. * If the animation flag has been turned on through the setAnimated()
  470. * method, then the new pixels will be immediately delivered to any
  471. * ImageConsumers that are currently interested in the data for
  472. * this image.
  473. * @param newpix the new pixel array
  474. * @param newmodel the specified <code>ColorModel</code>
  475. * @param offset the offset into the array
  476. * @param scansize the distance from one row of pixels to the next in
  477. * the array
  478. * @see #newPixels(int, int, int, int, boolean)
  479. * @see #setAnimated
  480. */
  481. public synchronized void newPixels(int[] newpix, ColorModel newmodel,
  482. int offset, int scansize) {
  483. this.pixels = newpix;
  484. this.model = newmodel;
  485. this.pixeloffset = offset;
  486. this.pixelscan = scansize;
  487. newPixels();
  488. }
  489. private void initConsumer(ImageConsumer ic) {
  490. if (isConsumer(ic)) {
  491. ic.setDimensions(width, height);
  492. }
  493. if (isConsumer(ic)) {
  494. ic.setProperties(properties);
  495. }
  496. if (isConsumer(ic)) {
  497. ic.setColorModel(model);
  498. }
  499. if (isConsumer(ic)) {
  500. ic.setHints(animating
  501. ? (fullbuffers
  502. ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  503. ImageConsumer.COMPLETESCANLINES)
  504. : ImageConsumer.RANDOMPIXELORDER)
  505. : (ImageConsumer.TOPDOWNLEFTRIGHT |
  506. ImageConsumer.COMPLETESCANLINES |
  507. ImageConsumer.SINGLEPASS |
  508. ImageConsumer.SINGLEFRAME));
  509. }
  510. }
  511. private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
  512. int off = pixeloffset + pixelscan * y + x;
  513. if (isConsumer(ic)) {
  514. if (pixels instanceof byte[]) {
  515. ic.setPixels(x, y, w, h, model,
  516. ((byte[]) pixels), off, pixelscan);
  517. } else {
  518. ic.setPixels(x, y, w, h, model,
  519. ((int[]) pixels), off, pixelscan);
  520. }
  521. }
  522. }
  523. }