- /*
- * @(#)AreaAveragingScaleFilter.java 1.15 03/12/19
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package java.awt.image;
-
- import java.awt.image.ImageConsumer;
- import java.awt.image.ColorModel;
- import java.util.Hashtable;
- import java.awt.Rectangle;
-
- /**
- * An ImageFilter class for scaling images using a simple area averaging
- * algorithm that produces smoother results than the nearest neighbor
- * algorithm.
- * <p>This class extends the basic ImageFilter Class to scale an existing
- * image and provide a source for a new image containing the resampled
- * image. The pixels in the source image are blended to produce pixels
- * for an image of the specified size. The blending process is analogous
- * to scaling up the source image to a multiple of the destination size
- * using pixel replication and then scaling it back down to the destination
- * size by simply averaging all the pixels in the supersized image that
- * fall within a given pixel of the destination image. If the data from
- * the source is not delivered in TopDownLeftRight order then the filter
- * will back off to a simple pixel replication behavior and utilize the
- * requestTopDownLeftRightResend() method to refilter the pixels in a
- * better way at the end.
- * <p>It is meant to be used in conjunction with a FilteredImageSource
- * object to produce scaled versions of existing images. Due to
- * implementation dependencies, there may be differences in pixel values
- * of an image filtered on different platforms.
- *
- * @see FilteredImageSource
- * @see ReplicateScaleFilter
- * @see ImageFilter
- *
- * @version 1.15 12/19/03
- * @author Jim Graham
- */
- public class AreaAveragingScaleFilter extends ReplicateScaleFilter {
- private static final ColorModel rgbmodel = ColorModel.getRGBdefault();
- private static final int neededHints = (TOPDOWNLEFTRIGHT
- | COMPLETESCANLINES);
-
- private boolean passthrough;
- private float reds[], greens[], blues[], alphas[];
- private int savedy;
- private int savedyrem;
-
- /**
- * Constructs an AreaAveragingScaleFilter that scales the pixels from
- * its source Image as specified by the width and height parameters.
- * @param width the target width to scale the image
- * @param height the target height to scale the image
- */
- public AreaAveragingScaleFilter(int width, int height) {
- super(width, height);
- }
-
- /**
- * Detect if the data is being delivered with the necessary hints
- * to allow the averaging algorithm to do its work.
- * <p>
- * Note: This method is intended to be called by the
- * <code>ImageProducer</code> of the <code>Image</code> whose
- * pixels are being filtered. Developers using
- * this class to filter pixels from an image should avoid calling
- * this method directly since that operation could interfere
- * with the filtering operation.
- * @see ImageConsumer#setHints
- */
- public void setHints(int hints) {
- passthrough = ((hints & neededHints) != neededHints);
- super.setHints(hints);
- }
-
- private void makeAccumBuffers() {
- reds = new float[destWidth];
- greens = new float[destWidth];
- blues = new float[destWidth];
- alphas = new float[destWidth];
- }
-
- private int[] calcRow() {
- float origmult = ((float) srcWidth) * srcHeight;
- if (outpixbuf == null || !(outpixbuf instanceof int[])) {
- outpixbuf = new int[destWidth];
- }
- int[] outpix = (int[]) outpixbuf;
- for (int x = 0; x < destWidth; x++) {
- float mult = origmult;
- int a = Math.round(alphas[x] / mult);
- if (a <= 0) {
- a = 0;
- } else if (a >= 255) {
- a = 255;
- } else {
- // un-premultiply the components (by modifying mult here, we
- // are effectively doing the divide by mult and divide by
- // alpha in the same step)
- mult = alphas[x] / 255;
- }
- int r = Math.round(reds[x] / mult);
- int g = Math.round(greens[x] / mult);
- int b = Math.round(blues[x] / mult);
- if (r < 0) {r = 0;} else if (r > 255) {r = 255;}
- if (g < 0) {g = 0;} else if (g > 255) {g = 255;}
- if (b < 0) {b = 0;} else if (b > 255) {b = 255;}
- outpix[x] = (a << 24 | r << 16 | g << 8 | b);
- }
- return outpix;
- }
-
- private void accumPixels(int x, int y, int w, int h,
- ColorModel model, Object pixels, int off,
- int scansize) {
- if (reds == null) {
- makeAccumBuffers();
- }
- int sy = y;
- int syrem = destHeight;
- int dy, dyrem;
- if (sy == 0) {
- dy = 0;
- dyrem = 0;
- } else {
- dy = savedy;
- dyrem = savedyrem;
- }
- while (sy < y + h) {
- int amty;
- if (dyrem == 0) {
- for (int i = 0; i < destWidth; i++) {
- alphas[i] = reds[i] = greens[i] = blues[i] = 0f;
- }
- dyrem = srcHeight;
- }
- if (syrem < dyrem) {
- amty = syrem;
- } else {
- amty = dyrem;
- }
- int sx = 0;
- int dx = 0;
- int sxrem = 0;
- int dxrem = srcWidth;
- float a = 0f, r = 0f, g = 0f, b = 0f;
- while (sx < w) {
- if (sxrem == 0) {
- sxrem = destWidth;
- int rgb;
- if (pixels instanceof byte[]) {
- rgb = ((byte[]) pixels)[off + sx] & 0xff;
- } else {
- rgb = ((int[]) pixels)[off + sx];
- }
- // getRGB() always returns non-premultiplied components
- rgb = model.getRGB(rgb);
- a = rgb >>> 24;
- r = (rgb >> 16) & 0xff;
- g = (rgb >> 8) & 0xff;
- b = rgb & 0xff;
- // premultiply the components if necessary
- if (a != 255.0f) {
- float ascale = a / 255.0f;
- r *= ascale;
- g *= ascale;
- b *= ascale;
- }
- }
- int amtx;
- if (sxrem < dxrem) {
- amtx = sxrem;
- } else {
- amtx = dxrem;
- }
- float mult = ((float) amtx) * amty;
- alphas[dx] += mult * a;
- reds[dx] += mult * r;
- greens[dx] += mult * g;
- blues[dx] += mult * b;
- if ((sxrem -= amtx) == 0) {
- sx++;
- }
- if ((dxrem -= amtx) == 0) {
- dx++;
- dxrem = srcWidth;
- }
- }
- if ((dyrem -= amty) == 0) {
- int outpix[] = calcRow();
- do {
- consumer.setPixels(0, dy, destWidth, 1,
- rgbmodel, outpix, 0, destWidth);
- dy++;
- } while ((syrem -= amty) >= amty && amty == srcHeight);
- } else {
- syrem -= amty;
- }
- if (syrem == 0) {
- syrem = destHeight;
- sy++;
- off += scansize;
- }
- }
- savedyrem = dyrem;
- savedy = dy;
- }
-
- /**
- * Combine the components for the delivered byte pixels into the
- * accumulation arrays and send on any averaged data for rows of
- * pixels that are complete. If the correct hints were not
- * specified in the setHints call then relay the work to our
- * superclass which is capable of scaling pixels regardless of
- * the delivery hints.
- * <p>
- * Note: This method is intended to be called by the
- * <code>ImageProducer</code> of the <code>Image</code>
- * whose pixels are being filtered. Developers using
- * this class to filter pixels from an image should avoid calling
- * this method directly since that operation could interfere
- * with the filtering operation.
- * @see ReplicateScaleFilter
- */
- public void setPixels(int x, int y, int w, int h,
- ColorModel model, byte pixels[], int off,
- int scansize) {
- if (passthrough) {
- super.setPixels(x, y, w, h, model, pixels, off, scansize);
- } else {
- accumPixels(x, y, w, h, model, pixels, off, scansize);
- }
- }
-
- /**
- * Combine the components for the delivered int pixels into the
- * accumulation arrays and send on any averaged data for rows of
- * pixels that are complete. If the correct hints were not
- * specified in the setHints call then relay the work to our
- * superclass which is capable of scaling pixels regardless of
- * the delivery hints.
- * <p>
- * Note: This method is intended to be called by the
- * <code>ImageProducer</code> of the <code>Image</code>
- * whose pixels are being filtered. Developers using
- * this class to filter pixels from an image should avoid calling
- * this method directly since that operation could interfere
- * with the filtering operation.
- * @see ReplicateScaleFilter
- */
- public void setPixels(int x, int y, int w, int h,
- ColorModel model, int pixels[], int off,
- int scansize) {
- if (passthrough) {
- super.setPixels(x, y, w, h, model, pixels, off, scansize);
- } else {
- accumPixels(x, y, w, h, model, pixels, off, scansize);
- }
- }
- }