View Javadoc
1   /*
2    * Copyright (c) 1996, 2002, Oracle and/or its affiliates. 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.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.awt.image;
27  
28  import java.awt.image.ImageConsumer;
29  import java.awt.image.ColorModel;
30  import java.util.Hashtable;
31  import 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   */
60  public 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 }