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} of the {@code Image} 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} of the {@code Image}
 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} of the {@code Image}
 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 }