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