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