1. /*
  2. * @(#)BufferedImageFilter.java 1.29 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.util.Hashtable;
  9. import java.awt.image.ImageConsumer;
  10. import java.awt.image.ImageFilter;
  11. /**
  12. * The <code>BufferedImageFilter</code> class subclasses an
  13. * <code>ImageFilter</code> to provide a simple means of
  14. * using a single-source/single-destination image operator
  15. * ({@link BufferedImageOp}) to filter a <code>BufferedImage</code>
  16. * in the Image Producer/Consumer/Observer
  17. * paradigm. Examples of these image operators are: {@link ConvolveOp},
  18. * {@link AffineTransformOp} and {@link LookupOp}.
  19. *
  20. * @see ImageFilter
  21. * @see BufferedImage
  22. * @see BufferedImageOp
  23. * @version 10 Feb 1997
  24. */
  25. public class BufferedImageFilter extends ImageFilter implements Cloneable {
  26. BufferedImageOp bufferedImageOp;
  27. ColorModel model;
  28. int width;
  29. int height;
  30. byte[] bytePixels;
  31. int[] intPixels;
  32. /**
  33. * Constructs a <code>BufferedImageFilter</code> with the
  34. * specified single-source/single-destination operator.
  35. * @param op the specified <code>BufferedImageOp</code> to
  36. * use to filter a <code>BufferedImage</code>
  37. * @throws NullPointerException if op is null
  38. */
  39. public BufferedImageFilter (BufferedImageOp op) {
  40. super();
  41. if (op == null) {
  42. throw new NullPointerException("Operation cannot be null");
  43. }
  44. bufferedImageOp = op;
  45. }
  46. /**
  47. * Returns the <code>BufferedImageOp</code>.
  48. * @return the operator of this <code>BufferedImageFilter</code>.
  49. */
  50. public BufferedImageOp getBufferedImageOp() {
  51. return bufferedImageOp;
  52. }
  53. /**
  54. * Filters the information provided in the
  55. * {@link ImageConsumer#setDimensions(int, int) setDimensions } method
  56. * of the {@link ImageConsumer} interface.
  57. * <p>
  58. * Note: This method is intended to be called by the
  59. * {@link ImageProducer} of the <code>Image</code> whose pixels are
  60. * being filtered. Developers using this class to retrieve pixels from
  61. * an image should avoid calling this method directly since that
  62. * operation could result in problems with retrieving the requested
  63. * pixels.
  64. * <p>
  65. * @param width the width to which to set the width of this
  66. * <code>BufferedImageFilter</code>
  67. * @param height the height to which to set the height of this
  68. * <code>BufferedImageFilter</code>
  69. * @see ImageConsumer#setDimensions
  70. */
  71. public void setDimensions(int width, int height) {
  72. if (width <= 0 || height <= 0) {
  73. imageComplete(STATICIMAGEDONE);
  74. return;
  75. }
  76. this.width = width;
  77. this.height = height;
  78. }
  79. /**
  80. * Filters the information provided in the
  81. * {@link ImageConsumer#setColorModel(ColorModel) setColorModel} method
  82. * of the <code>ImageConsumer</code> interface.
  83. * <p>
  84. * If <code>model</code> is <code>null</code>, this
  85. * method clears the current <code>ColorModel</code> of this
  86. * <code>BufferedImageFilter</code>.
  87. * <p>
  88. * Note: This method is intended to be called by the
  89. * <code>ImageProducer</code> of the <code>Image</code>
  90. * whose pixels are being filtered. Developers using this
  91. * class to retrieve pixels from an image
  92. * should avoid calling this method directly since that
  93. * operation could result in problems with retrieving the
  94. * requested pixels.
  95. * @param model the {@link ColorModel} to which to set the
  96. * <code>ColorModel</code> of this <code>BufferedImageFilter</code>
  97. * @see ImageConsumer#setColorModel
  98. */
  99. public void setColorModel(ColorModel model) {
  100. this.model = model;
  101. }
  102. private void convertToRGB() {
  103. int size = width * height;
  104. int newpixels[] = new int[size];
  105. if (bytePixels != null) {
  106. for (int i = 0; i < size; i++) {
  107. newpixels[i] = this.model.getRGB(bytePixels[i] & 0xff);
  108. }
  109. } else if (intPixels != null) {
  110. for (int i = 0; i < size; i++) {
  111. newpixels[i] = this.model.getRGB(intPixels[i]);
  112. }
  113. }
  114. bytePixels = null;
  115. intPixels = newpixels;
  116. this.model = ColorModel.getRGBdefault();
  117. }
  118. /**
  119. * Filters the information provided in the <code>setPixels</code>
  120. * method of the <code>ImageConsumer</code> interface which takes
  121. * an array of bytes.
  122. * <p>
  123. * Note: This method is intended to be called by the
  124. * <code>ImageProducer</code> of the <code>Image</code> whose pixels
  125. * are being filtered. Developers using
  126. * this class to retrieve pixels from an image should avoid calling
  127. * this method directly since that operation could result in problems
  128. * with retrieving the requested pixels.
  129. * @throws IllegalArgumentException if width or height are less than
  130. * zero.
  131. * @see ImageConsumer#setPixels(int, int, int, int, ColorModel, byte[],
  132. int, int)
  133. */
  134. public void setPixels(int x, int y, int w, int h,
  135. ColorModel model, byte pixels[], int off,
  136. int scansize) {
  137. // Fix 4184230
  138. if (w < 0 || h < 0) {
  139. throw new IllegalArgumentException("Width ("+w+
  140. ") and height ("+h+
  141. ") must be > 0");
  142. }
  143. // Nothing to do
  144. if (w == 0 || h == 0) {
  145. return;
  146. }
  147. if (y < 0) {
  148. int diff = -y;
  149. if (diff >= h) {
  150. return;
  151. }
  152. off += scansize * diff;
  153. y += diff;
  154. h -= diff;
  155. }
  156. if (y + h > height) {
  157. h = height - y;
  158. if (h <= 0) {
  159. return;
  160. }
  161. }
  162. if (x < 0) {
  163. int diff = -x;
  164. if (diff >= w) {
  165. return;
  166. }
  167. off += diff;
  168. x += diff;
  169. w -= diff;
  170. }
  171. if (x + w > width) {
  172. w = width - x;
  173. if (w <= 0) {
  174. return;
  175. }
  176. }
  177. int dstPtr = y*width + x;
  178. if (intPixels == null) {
  179. if (bytePixels == null) {
  180. bytePixels = new byte[width*height];
  181. this.model = model;
  182. } else if (this.model != model) {
  183. convertToRGB();
  184. }
  185. if (bytePixels != null) {
  186. for (int sh = h; sh > 0; sh--) {
  187. System.arraycopy(pixels, off, bytePixels, dstPtr, w);
  188. off += scansize;
  189. dstPtr += width;
  190. }
  191. }
  192. }
  193. if (intPixels != null) {
  194. int dstRem = width - w;
  195. int srcRem = scansize - w;
  196. for (int sh = h; sh > 0; sh--) {
  197. for (int sw = w; sw > 0; sw--) {
  198. intPixels[dstPtr++] = model.getRGB(pixels[off++]&0xff);
  199. }
  200. off += srcRem;
  201. dstPtr += dstRem;
  202. }
  203. }
  204. }
  205. /**
  206. * Filters the information provided in the <code>setPixels</code>
  207. * method of the <code>ImageConsumer</code> interface which takes
  208. * an array of integers.
  209. * <p>
  210. * Note: This method is intended to be called by the
  211. * <code>ImageProducer</code> of the <code>Image</code> whose
  212. * pixels are being filtered. Developers using this class to
  213. * retrieve pixels from an image should avoid calling this method
  214. * directly since that operation could result in problems
  215. * with retrieving the requested pixels.
  216. * @throws IllegalArgumentException if width or height are less than
  217. * zero.
  218. * @see ImageConsumer#setPixels(int, int, int, int, ColorModel, int[],
  219. int, int)
  220. */
  221. public void setPixels(int x, int y, int w, int h,
  222. ColorModel model, int pixels[], int off,
  223. int scansize) {
  224. // Fix 4184230
  225. if (w < 0 || h < 0) {
  226. throw new IllegalArgumentException("Width ("+w+
  227. ") and height ("+h+
  228. ") must be > 0");
  229. }
  230. // Nothing to do
  231. if (w == 0 || h == 0) {
  232. return;
  233. }
  234. if (y < 0) {
  235. int diff = -y;
  236. if (diff >= h) {
  237. return;
  238. }
  239. off += scansize * diff;
  240. y += diff;
  241. h -= diff;
  242. }
  243. if (y + h > height) {
  244. h = height - y;
  245. if (h <= 0) {
  246. return;
  247. }
  248. }
  249. if (x < 0) {
  250. int diff = -x;
  251. if (diff >= w) {
  252. return;
  253. }
  254. off += diff;
  255. x += diff;
  256. w -= diff;
  257. }
  258. if (x + w > width) {
  259. w = width - x;
  260. if (w <= 0) {
  261. return;
  262. }
  263. }
  264. if (intPixels == null) {
  265. if (bytePixels == null) {
  266. intPixels = new int[width * height];
  267. this.model = model;
  268. } else {
  269. convertToRGB();
  270. }
  271. }
  272. int dstPtr = y*width + x;
  273. if (this.model == model) {
  274. for (int sh = h; sh > 0; sh--) {
  275. System.arraycopy(pixels, off, intPixels, dstPtr, w);
  276. off += scansize;
  277. dstPtr += width;
  278. }
  279. } else {
  280. if (this.model != ColorModel.getRGBdefault()) {
  281. convertToRGB();
  282. }
  283. int dstRem = width - w;
  284. int srcRem = scansize - w;
  285. for (int sh = h; sh > 0; sh--) {
  286. for (int sw = w; sw > 0; sw--) {
  287. intPixels[dstPtr++] = model.getRGB(pixels[off++]);
  288. }
  289. off += srcRem;
  290. dstPtr += dstRem;
  291. }
  292. }
  293. }
  294. /**
  295. * Filters the information provided in the <code>imageComplete</code>
  296. * method of the <code>ImageConsumer</code> interface.
  297. * <p>
  298. * Note: This method is intended to be called by the
  299. * <code>ImageProducer</code> of the <code>Image</code> whose pixels
  300. * are being filtered. Developers using
  301. * this class to retrieve pixels from an image should avoid calling
  302. * this method directly since that operation could result in problems
  303. * with retrieving the requested pixels.
  304. * @param status the status of image loading
  305. * @throws ImagingOpException if there was a problem calling the filter
  306. * method of the <code>BufferedImageOp</code> associated with this
  307. * instance.
  308. * @see ImageConsumer#imageComplete
  309. */
  310. public void imageComplete(int status) {
  311. WritableRaster wr;
  312. switch(status) {
  313. case IMAGEERROR:
  314. case IMAGEABORTED:
  315. // reinitialize the params
  316. model = null;
  317. width = -1;
  318. height = -1;
  319. intPixels = null;
  320. bytePixels = null;
  321. break;
  322. case SINGLEFRAMEDONE:
  323. case STATICIMAGEDONE:
  324. if (width <= 0 || height <= 0) break;
  325. if (model instanceof DirectColorModel) {
  326. if (intPixels == null) break;
  327. wr = createDCMraster();
  328. }
  329. else if (model instanceof IndexColorModel) {
  330. int[] bandOffsets = {0};
  331. if (bytePixels == null) break;
  332. DataBufferByte db = new DataBufferByte(bytePixels,
  333. width*height);
  334. wr = Raster.createInterleavedRaster(db, width, height, width,
  335. 1, bandOffsets, null);
  336. }
  337. else {
  338. convertToRGB();
  339. if (intPixels == null) break;
  340. wr = createDCMraster();
  341. }
  342. BufferedImage bi = new BufferedImage(model, wr,
  343. model.isAlphaPremultiplied(),
  344. null);
  345. bi = bufferedImageOp.filter(bi, null);
  346. WritableRaster r = bi.getRaster();
  347. ColorModel cm = bi.getColorModel();
  348. int w = r.getWidth();
  349. int h = r.getHeight();
  350. consumer.setDimensions(w, h);
  351. consumer.setColorModel(cm);
  352. if (cm instanceof DirectColorModel) {
  353. DataBufferInt db = (DataBufferInt) r.getDataBuffer();
  354. consumer.setPixels(0, 0, w, h,
  355. cm, db.getData(), 0, w);
  356. }
  357. else if (cm instanceof IndexColorModel) {
  358. DataBufferByte db = (DataBufferByte) r.getDataBuffer();
  359. consumer.setPixels(0, 0, w, h,
  360. cm, db.getData(), 0, w);
  361. }
  362. else {
  363. throw new InternalError("Unknown color model "+cm);
  364. }
  365. break;
  366. }
  367. consumer.imageComplete(status);
  368. }
  369. private final WritableRaster createDCMraster() {
  370. WritableRaster wr;
  371. DirectColorModel dcm = (DirectColorModel) model;
  372. boolean hasAlpha = model.hasAlpha();
  373. int[] bandMasks = new int[3+(hasAlpha ? 1 : 0)];
  374. bandMasks[0] = dcm.getRedMask();
  375. bandMasks[1] = dcm.getGreenMask();
  376. bandMasks[2] = dcm.getBlueMask();
  377. if (hasAlpha) {
  378. bandMasks[3] = dcm.getAlphaMask();
  379. }
  380. DataBufferInt db = new DataBufferInt(intPixels, width*height);
  381. wr = Raster.createPackedRaster(db, width, height, width,
  382. bandMasks, null);
  383. return wr;
  384. }
  385. }