/*
* Copyright (c) 1996, 2002, Oracle and/or its affiliates. All rights reserved.
* ORACLE 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
*
* @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);
}
}
}