blob: b31dd8da0a02246c7360d19c15efcf5fd008575d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt.image;
27
28import java.awt.image.ImageConsumer;
29import java.awt.image.ColorModel;
30import java.util.Hashtable;
31import java.awt.Rectangle;
32
33/**
34 * An ImageFilter class for scaling images using a simple area averaging
35 * algorithm that produces smoother results than the nearest neighbor
36 * algorithm.
37 * <p>This class extends the basic ImageFilter Class to scale an existing
38 * image and provide a source for a new image containing the resampled
39 * image. The pixels in the source image are blended to produce pixels
40 * for an image of the specified size. The blending process is analogous
41 * to scaling up the source image to a multiple of the destination size
42 * using pixel replication and then scaling it back down to the destination
43 * size by simply averaging all the pixels in the supersized image that
44 * fall within a given pixel of the destination image. If the data from
45 * the source is not delivered in TopDownLeftRight order then the filter
46 * will back off to a simple pixel replication behavior and utilize the
47 * requestTopDownLeftRightResend() method to refilter the pixels in a
48 * better way at the end.
49 * <p>It is meant to be used in conjunction with a FilteredImageSource
50 * object to produce scaled versions of existing images. Due to
51 * implementation dependencies, there may be differences in pixel values
52 * of an image filtered on different platforms.
53 *
54 * @see FilteredImageSource
55 * @see ReplicateScaleFilter
56 * @see ImageFilter
57 *
58 * @author Jim Graham
59 */
60public class AreaAveragingScaleFilter extends ReplicateScaleFilter {
61 private static final ColorModel rgbmodel = ColorModel.getRGBdefault();
62 private static final int neededHints = (TOPDOWNLEFTRIGHT
63 | COMPLETESCANLINES);
64
65 private boolean passthrough;
66 private float reds[], greens[], blues[], alphas[];
67 private int savedy;
68 private int savedyrem;
69
70 /**
71 * Constructs an AreaAveragingScaleFilter that scales the pixels from
72 * its source Image as specified by the width and height parameters.
73 * @param width the target width to scale the image
74 * @param height the target height to scale the image
75 */
76 public AreaAveragingScaleFilter(int width, int height) {
77 super(width, height);
78 }
79
80 /**
81 * Detect if the data is being delivered with the necessary hints
82 * to allow the averaging algorithm to do its work.
83 * <p>
84 * Note: This method is intended to be called by the
85 * <code>ImageProducer</code> of the <code>Image</code> whose
86 * pixels are being filtered. Developers using
87 * this class to filter pixels from an image should avoid calling
88 * this method directly since that operation could interfere
89 * with the filtering operation.
90 * @see ImageConsumer#setHints
91 */
92 public void setHints(int hints) {
93 passthrough = ((hints & neededHints) != neededHints);
94 super.setHints(hints);
95 }
96
97 private void makeAccumBuffers() {
98 reds = new float[destWidth];
99 greens = new float[destWidth];
100 blues = new float[destWidth];
101 alphas = new float[destWidth];
102 }
103
104 private int[] calcRow() {
105 float origmult = ((float) srcWidth) * srcHeight;
106 if (outpixbuf == null || !(outpixbuf instanceof int[])) {
107 outpixbuf = new int[destWidth];
108 }
109 int[] outpix = (int[]) outpixbuf;
110 for (int x = 0; x < destWidth; x++) {
111 float mult = origmult;
112 int a = Math.round(alphas[x] / mult);
113 if (a <= 0) {
114 a = 0;
115 } else if (a >= 255) {
116 a = 255;
117 } else {
118 // un-premultiply the components (by modifying mult here, we
119 // are effectively doing the divide by mult and divide by
120 // alpha in the same step)
121 mult = alphas[x] / 255;
122 }
123 int r = Math.round(reds[x] / mult);
124 int g = Math.round(greens[x] / mult);
125 int b = Math.round(blues[x] / mult);
126 if (r < 0) {r = 0;} else if (r > 255) {r = 255;}
127 if (g < 0) {g = 0;} else if (g > 255) {g = 255;}
128 if (b < 0) {b = 0;} else if (b > 255) {b = 255;}
129 outpix[x] = (a << 24 | r << 16 | g << 8 | b);
130 }
131 return outpix;
132 }
133
134 private void accumPixels(int x, int y, int w, int h,
135 ColorModel model, Object pixels, int off,
136 int scansize) {
137 if (reds == null) {
138 makeAccumBuffers();
139 }
140 int sy = y;
141 int syrem = destHeight;
142 int dy, dyrem;
143 if (sy == 0) {
144 dy = 0;
145 dyrem = 0;
146 } else {
147 dy = savedy;
148 dyrem = savedyrem;
149 }
150 while (sy < y + h) {
151 int amty;
152 if (dyrem == 0) {
153 for (int i = 0; i < destWidth; i++) {
154 alphas[i] = reds[i] = greens[i] = blues[i] = 0f;
155 }
156 dyrem = srcHeight;
157 }
158 if (syrem < dyrem) {
159 amty = syrem;
160 } else {
161 amty = dyrem;
162 }
163 int sx = 0;
164 int dx = 0;
165 int sxrem = 0;
166 int dxrem = srcWidth;
167 float a = 0f, r = 0f, g = 0f, b = 0f;
168 while (sx < w) {
169 if (sxrem == 0) {
170 sxrem = destWidth;
171 int rgb;
172 if (pixels instanceof byte[]) {
173 rgb = ((byte[]) pixels)[off + sx] & 0xff;
174 } else {
175 rgb = ((int[]) pixels)[off + sx];
176 }
177 // getRGB() always returns non-premultiplied components
178 rgb = model.getRGB(rgb);
179 a = rgb >>> 24;
180 r = (rgb >> 16) & 0xff;
181 g = (rgb >> 8) & 0xff;
182 b = rgb & 0xff;
183 // premultiply the components if necessary
184 if (a != 255.0f) {
185 float ascale = a / 255.0f;
186 r *= ascale;
187 g *= ascale;
188 b *= ascale;
189 }
190 }
191 int amtx;
192 if (sxrem < dxrem) {
193 amtx = sxrem;
194 } else {
195 amtx = dxrem;
196 }
197 float mult = ((float) amtx) * amty;
198 alphas[dx] += mult * a;
199 reds[dx] += mult * r;
200 greens[dx] += mult * g;
201 blues[dx] += mult * b;
202 if ((sxrem -= amtx) == 0) {
203 sx++;
204 }
205 if ((dxrem -= amtx) == 0) {
206 dx++;
207 dxrem = srcWidth;
208 }
209 }
210 if ((dyrem -= amty) == 0) {
211 int outpix[] = calcRow();
212 do {
213 consumer.setPixels(0, dy, destWidth, 1,
214 rgbmodel, outpix, 0, destWidth);
215 dy++;
216 } while ((syrem -= amty) >= amty && amty == srcHeight);
217 } else {
218 syrem -= amty;
219 }
220 if (syrem == 0) {
221 syrem = destHeight;
222 sy++;
223 off += scansize;
224 }
225 }
226 savedyrem = dyrem;
227 savedy = dy;
228 }
229
230 /**
231 * Combine the components for the delivered byte pixels into the
232 * accumulation arrays and send on any averaged data for rows of
233 * pixels that are complete. If the correct hints were not
234 * specified in the setHints call then relay the work to our
235 * superclass which is capable of scaling pixels regardless of
236 * the delivery hints.
237 * <p>
238 * Note: This method is intended to be called by the
239 * <code>ImageProducer</code> of the <code>Image</code>
240 * whose pixels are being filtered. Developers using
241 * this class to filter pixels from an image should avoid calling
242 * this method directly since that operation could interfere
243 * with the filtering operation.
244 * @see ReplicateScaleFilter
245 */
246 public void setPixels(int x, int y, int w, int h,
247 ColorModel model, byte pixels[], int off,
248 int scansize) {
249 if (passthrough) {
250 super.setPixels(x, y, w, h, model, pixels, off, scansize);
251 } else {
252 accumPixels(x, y, w, h, model, pixels, off, scansize);
253 }
254 }
255
256 /**
257 * Combine the components for the delivered int pixels into the
258 * accumulation arrays and send on any averaged data for rows of
259 * pixels that are complete. If the correct hints were not
260 * specified in the setHints call then relay the work to our
261 * superclass which is capable of scaling pixels regardless of
262 * the delivery hints.
263 * <p>
264 * Note: This method is intended to be called by the
265 * <code>ImageProducer</code> of the <code>Image</code>
266 * whose pixels are being filtered. Developers using
267 * this class to filter pixels from an image should avoid calling
268 * this method directly since that operation could interfere
269 * with the filtering operation.
270 * @see ReplicateScaleFilter
271 */
272 public void setPixels(int x, int y, int w, int h,
273 ColorModel model, int pixels[], int off,
274 int scansize) {
275 if (passthrough) {
276 super.setPixels(x, y, w, h, model, pixels, off, scansize);
277 } else {
278 accumPixels(x, y, w, h, model, pixels, off, scansize);
279 }
280 }
281}