1. /*
  2. * @(#)AreaAveragingScaleFilter.java 1.15 03/12/19
  3. *
  4. * Copyright 2004 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.ColorModel;
  10. import java.util.Hashtable;
  11. import java.awt.Rectangle;
  12. /**
  13. * An ImageFilter class for scaling images using a simple area averaging
  14. * algorithm that produces smoother results than the nearest neighbor
  15. * algorithm.
  16. * <p>This class extends the basic ImageFilter Class to scale an existing
  17. * image and provide a source for a new image containing the resampled
  18. * image. The pixels in the source image are blended to produce pixels
  19. * for an image of the specified size. The blending process is analogous
  20. * to scaling up the source image to a multiple of the destination size
  21. * using pixel replication and then scaling it back down to the destination
  22. * size by simply averaging all the pixels in the supersized image that
  23. * fall within a given pixel of the destination image. If the data from
  24. * the source is not delivered in TopDownLeftRight order then the filter
  25. * will back off to a simple pixel replication behavior and utilize the
  26. * requestTopDownLeftRightResend() method to refilter the pixels in a
  27. * better way at the end.
  28. * <p>It is meant to be used in conjunction with a FilteredImageSource
  29. * object to produce scaled versions of existing images. Due to
  30. * implementation dependencies, there may be differences in pixel values
  31. * of an image filtered on different platforms.
  32. *
  33. * @see FilteredImageSource
  34. * @see ReplicateScaleFilter
  35. * @see ImageFilter
  36. *
  37. * @version 1.15 12/19/03
  38. * @author Jim Graham
  39. */
  40. public class AreaAveragingScaleFilter extends ReplicateScaleFilter {
  41. private static final ColorModel rgbmodel = ColorModel.getRGBdefault();
  42. private static final int neededHints = (TOPDOWNLEFTRIGHT
  43. | COMPLETESCANLINES);
  44. private boolean passthrough;
  45. private float reds[], greens[], blues[], alphas[];
  46. private int savedy;
  47. private int savedyrem;
  48. /**
  49. * Constructs an AreaAveragingScaleFilter that scales the pixels from
  50. * its source Image as specified by the width and height parameters.
  51. * @param width the target width to scale the image
  52. * @param height the target height to scale the image
  53. */
  54. public AreaAveragingScaleFilter(int width, int height) {
  55. super(width, height);
  56. }
  57. /**
  58. * Detect if the data is being delivered with the necessary hints
  59. * to allow the averaging algorithm to do its work.
  60. * <p>
  61. * Note: This method is intended to be called by the
  62. * <code>ImageProducer</code> of the <code>Image</code> whose
  63. * pixels are being filtered. Developers using
  64. * this class to filter pixels from an image should avoid calling
  65. * this method directly since that operation could interfere
  66. * with the filtering operation.
  67. * @see ImageConsumer#setHints
  68. */
  69. public void setHints(int hints) {
  70. passthrough = ((hints & neededHints) != neededHints);
  71. super.setHints(hints);
  72. }
  73. private void makeAccumBuffers() {
  74. reds = new float[destWidth];
  75. greens = new float[destWidth];
  76. blues = new float[destWidth];
  77. alphas = new float[destWidth];
  78. }
  79. private int[] calcRow() {
  80. float origmult = ((float) srcWidth) * srcHeight;
  81. if (outpixbuf == null || !(outpixbuf instanceof int[])) {
  82. outpixbuf = new int[destWidth];
  83. }
  84. int[] outpix = (int[]) outpixbuf;
  85. for (int x = 0; x < destWidth; x++) {
  86. float mult = origmult;
  87. int a = Math.round(alphas[x] / mult);
  88. if (a <= 0) {
  89. a = 0;
  90. } else if (a >= 255) {
  91. a = 255;
  92. } else {
  93. // un-premultiply the components (by modifying mult here, we
  94. // are effectively doing the divide by mult and divide by
  95. // alpha in the same step)
  96. mult = alphas[x] / 255;
  97. }
  98. int r = Math.round(reds[x] / mult);
  99. int g = Math.round(greens[x] / mult);
  100. int b = Math.round(blues[x] / mult);
  101. if (r < 0) {r = 0;} else if (r > 255) {r = 255;}
  102. if (g < 0) {g = 0;} else if (g > 255) {g = 255;}
  103. if (b < 0) {b = 0;} else if (b > 255) {b = 255;}
  104. outpix[x] = (a << 24 | r << 16 | g << 8 | b);
  105. }
  106. return outpix;
  107. }
  108. private void accumPixels(int x, int y, int w, int h,
  109. ColorModel model, Object pixels, int off,
  110. int scansize) {
  111. if (reds == null) {
  112. makeAccumBuffers();
  113. }
  114. int sy = y;
  115. int syrem = destHeight;
  116. int dy, dyrem;
  117. if (sy == 0) {
  118. dy = 0;
  119. dyrem = 0;
  120. } else {
  121. dy = savedy;
  122. dyrem = savedyrem;
  123. }
  124. while (sy < y + h) {
  125. int amty;
  126. if (dyrem == 0) {
  127. for (int i = 0; i < destWidth; i++) {
  128. alphas[i] = reds[i] = greens[i] = blues[i] = 0f;
  129. }
  130. dyrem = srcHeight;
  131. }
  132. if (syrem < dyrem) {
  133. amty = syrem;
  134. } else {
  135. amty = dyrem;
  136. }
  137. int sx = 0;
  138. int dx = 0;
  139. int sxrem = 0;
  140. int dxrem = srcWidth;
  141. float a = 0f, r = 0f, g = 0f, b = 0f;
  142. while (sx < w) {
  143. if (sxrem == 0) {
  144. sxrem = destWidth;
  145. int rgb;
  146. if (pixels instanceof byte[]) {
  147. rgb = ((byte[]) pixels)[off + sx] & 0xff;
  148. } else {
  149. rgb = ((int[]) pixels)[off + sx];
  150. }
  151. // getRGB() always returns non-premultiplied components
  152. rgb = model.getRGB(rgb);
  153. a = rgb >>> 24;
  154. r = (rgb >> 16) & 0xff;
  155. g = (rgb >> 8) & 0xff;
  156. b = rgb & 0xff;
  157. // premultiply the components if necessary
  158. if (a != 255.0f) {
  159. float ascale = a / 255.0f;
  160. r *= ascale;
  161. g *= ascale;
  162. b *= ascale;
  163. }
  164. }
  165. int amtx;
  166. if (sxrem < dxrem) {
  167. amtx = sxrem;
  168. } else {
  169. amtx = dxrem;
  170. }
  171. float mult = ((float) amtx) * amty;
  172. alphas[dx] += mult * a;
  173. reds[dx] += mult * r;
  174. greens[dx] += mult * g;
  175. blues[dx] += mult * b;
  176. if ((sxrem -= amtx) == 0) {
  177. sx++;
  178. }
  179. if ((dxrem -= amtx) == 0) {
  180. dx++;
  181. dxrem = srcWidth;
  182. }
  183. }
  184. if ((dyrem -= amty) == 0) {
  185. int outpix[] = calcRow();
  186. do {
  187. consumer.setPixels(0, dy, destWidth, 1,
  188. rgbmodel, outpix, 0, destWidth);
  189. dy++;
  190. } while ((syrem -= amty) >= amty && amty == srcHeight);
  191. } else {
  192. syrem -= amty;
  193. }
  194. if (syrem == 0) {
  195. syrem = destHeight;
  196. sy++;
  197. off += scansize;
  198. }
  199. }
  200. savedyrem = dyrem;
  201. savedy = dy;
  202. }
  203. /**
  204. * Combine the components for the delivered byte pixels into the
  205. * accumulation arrays and send on any averaged data for rows of
  206. * pixels that are complete. If the correct hints were not
  207. * specified in the setHints call then relay the work to our
  208. * superclass which is capable of scaling pixels regardless of
  209. * the delivery hints.
  210. * <p>
  211. * Note: This method is intended to be called by the
  212. * <code>ImageProducer</code> of the <code>Image</code>
  213. * whose pixels are being filtered. Developers using
  214. * this class to filter pixels from an image should avoid calling
  215. * this method directly since that operation could interfere
  216. * with the filtering operation.
  217. * @see ReplicateScaleFilter
  218. */
  219. public void setPixels(int x, int y, int w, int h,
  220. ColorModel model, byte pixels[], int off,
  221. int scansize) {
  222. if (passthrough) {
  223. super.setPixels(x, y, w, h, model, pixels, off, scansize);
  224. } else {
  225. accumPixels(x, y, w, h, model, pixels, off, scansize);
  226. }
  227. }
  228. /**
  229. * Combine the components for the delivered int pixels into the
  230. * accumulation arrays and send on any averaged data for rows of
  231. * pixels that are complete. If the correct hints were not
  232. * specified in the setHints call then relay the work to our
  233. * superclass which is capable of scaling pixels regardless of
  234. * the delivery hints.
  235. * <p>
  236. * Note: This method is intended to be called by the
  237. * <code>ImageProducer</code> of the <code>Image</code>
  238. * whose pixels are being filtered. Developers using
  239. * this class to filter pixels from an image should avoid calling
  240. * this method directly since that operation could interfere
  241. * with the filtering operation.
  242. * @see ReplicateScaleFilter
  243. */
  244. public void setPixels(int x, int y, int w, int h,
  245. ColorModel model, int pixels[], int off,
  246. int scansize) {
  247. if (passthrough) {
  248. super.setPixels(x, y, w, h, model, pixels, off, scansize);
  249. } else {
  250. accumPixels(x, y, w, h, model, pixels, off, scansize);
  251. }
  252. }
  253. }