001    /* AreaAveragingScaleFilter.java -- Java class for filtering images
002       Copyright (C) 1999,2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.awt.image;
040    
041    /**
042     * This filter should produce images which do not have image artifacts
043     * like broken lines which were originally unbroken.  The cost is of
044     * course speed.  Using bi-linear interpolation here against 4 pixel
045     * points should give the desired results although Sun does not
046     * specify what the exact algorithm should be.
047     * <br>
048     *
049     * @author C. Brian Jones (cbj@gnu.org)
050     */
051    public class AreaAveragingScaleFilter extends ReplicateScaleFilter
052    {
053      /**
054       * Construct an instance of <code>AreaAveragingScaleFilter</code> which
055       * should be used in conjunction with a <code>FilteredImageSource</code>
056       * object.
057       *
058       * @param width the width of the destination image
059       * @param height the height of the destination image
060       */
061      public AreaAveragingScaleFilter(int width, int height) {
062        super(width, height);
063      }
064    
065      /**
066       * The <code>ImageProducer</code> should call this method with a
067       * bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
068       * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
069       * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the
070       * <code>ImageConsumer</code> interface.
071       * <br>
072       * FIXME - more than likely Sun's implementation desires
073       * <code>TOPDOWNLEFTRIGHT</code> order and this method is overloaded here
074       * in order to assure that mask is part of the hints added to
075       * the consumer.
076       *
077       * @param flags a bit mask of hints
078       * @see ImageConsumer
079       */
080      public void setHints(int flags)
081      {
082        if (consumer != null)
083          consumer.setHints(flags);
084      }
085    
086      /**
087       * This function delivers a rectangle of pixels where any
088       * pixel(m,n) is stored in the array as a <code>byte</code> at
089       * index (n * scansize + m + offset).
090       *
091       * @param x the x coordinate of the rectangle
092       * @param y the y coordinate of the rectangle
093       * @param w the width of the rectangle
094       * @param h the height of the rectangle
095       * @param model the <code>ColorModel</code> used to translate the pixels
096       * @param pixels the array of pixel values
097       * @param offset the index of the first pixels in the <code>pixels</code> array
098       * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
099       */
100      public void setPixels(int x, int y, int w, int h,
101                            ColorModel model, byte[] pixels, int offset, int scansize)
102      {
103        double rx = ((double) srcWidth) / destWidth;
104        double ry = ((double) srcHeight) / destHeight;
105    
106        int destScansize = (int) Math.round(scansize / rx);
107    
108        byte[] destPixels = averagePixels(x, y, w, h,
109                                          model, pixels, offset, scansize,
110                                          rx, ry, destScansize);
111    
112        if (consumer != null)
113          consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry),
114                             (int) Math.ceil(w/rx), (int) Math.ceil(h/ry),
115                             model, destPixels, 0, destScansize);
116      }
117    
118      /**
119       * This function delivers a rectangle of pixels where any
120       * pixel(m,n) is stored in the array as an <code>int</code> at
121       * index (n * scansize + m + offset).
122       *
123       * @param x the x coordinate of the rectangle
124       * @param y the y coordinate of the rectangle
125       * @param w the width of the rectangle
126       * @param h the height of the rectangle
127       * @param model the <code>ColorModel</code> used to translate the pixels
128       * @param pixels the array of pixel values
129       * @param offset the index of the first pixels in the <code>pixels</code> array
130       * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
131       */
132      public void setPixels(int x, int y, int w, int h,
133                            ColorModel model, int[] pixels, int offset, int scansize)
134      {
135        double rx = ((double) srcWidth) / destWidth;
136        double ry = ((double) srcHeight) / destHeight;
137    
138        int destScansize = (int) Math.round(scansize / rx);
139    
140        int[] destPixels = averagePixels(x, y, w, h,
141                                         model, pixels, offset, scansize,
142                                         rx, ry, destScansize);
143    
144        if (consumer != null)
145          consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry),
146                             (int) Math.ceil(w/rx), (int) Math.ceil(h/ry),
147                             model, destPixels, 0, destScansize);
148      }
149    
150      /**
151       * This is a really terrible implementation,
152       * since it uses the nearest-neighbor method. This filter is rarely used though.
153       *
154       * @param srcx, srcy - Source rectangle upper-left corner
155       * @param srcw, srch - Source rectangle width and height
156       * @param model - Pixel color model
157       * @param srcPixels - Source pixel data.
158       * @param srcOffset - Starting offset into the source pixel data array.
159       * @param srcScansize - Source array scanline size.
160       * @param rx,ry - Scaling factor.
161       * @param destScansize - Destination array scanline size.
162       */
163      private byte[] averagePixels(int srcx, int srcy, int srcw, int srch,
164                                   ColorModel model, byte[] srcPixels,
165                                   int srcOffset, int srcScansize,
166                                   double rx, double ry, int destScansize)
167      {
168        int destW = (int) Math.ceil(srcw/rx);
169        int destH = (int) Math.ceil(srch/ry);
170        byte[] destPixels = new byte[ destW * destH ];
171        int sx, sy;
172    
173        int w = (int)Math.ceil(rx);
174        int h = (int)Math.ceil(ry);
175    
176        for(int x = 0; x < destW; x++)
177          for(int y = 0; y < destH; y++)
178            {
179              sx = (int) (x * rx);
180              sy = (int) (y * ry);
181    
182              int r,g,b,a;
183              r = g = b = a = 0;
184    
185              for(int i = 0; i < w; i++)
186                {
187                  for(int j = 0; j < h; j++)
188                    {
189                      int idx = srcx + sx + i + (srcy + sy + j)*srcScansize;
190                      r += model.getRed(srcPixels[ idx ]);
191                      g += model.getGreen(srcPixels[ idx ]);
192                      b += model.getBlue(srcPixels[ idx ]);
193                      a += model.getAlpha(srcPixels[ idx ]);
194                    }
195                }
196    
197              r = r / (w * h);
198              g = g / (w * h);
199              b = b / (w * h);
200              a = a / (w * h);
201    
202              // Does this really work?
203              destPixels[x + destScansize*y] = (byte)model.getDataElement
204                (new int[]{r, g, b, a}, 0);
205            }
206    
207        return destPixels;
208      }
209    
210      /**
211       * This is a really terrible implementation,
212       * since it uses the nearest-neighbor method. This filter is rarely used though.
213       *
214       * @param srcx, srcy - Source rectangle upper-left corner
215       * @param srcw, srch - Source rectangle width and height
216       * @param model - Pixel color model
217       * @param srcPixels - Source pixel data.
218       * @param srcOffset - Starting offset into the source pixel data array.
219       * @param srcScansize - Source array scanline size.
220       * @param rx,ry - Scaling factor.
221       * @param destScansize - Destination array scanline size.
222       */
223      private int[] averagePixels(int srcx, int srcy, int srcw, int srch,
224                                  ColorModel model, int[] srcPixels,
225                                  int srcOffset, int srcScansize,
226                                  double rx, double ry, int destScansize)
227      {
228        int destW = (int) Math.ceil(srcw/rx);
229        int destH = (int) Math.ceil(srch/ry);
230        int[] destPixels = new int[ destW * destH ];
231        int sx, sy;
232    
233        int w = (int)Math.ceil(rx);
234        int h = (int)Math.ceil(ry);
235    
236        for(int x = 0; x < destW; x++)
237          for(int y = 0; y < destH; y++)
238            {
239              sx = (int) (x * rx);
240              sy = (int) (y * ry);
241    
242              int r,g,b,a;
243              r = g = b = a = 0;
244    
245              for(int i = 0; i < w; i++)
246                {
247                  for(int j = 0; j < h; j++)
248                    {
249                      int idx = srcx + sx + i + (srcy + sy + j)*srcScansize;
250                      r += model.getRed(srcPixels[ idx ]);
251                      g += model.getGreen(srcPixels[ idx ]);
252                      b += model.getBlue(srcPixels[ idx ]);
253                      a += model.getAlpha(srcPixels[ idx ]);
254                    }
255                }
256    
257              r = r / (w * h);
258              g = g / (w * h);
259              b = b / (w * h);
260              a = a / (w * h);
261    
262              destPixels[x + destScansize*y] = model.getDataElement
263                (new int[]{r, g, b, a}, 0);
264            }
265    
266        return destPixels;
267      }
268    }