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 }