001/* AreaAveragingScaleFilter.java -- Java class for filtering images
002   Copyright (C) 1999,2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package 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 */
051public 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}