diff options
author | Tom Tromey <tromey@gcc.gnu.org> | 2007-01-09 19:58:05 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2007-01-09 19:58:05 +0000 |
commit | 97b8365cafc3a344a22d3980b8ed885f5c6d8357 (patch) | |
tree | 996a5f57d4a68c53473382e45cb22f574cb3e4db /libjava/classpath/java/awt/image/RescaleOp.java | |
parent | c648dedbde727ca3f883bb5fd773aa4af70d3369 (diff) | |
download | gcc-97b8365cafc3a344a22d3980b8ed885f5c6d8357.zip gcc-97b8365cafc3a344a22d3980b8ed885f5c6d8357.tar.gz gcc-97b8365cafc3a344a22d3980b8ed885f5c6d8357.tar.bz2 |
Merged gcj-eclipse branch to trunk.
From-SVN: r120621
Diffstat (limited to 'libjava/classpath/java/awt/image/RescaleOp.java')
-rw-r--r-- | libjava/classpath/java/awt/image/RescaleOp.java | 293 |
1 files changed, 230 insertions, 63 deletions
diff --git a/libjava/classpath/java/awt/image/RescaleOp.java b/libjava/classpath/java/awt/image/RescaleOp.java index d5b2969..d56b12cb9 100644 --- a/libjava/classpath/java/awt/image/RescaleOp.java +++ b/libjava/classpath/java/awt/image/RescaleOp.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 Free Software Foundation +/* Copyright (C) 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -43,7 +43,23 @@ import java.awt.geom.Rectangle2D; import java.util.Arrays; /** + * RescaleOp is a filter that changes each pixel by a scaling factor and offset. + * + * For filtering Rasters, either one scaling factor and offset can be specified, + * which will be applied to all bands; or a scaling factor and offset can be + * specified for each band. + * + * For BufferedImages, the scaling may apply to both color and alpha components. + * If only one scaling factor is provided, or if the number of factors provided + * equals the number of color components, the scaling is performed on all color + * components. Otherwise, the scaling is performed on all components including + * alpha. Alpha premultiplication is ignored. + * + * After filtering, if color conversion is necessary, the conversion happens, + * taking alpha premultiplication into account. + * * @author Jerry Quinn (jlquinn@optonline.net) + * @author Francis Kung (fkung@redhat.com) */ public class RescaleOp implements BufferedImageOp, RasterOp { @@ -51,15 +67,43 @@ public class RescaleOp implements BufferedImageOp, RasterOp private float[] offsets; private RenderingHints hints = null; + /** + * Create a new RescaleOp object using the given scale factors and offsets. + * + * The length of the arrays must be equal to the number of bands (or number of + * data or color components) of the raster/image that this Op will be used on, + * otherwise an IllegalArgumentException will be thrown when calling the + * filter method. + * + * @param scaleFactors an array of scale factors. + * @param offsets an array of offsets. + * @param hints any rendering hints to use (can be null). + * @throws NullPointerException if the scaleFactors or offsets array is null. + */ public RescaleOp(float[] scaleFactors, float[] offsets, RenderingHints hints) { - this.scale = scaleFactors; - this.offsets = offsets; + int length = Math.min(scaleFactors.length, offsets.length); + + scale = new float[length]; + System.arraycopy(scaleFactors, 0, this.scale, 0, length); + + this.offsets = new float[length]; + System.arraycopy(offsets, 0, this.offsets, 0, length); + this.hints = hints; } + /** + * Create a new RescaleOp object using the given scale factor and offset. + * + * The same scale factor and offset will be used on all bands/components. + * + * @param scaleFactor the scale factor to use. + * @param offset the offset to use. + * @param hints any rendering hints to use (can be null). + */ public RescaleOp(float scaleFactor, float offset, RenderingHints hints) @@ -69,22 +113,47 @@ public class RescaleOp implements BufferedImageOp, RasterOp this.hints = hints; } + /** + * Returns the scaling factors. This method accepts an optional array, which + * will be used to store the factors if not null (this avoids allocating a + * new array). If this array is too small to hold all the scaling factors, + * the array will be filled and the remaining factors discarded. + * + * @param scaleFactors array to store the scaling factors in (can be null). + * @return an array of scaling factors. + */ public final float[] getScaleFactors(float[] scaleFactors) { if (scaleFactors == null) scaleFactors = new float[scale.length]; - System.arraycopy(scale, 0, scaleFactors, 0, scale.length); + System.arraycopy(scale, 0, scaleFactors, 0, Math.min(scale.length, + scaleFactors.length)); return scaleFactors; } + /** + * Returns the offsets. This method accepts an optional array, which + * will be used to store the offsets if not null (this avoids allocating a + * new array). If this array is too small to hold all the offsets, the array + * will be filled and the remaining factors discarded. + * + * @param offsets array to store the offsets in (can be null). + * @return an array of offsets. + */ public final float[] getOffsets(float[] offsets) { if (offsets == null) offsets = new float[this.offsets.length]; - System.arraycopy(this.offsets, 0, offsets, 0, this.offsets.length); + System.arraycopy(this.offsets, 0, offsets, 0, Math.min(this.offsets.length, + offsets.length)); return offsets; } + /** + * Returns the number of scaling factors / offsets. + * + * @return the number of scaling factors / offsets. + */ public final int getNumFactors() { return scale.length; @@ -98,36 +167,74 @@ public class RescaleOp implements BufferedImageOp, RasterOp return hints; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage) + /** + * Converts the source image using the scale factors and offsets specified in + * the constructor. The resulting image is stored in the destination image if + * one is provided; otherwise a new BufferedImage is created and returned. + * + * The source image cannot use an IndexColorModel, and the destination image + * (if one is provided) must have the same size. + * + * If the final value of a sample is beyond the range of the color model, it + * will be clipped to the appropriate maximum / minimum. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The rescaled image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { - // TODO Make sure premultiplied alpha is handled correctly. - // TODO See that color conversion is handled. - // TODO figure out how to use rendering hints. - if (scale.length != offsets.length) - throw new IllegalArgumentException(); + // Initial checks + if (scale.length != 1 + && scale.length != src.getColorModel().getNumComponents() + && (scale.length != src.getColorModel().getNumColorComponents())) + throw new IllegalArgumentException("Source image has wrong number of " + + "bands for these scaling factors."); - ColorModel scm = src.getColorModel(); - if (dst == null) dst = createCompatibleDestImage(src, null); + if (dst == null) + dst = createCompatibleDestImage(src, null); + else if (src.getHeight() != dst.getHeight() + || src.getWidth() != dst.getWidth()) + throw new IllegalArgumentException("Source and destination images are " + + "different sizes."); - WritableRaster wsrc = src.getRaster(); - WritableRaster wdst = dst.getRaster(); - - // Share constant across colors except alpha - if (scale.length == 1 || scale.length == scm.getNumColorComponents()) + // Prepare for possible colorspace conversion + BufferedImage dst2 = dst; + if (dst.getColorModel().getColorSpace().getType() != src.getColorModel().getColorSpace().getType()) + dst2 = createCompatibleDestImage(src, src.getColorModel()); + + // Figure out how many bands to scale + int numBands = scale.length; + if (scale.length == 1) + numBands = src.getColorModel().getNumColorComponents(); + boolean[] bands = new boolean[numBands]; + // this assumes the alpha, if present, is the last band + Arrays.fill(bands, true); + + // Perform rescaling + filter(src.getRaster(), dst2.getRaster(), bands); + + // Copy alpha band if needed (ie if it exists and wasn't scaled) + // NOTE: This assumes the alpha component is the last band! + if (src.getColorModel().hasAlpha() + && numBands == src.getColorModel().getNumColorComponents()) { - // Construct a raster that doesn't include an alpha band. - int[] subbands = new int[scm.getNumColorComponents()]; - for (int i=0; i < subbands.length; i++) subbands[i] = i; - wsrc = - wsrc.createWritableChild(wsrc.minX, wsrc.minY, wsrc.width, wsrc.height, - wsrc.minX, wsrc.minY, subbands); + + dst2.getRaster().setSamples(0, 0, src.getWidth(), src.getHeight(), + numBands, + src.getRaster().getSamples(0, 0, + src.getWidth(), + src.getHeight(), + numBands, + (int[]) null)); } - // else all color bands - filter(wsrc, wdst); + // Perform colorspace conversion if needed + if (dst != dst2) + new ColorConvertOp(hints).filter(dst2, dst); + return dst; } @@ -136,50 +243,106 @@ public class RescaleOp implements BufferedImageOp, RasterOp */ public final WritableRaster filter(Raster src, WritableRaster dest) { - if (dest == null) dest = src.createCompatibleWritableRaster(); - // Required sanity checks - if (src.numBands != dest.numBands || scale.length != offsets.length) - throw new IllegalArgumentException(); if (scale.length != 1 && scale.length != src.numBands) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Number of rasters is incompatible " + + "with the number of scaling " + + "factors provided."); - // Create scaling arrays if needed - float[] lscale = scale; - float[] loff = offsets; - if (scale.length == 1) - { - lscale = new float[src.numBands]; - Arrays.fill(lscale, scale[0]); - loff = new float[src.numBands]; - Arrays.fill(loff, offsets[0]); - } + if (dest == null) + dest = src.createCompatibleWritableRaster(); + else if (src.getHeight() != dest.getHeight() + || src.getWidth() != dest.getWidth()) + throw new IllegalArgumentException("Source and destination rasters are " + + "different sizes."); + else if (src.numBands != dest.numBands) + throw new IllegalArgumentException("Source and destination rasters " + + "are incompatible."); + + // Filter all bands + boolean[] bands = new boolean[src.getNumBands()]; + Arrays.fill(bands, true); + return filter(src, dest, bands); + } + + /** + * Perform raster-based filtering on a selected number of bands. + * + * The length of the bands array should equal the number of bands; a true + * element indicates filtering should happen on the corresponding band, while + * a false element will skip the band. + * + * The rasters are assumed to be compatible and non-null. + * + * @param src the source raster. + * @param dest the destination raster. + * @param bands an array indicating which bands to filter. + * @throws NullPointerException if any parameter is null. + * @throws ArrayIndexOutOfBoundsException if the bands array is too small. + * @return the destination raster. + */ + private WritableRaster filter(Raster src, WritableRaster dest, boolean[] bands) + { + int[] values = new int[src.getHeight() * src.getWidth()]; + float scaleFactor, offset; + + // Find max sample value, to be used for clipping later + int[] maxValue = src.getSampleModel().getSampleSize(); + for (int i = 0; i < maxValue.length; i++) + maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1; + + // TODO: can this be optimized further? + // Filter all samples of all requested bands + for (int band = 0; band < bands.length; band++) + if (bands[band]) + { + values = src.getSamples(src.getMinX(), src.getMinY(), src.getWidth(), + src.getHeight(), band, values); - // TODO The efficiency here can be improved for various data storage - // patterns, aka SampleModels. - float[] pixel = new float[src.numBands]; - for (int y = src.minY; y < src.height + src.minY; y++) - for (int x = src.minX; x < src.width + src.minX; x++) - { - src.getPixel(x, y, pixel); - for (int b = 0; b < src.numBands; b++) - pixel[b] = pixel[b] * lscale[b] + loff[b]; - dest.setPixel(x, y, pixel); - } + if (scale.length == 1) + { + scaleFactor = scale[0]; + offset = offsets[0]; + } + else + { + scaleFactor = scale[band]; + offset = offsets[band]; + } + + for (int i = 0; i < values.length; i++) + { + values[i] = (int) (values[i] * scaleFactor + offset); + + // Clip if needed + if (values[i] < 0) + values[i] = 0; + if (values[i] > maxValue[band]) + values[i] = maxValue[band]; + } + + dest.setSamples(dest.getMinX(), dest.getMinY(), dest.getWidth(), + dest.getHeight(), band, values); + } + return dest; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + /* + * (non-Javadoc) + * + * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, + * java.awt.image.ColorModel) */ public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { - if (dstCM == null) dstCM = src.getColorModel(); - WritableRaster wr = src.getRaster().createCompatibleWritableRaster(); - BufferedImage image - = new BufferedImage(dstCM, wr, src.isPremultiplied, null); - return image; + if (dstCM == null) + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); + + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isAlphaPremultiplied(), null); } /* (non-Javadoc) @@ -209,9 +372,13 @@ public class RescaleOp implements BufferedImageOp, RasterOp /* (non-Javadoc) * @see java.awt.image.BufferedImageOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) */ - public final Point2D getPoint2D(Point2D src, Point2D dst) { - if (dst == null) dst = (Point2D) src.clone(); - else dst.setLocation(src); + public final Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) + dst = (Point2D) src.clone(); + else + dst.setLocation(src); + return dst; } |