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