1. /*
  2. * @(#)AreaAveragingScaleFilter.java 1.7 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.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. * 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. * It is meant to be used in conjunction with a FilteredImageSource
  29. * object to produce scaled versions of existing images.
  30. *
  31. * @see FilteredImageSource
  32. * @see ReplicateImageFilter
  33. * @see ImageFilter
  34. *
  35. * @version 1.7 11/29/01
  36. * @author Jim Graham
  37. */
  38. public class AreaAveragingScaleFilter extends ReplicateScaleFilter {
  39. private static final ColorModel rgbmodel = ColorModel.getRGBdefault();
  40. private static final int neededHints = (TOPDOWNLEFTRIGHT
  41. | COMPLETESCANLINES);
  42. private boolean passthrough;
  43. private float reds[], greens[], blues[], alphas[];
  44. private int savedy;
  45. private int savedyrem;
  46. /**
  47. * Constructs an AreaAveragingScaleFilter that scales the pixels from
  48. * its source Image as specified by the width and height parameters.
  49. * @param width the target width to scale the image
  50. * @param height the target height to scale the image
  51. */
  52. public AreaAveragingScaleFilter(int width, int height) {
  53. super(width, height);
  54. }
  55. /**
  56. * Detect if the data is being delivered with the necessary hints
  57. * to allow the averaging algorithm to do its work.
  58. * @see ImageConsumer#setHints
  59. */
  60. public void setHints(int hints) {
  61. passthrough = ((hints & neededHints) != neededHints);
  62. super.setHints(hints);
  63. }
  64. private void makeAccumBuffers() {
  65. reds = new float[destWidth];
  66. greens = new float[destWidth];
  67. blues = new float[destWidth];
  68. alphas = new float[destWidth];
  69. }
  70. private int[] calcRow() {
  71. float mult = ((float) srcWidth) * srcHeight;
  72. if (outpixbuf == null || !(outpixbuf instanceof int[])) {
  73. outpixbuf = new int[destWidth];
  74. }
  75. int[] outpix = (int[]) outpixbuf;
  76. for (int x = 0; x < destWidth; x++) {
  77. int a = Math.round(alphas[x] / mult);
  78. int r = Math.round(reds[x] / mult);
  79. int g = Math.round(greens[x] / mult);
  80. int b = Math.round(blues[x] / mult);
  81. if (a < 0) {a = 0;} else if (a > 255) {a = 255;}
  82. if (r < 0) {r = 0;} else if (r > 255) {r = 255;}
  83. if (g < 0) {g = 0;} else if (g > 255) {g = 255;}
  84. if (b < 0) {b = 0;} else if (b > 255) {b = 255;}
  85. outpix[x] = (a << 24 | r << 16 | g << 8 | b);
  86. }
  87. return outpix;
  88. }
  89. private void accumPixels(int x, int y, int w, int h,
  90. ColorModel model, Object pixels, int off,
  91. int scansize) {
  92. if (reds == null) {
  93. makeAccumBuffers();
  94. }
  95. int sy = y;
  96. int syrem = destHeight;
  97. int dy, dyrem;
  98. if (sy == 0) {
  99. dy = 0;
  100. dyrem = 0;
  101. } else {
  102. dy = savedy;
  103. dyrem = savedyrem;
  104. }
  105. while (sy < y + h) {
  106. int amty;
  107. if (dyrem == 0) {
  108. for (int i = 0; i < destWidth; i++) {
  109. alphas[i] = reds[i] = greens[i] = blues[i] = 0f;
  110. }
  111. dyrem = srcHeight;
  112. }
  113. if (syrem < dyrem) {
  114. amty = syrem;
  115. } else {
  116. amty = dyrem;
  117. }
  118. int sx = 0;
  119. int dx = 0;
  120. int sxrem = 0;
  121. int dxrem = srcWidth;
  122. float a = 0f, r = 0f, g = 0f, b = 0f;
  123. while (sx < w) {
  124. if (sxrem == 0) {
  125. sxrem = destWidth;
  126. int rgb;
  127. if (pixels instanceof byte[]) {
  128. rgb = ((byte[]) pixels)[off + sx] & 0xff;
  129. } else {
  130. rgb = ((int[]) pixels)[off + sx];
  131. }
  132. rgb = model.getRGB(rgb);
  133. a = rgb >>> 24;
  134. r = (rgb >> 16) & 0xff;
  135. g = (rgb >> 8) & 0xff;
  136. b = rgb & 0xff;
  137. }
  138. int amtx;
  139. if (sxrem < dxrem) {
  140. amtx = sxrem;
  141. } else {
  142. amtx = dxrem;
  143. }
  144. float mult = ((float) amtx) * amty;
  145. alphas[dx] += mult * a;
  146. reds[dx] += mult * r;
  147. greens[dx] += mult * g;
  148. blues[dx] += mult * b;
  149. if ((sxrem -= amtx) == 0) {
  150. sx++;
  151. }
  152. if ((dxrem -= amtx) == 0) {
  153. dx++;
  154. dxrem = srcWidth;
  155. }
  156. }
  157. if ((dyrem -= amty) == 0) {
  158. int outpix[] = calcRow();
  159. do {
  160. consumer.setPixels(0, dy, destWidth, 1,
  161. rgbmodel, outpix, 0, destWidth);
  162. dy++;
  163. } while ((syrem -= amty) >= amty && amty == srcHeight);
  164. } else {
  165. syrem -= amty;
  166. }
  167. if (syrem == 0) {
  168. syrem = destHeight;
  169. sy++;
  170. off += scansize;
  171. }
  172. }
  173. savedyrem = dyrem;
  174. savedy = dy;
  175. }
  176. /**
  177. * Combine the components for the delivered byte pixels into the
  178. * accumulation arrays and send on any averaged data for rows of
  179. * pixels that are complete. If the correct hints were not
  180. * specified in the setHints call then relay the work to our
  181. * superclass which is capable of scaling pixels regardless of
  182. * the delivery hints.
  183. * @see ReplicateScaleFilter
  184. */
  185. public void setPixels(int x, int y, int w, int h,
  186. ColorModel model, byte pixels[], int off,
  187. int scansize) {
  188. if (passthrough) {
  189. super.setPixels(x, y, w, h, model, pixels, off, scansize);
  190. } else {
  191. accumPixels(x, y, w, h, model, pixels, off, scansize);
  192. }
  193. }
  194. /**
  195. * Combine the components for the delivered int pixels into the
  196. * accumulation arrays and send on any averaged data for rows of
  197. * pixels that are complete. If the correct hints were not
  198. * specified in the setHints call then relay the work to our
  199. * superclass which is capable of scaling pixels regardless of
  200. * the delivery hints.
  201. * @see ReplicateScaleFilter
  202. */
  203. public void setPixels(int x, int y, int w, int h,
  204. ColorModel model, int pixels[], int off,
  205. int scansize) {
  206. if (passthrough) {
  207. super.setPixels(x, y, w, h, model, pixels, off, scansize);
  208. } else {
  209. accumPixels(x, y, w, h, model, pixels, off, scansize);
  210. }
  211. }
  212. }