aboutsummaryrefslogtreecommitdiff
path: root/libjava/classpath/java/awt/image
diff options
context:
space:
mode:
authorTom Tromey <tromey@gcc.gnu.org>2007-01-09 19:58:05 +0000
committerTom Tromey <tromey@gcc.gnu.org>2007-01-09 19:58:05 +0000
commit97b8365cafc3a344a22d3980b8ed885f5c6d8357 (patch)
tree996a5f57d4a68c53473382e45cb22f574cb3e4db /libjava/classpath/java/awt/image
parentc648dedbde727ca3f883bb5fd773aa4af70d3369 (diff)
downloadgcc-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')
-rw-r--r--libjava/classpath/java/awt/image/AffineTransformOp.java513
-rw-r--r--libjava/classpath/java/awt/image/BandCombineOp.java128
-rw-r--r--libjava/classpath/java/awt/image/BufferedImage.java361
-rw-r--r--libjava/classpath/java/awt/image/ColorConvertOp.java443
-rw-r--r--libjava/classpath/java/awt/image/ColorModel.java40
-rw-r--r--libjava/classpath/java/awt/image/ComponentColorModel.java31
-rw-r--r--libjava/classpath/java/awt/image/ConvolveOp.java131
-rw-r--r--libjava/classpath/java/awt/image/CropImageFilter.java9
-rw-r--r--libjava/classpath/java/awt/image/DirectColorModel.java16
-rw-r--r--libjava/classpath/java/awt/image/ImageConsumer.java2
-rw-r--r--libjava/classpath/java/awt/image/ImageFilter.java350
-rw-r--r--libjava/classpath/java/awt/image/IndexColorModel.java62
-rw-r--r--libjava/classpath/java/awt/image/LookupOp.java141
-rw-r--r--libjava/classpath/java/awt/image/MemoryImageSource.java76
-rw-r--r--libjava/classpath/java/awt/image/PixelGrabber.java4
-rw-r--r--libjava/classpath/java/awt/image/RGBImageFilter.java416
-rw-r--r--libjava/classpath/java/awt/image/Raster.java7
-rw-r--r--libjava/classpath/java/awt/image/RenderedImage.java2
-rw-r--r--libjava/classpath/java/awt/image/ReplicateScaleFilter.java153
-rw-r--r--libjava/classpath/java/awt/image/RescaleOp.java293
-rw-r--r--libjava/classpath/java/awt/image/SampleModel.java29
-rw-r--r--libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java121
-rw-r--r--libjava/classpath/java/awt/image/WritableRaster.java24
-rw-r--r--libjava/classpath/java/awt/image/renderable/ParameterBlock.java24
-rw-r--r--libjava/classpath/java/awt/image/renderable/RenderableImage.java2
-rw-r--r--libjava/classpath/java/awt/image/renderable/RenderableImageOp.java2
26 files changed, 2140 insertions, 1240 deletions
diff --git a/libjava/classpath/java/awt/image/AffineTransformOp.java b/libjava/classpath/java/awt/image/AffineTransformOp.java
index bb4b795..849c5b0 100644
--- a/libjava/classpath/java/awt/image/AffineTransformOp.java
+++ b/libjava/classpath/java/awt/image/AffineTransformOp.java
@@ -1,6 +1,6 @@
/* AffineTransformOp.java -- This class performs affine
transformation between two images or rasters in 2 dimensions.
- Copyright (C) 2004 Free Software Foundation
+ Copyright (C) 2004, 2006 Free Software Foundation
This file is part of GNU Classpath.
@@ -39,6 +39,7 @@ exception statement from your version. */
package java.awt.image;
import java.awt.Graphics2D;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
@@ -48,10 +49,14 @@ import java.awt.geom.Rectangle2D;
import java.util.Arrays;
/**
- * This class performs affine transformation between two images or
- * rasters in 2 dimensions.
+ * AffineTransformOp performs matrix-based transformations (translations,
+ * scales, flips, rotations, and shears).
+ *
+ * If interpolation is required, nearest neighbour, bilinear, and bicubic
+ * methods are available.
*
* @author Olga Rodimina (rodimina@redhat.com)
+ * @author Francis Kung (fkung@redhat.com)
*/
public class AffineTransformOp implements BufferedImageOp, RasterOp
{
@@ -74,6 +79,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
*
* @param xform AffineTransform that will applied to the source image
* @param interpolationType type of interpolation used
+ * @throws ImagingOpException if the transform matrix is noninvertible
*/
public AffineTransformOp (AffineTransform xform, int interpolationType)
{
@@ -102,6 +108,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
*
* @param xform AffineTransform that will applied to the source image
* @param hints rendering hints that will be used during transformation
+ * @throws ImagingOpException if the transform matrix is noninvertible
*/
public AffineTransformOp (AffineTransform xform, RenderingHints hints)
{
@@ -112,185 +119,165 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
}
/**
- * Creates empty BufferedImage with the size equal to that of the
- * transformed image and correct number of bands. The newly created
+ * Creates a new BufferedImage with the size equal to that of the
+ * transformed image and the correct number of bands. The newly created
* image is created with the specified ColorModel.
- * If the ColorModel is equal to null, then image is created
- * with the ColorModel of the source image.
+ * If a ColorModel is not specified, an appropriate ColorModel is used.
*
- * @param src source image
- * @param destCM color model for the destination image
- * @return new compatible destination image
+ * @param src the source image.
+ * @param destCM color model for the destination image (can be null).
+ * @return a new compatible destination image.
*/
public BufferedImage createCompatibleDestImage (BufferedImage src,
ColorModel destCM)
{
+ if (destCM != null)
+ return new BufferedImage(destCM,
+ createCompatibleDestRaster(src.getRaster()),
+ src.isAlphaPremultiplied(), null);
+
+ // This behaviour was determined by Mauve testcases, and is compatible
+ // with the reference implementation
+ if (src.getType() == BufferedImage.TYPE_INT_ARGB_PRE
+ || src.getType() == BufferedImage.TYPE_4BYTE_ABGR
+ || src.getType() == BufferedImage.TYPE_4BYTE_ABGR_PRE)
+ return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
- // if destCm is not specified, use color model of the source image
-
- if (destCM == null)
- destCM = src.getColorModel ();
-
- return new BufferedImage (destCM,
- createCompatibleDestRaster (src.getRaster ()),
- src.isAlphaPremultiplied (),
- null);
-
+ else
+ return new BufferedImage(src.getWidth(), src.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
}
/**
- * Creates empty WritableRaster with the size equal to the transformed
- * source raster and correct number of bands
+ * Creates a new WritableRaster with the size equal to the transformed
+ * source raster and correct number of bands .
*
- * @param src source raster
- * @throws RasterFormatException if resulting width or height of raster is 0
- * @return new compatible raster
+ * @param src the source raster.
+ * @throws RasterFormatException if resulting width or height of raster is 0.
+ * @return a new compatible raster.
*/
public WritableRaster createCompatibleDestRaster (Raster src)
{
- Rectangle rect = (Rectangle) getBounds2D (src);
+ Rectangle2D rect = getBounds2D(src);
- // throw RasterFormatException if resulting width or height of the
- // transformed raster is 0
-
- if (rect.getWidth () == 0 || rect.getHeight () == 0)
+ if (rect.getWidth() == 0 || rect.getHeight() == 0)
throw new RasterFormatException("width or height is 0");
- return src.createCompatibleWritableRaster ((int) rect.getWidth (),
- (int) rect.getHeight ());
+ return src.createCompatibleWritableRaster((int) rect.getWidth(),
+ (int) rect.getHeight());
}
/**
* Transforms source image using transform specified at the constructor.
- * The resulting transformed image is stored in the destination image.
+ * The resulting transformed image is stored in the destination image if one
+ * is provided; otherwise a new BufferedImage is created and returned.
*
* @param src source image
* @param dst destination image
- * @return transformed source image
+ * @throws IllegalArgumentException if the source and destination image are
+ * the same
+ * @return transformed source image.
*/
public final BufferedImage filter (BufferedImage src, BufferedImage dst)
{
-
if (dst == src)
- throw new IllegalArgumentException ("src image cannot be the same as the dst image");
-
- // If the destination image is null, then BufferedImage is
- // created with ColorModel of the source image
+ throw new IllegalArgumentException("src image cannot be the same as "
+ + "the dst image");
+ // If the destination image is null, then use a compatible BufferedImage
if (dst == null)
- dst = createCompatibleDestImage(src, src.getColorModel ());
-
- // FIXME: Must check if color models of src and dst images are the same.
- // If it is not, then source image should be converted to color model
- // of the destination image
+ dst = createCompatibleDestImage(src, null);
- Graphics2D gr = (Graphics2D) dst.createGraphics ();
- gr.setRenderingHints (hints);
- gr.drawImage (src, transform, null);
+ Graphics2D gr = (Graphics2D) dst.createGraphics();
+ gr.setRenderingHints(hints);
+ gr.drawImage(src, transform, null);
return dst;
-
}
/**
* Transforms source raster using transform specified at the constructor.
- * The resulting raster is stored in the destination raster.
+ * The resulting raster is stored in the destination raster if it is not
+ * null, otherwise a new raster is created and returned.
*
* @param src source raster
* @param dst destination raster
- * @return transformed raster
+ * @throws IllegalArgumentException if the source and destination are not
+ * compatible
+ * @return transformed raster.
*/
- public final WritableRaster filter (Raster src, WritableRaster dst)
+ public final WritableRaster filter(Raster src, WritableRaster dst)
{
+ // Initial checks
if (dst == src)
throw new IllegalArgumentException("src image cannot be the same as"
- + " the dst image");
+ + " the dst image");
if (dst == null)
dst = createCompatibleDestRaster(src);
if (src.getNumBands() != dst.getNumBands())
throw new IllegalArgumentException("src and dst must have same number"
- + " of bands");
+ + " of bands");
- double[] dpts = new double[dst.getWidth() * 2];
- double[] pts = new double[dst.getWidth() * 2];
+ // Optimization for rasters that can be represented in the RGB colormodel:
+ // wrap the rasters in images, and let Cairo do the transformation
+ if (ColorModel.getRGBdefault().isCompatibleSampleModel(src.getSampleModel())
+ && ColorModel.getRGBdefault().isCompatibleSampleModel(dst.getSampleModel()))
+ {
+ WritableRaster src2 = Raster.createWritableRaster(src.getSampleModel(),
+ src.getDataBuffer(),
+ new Point(src.getMinX(),
+ src.getMinY()));
+ BufferedImage iSrc = new BufferedImage(ColorModel.getRGBdefault(),
+ src2, false, null);
+ BufferedImage iDst = new BufferedImage(ColorModel.getRGBdefault(), dst,
+ false, null);
+
+ return filter(iSrc, iDst).getRaster();
+ }
+
+ // Otherwise, we need to do the transformation in java code...
+ // Create arrays to hold all the points
+ double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2];
+ double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2];
+
+ // Populate array with all points in the *destination* raster
+ int i = 0;
for (int x = 0; x < dst.getWidth(); x++)
- {
- dpts[2 * x] = x + dst.getMinX();
- dpts[2 * x + 1] = x;
- }
- Rectangle srcbounds = src.getBounds();
- if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
- {
- for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
- {
- try {
- transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
- } catch (NoninvertibleTransformException e) {
- // Can't happen since the constructor traps this
- e.printStackTrace();
- }
-
- for (int x = 0; x < dst.getWidth(); x++)
- {
- if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
- continue;
- dst.setDataElements(x + dst.getMinX(), y,
- src.getDataElements((int)pts[2 * x],
- (int)pts[2 * x + 1],
- null));
- }
- }
- }
- else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
- {
- double[] tmp = new double[4 * src.getNumBands()];
- for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++)
{
- try {
- transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2);
- } catch (NoninvertibleTransformException e) {
- // Can't happen since the constructor traps this
- e.printStackTrace();
- }
-
- for (int x = 0; x < dst.getWidth(); x++)
- {
- if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1]))
- continue;
- int xx = (int)pts[2 * x];
- int yy = (int)pts[2 * x + 1];
- double dx = (pts[2 * x] - xx);
- double dy = (pts[2 * x + 1] - yy);
-
- // TODO write this more intelligently
- if (xx == src.getMinX() + src.getWidth() - 1 ||
- yy == src.getMinY() + src.getHeight() - 1)
+ for (int y = 0; y < dst.getHeight(); y++)
{
- // bottom or right edge
- Arrays.fill(tmp, 0);
- src.getPixel(xx, yy, tmp);
+ dstPts[i++] = x;
+ dstPts[i++] = y;
}
- else
- {
- // Normal case
- src.getPixels(xx, yy, 2, 2, tmp);
- for (int b = 0; b < src.getNumBands(); b++)
- tmp[b] = dx * dy * tmp[b]
- + (1 - dx) * dy * tmp[b + src.getNumBands()]
- + dx * (1 - dy) * tmp[b + 2 * src.getNumBands()]
- + (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()];
- }
- dst.setPixel(x, y, tmp);
- }
}
- }
- else
- {
- // Bicubic
- throw new UnsupportedOperationException("not implemented yet");
- }
+ Rectangle srcbounds = src.getBounds();
+
+ // Use an inverse transform to map each point in the destination to
+ // a point in the source. Note that, while all points in the destination
+ // matrix are integers, this is not necessarily true for points in the
+ // source (hence why interpolation is required)
+ try
+ {
+ AffineTransform inverseTx = transform.createInverse();
+ inverseTx.transform(dstPts, 0, srcPts, 0, dstPts.length / 2);
+ }
+ catch (NoninvertibleTransformException e)
+ {
+ // Shouldn't happen since the constructor traps this
+ throw new ImagingOpException(e.getMessage());
+ }
+
+ // Different interpolation methods...
+ if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
+ filterNearest(src, dst, dstPts, srcPts);
+ else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
+ filterBilinear(src, dst, dstPts, srcPts);
+
+ else // bicubic
+ filterBicubic(src, dst, dstPts, srcPts);
+
return dst;
}
@@ -314,27 +301,22 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
*/
public final Rectangle2D getBounds2D (Raster src)
{
- // determine new size for the transformed raster.
- // Need to calculate transformed coordinates of the lower right
- // corner of the raster. The upper left corner is always (0,0)
-
- double x2 = (double) src.getWidth () + src.getMinX ();
- double y2 = (double) src.getHeight () + src.getMinY ();
- Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null);
-
- Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ());
- return rect.getBounds ();
+ return transform.createTransformedShape(src.getBounds()).getBounds2D();
}
/**
- * Returns interpolation type used during transformations
+ * Returns interpolation type used during transformations.
*
* @return interpolation type
*/
public final int getInterpolationType ()
{
- if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR))
+ if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
return TYPE_BILINEAR;
+
+ else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC))
+ return TYPE_BICUBIC;
+
else
return TYPE_NEAREST_NEIGHBOR;
}
@@ -355,7 +337,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
/**
* Returns rendering hints that are used during transformation.
*
- * @return rendering hints
+ * @return the rendering hints used in this Op.
*/
public final RenderingHints getRenderingHints ()
{
@@ -366,10 +348,261 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp
* Returns transform used in transformation between source and destination
* image.
*
- * @return transform
+ * @return the transform used in this Op.
*/
public final AffineTransform getTransform ()
{
return transform;
}
+
+ /**
+ * Perform nearest-neighbour filtering
+ *
+ * @param src the source raster
+ * @param dst the destination raster
+ * @param dpts array of points on the destination raster
+ * @param pts array of corresponding points on the source raster
+ */
+ private void filterNearest(Raster src, WritableRaster dst, double[] dpts,
+ double[] pts)
+ {
+ Rectangle srcbounds = src.getBounds();
+
+ // For all points on the destination raster, copy the value from the
+ // corrosponding (rounded) source point
+ for (int i = 0; i < dpts.length; i += 2)
+ {
+ int srcX = (int) Math.round(pts[i]) + src.getMinX();
+ int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
+
+ if (srcbounds.contains(srcX, srcY))
+ dst.setDataElements((int) dpts[i] + dst.getMinX(),
+ (int) dpts[i + 1] + dst.getMinY(),
+ src.getDataElements(srcX, srcY, null));
+ }
+ }
+
+ /**
+ * Perform bilinear filtering
+ *
+ * @param src the source raster
+ * @param dst the destination raster
+ * @param dpts array of points on the destination raster
+ * @param pts array of corresponding points on the source raster
+ */
+ private void filterBilinear(Raster src, WritableRaster dst, double[] dpts,
+ double[] pts)
+ {
+ Rectangle srcbounds = src.getBounds();
+
+ Object xyarr = null;
+ Object xp1arr = null;
+ Object yp1arr = null;
+ Object xyp1arr = null;
+
+ double xy;
+ double xp1;
+ double yp1;
+ double xyp1;
+
+ double[] result = new double[src.getNumBands()];
+
+ // For all points in the destination raster, use bilinear interpolation
+ // to find the value from the corrosponding source points
+ for (int i = 0; i < dpts.length; i += 2)
+ {
+ int srcX = (int) Math.round(pts[i]) + src.getMinX();
+ int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
+
+ if (srcbounds.contains(srcX, srcY))
+ {
+ // Corner case at the bottom or right edge; use nearest neighbour
+ if (pts[i] >= src.getWidth() - 1
+ || pts[i + 1] >= src.getHeight() - 1)
+ dst.setDataElements((int) dpts[i] + dst.getMinX(),
+ (int) dpts[i + 1] + dst.getMinY(),
+ src.getDataElements(srcX, srcY, null));
+
+ // Standard case, apply the bilinear formula
+ else
+ {
+ int x = (int) Math.floor(pts[i] + src.getMinX());
+ int y = (int) Math.floor(pts[i + 1] + src.getMinY());
+ double xdiff = pts[i] + src.getMinX() - x;
+ double ydiff = pts[i + 1] + src.getMinY() - y;
+
+ // Get surrounding pixels used in interpolation... optimized
+ // to use the smallest datatype possible.
+ if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
+ || src.getTransferType() == DataBuffer.TYPE_FLOAT)
+ {
+ xyarr = src.getPixel(x, y, (double[])xyarr);
+ xp1arr = src.getPixel(x+1, y, (double[])xp1arr);
+ yp1arr = src.getPixel(x, y+1, (double[])yp1arr);
+ xyp1arr = src.getPixel(x+1, y+1, (double[])xyp1arr);
+ }
+ else
+ {
+ xyarr = src.getPixel(x, y, (int[])xyarr);
+ xp1arr = src.getPixel(x+1, y, (int[])xp1arr);
+ yp1arr = src.getPixel(x, y+1, (int[])yp1arr);
+ xyp1arr = src.getPixel(x+1, y+1, (int[])xyp1arr);
+ }
+ // using
+ // array[] pixels = src.getPixels(x, y, 2, 2, pixels);
+ // instead of doing four individual src.getPixel() calls
+ // should be faster, but benchmarking shows that it's not...
+
+ // Run interpolation for each band
+ for (int j = 0; j < src.getNumBands(); j++)
+ {
+ // Pull individual sample values out of array
+ if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
+ || src.getTransferType() == DataBuffer.TYPE_FLOAT)
+ {
+ xy = ((double[])xyarr)[j];
+ xp1 = ((double[])xp1arr)[j];
+ yp1 = ((double[])yp1arr)[j];
+ xyp1 = ((double[])xyp1arr)[j];
+ }
+ else
+ {
+ xy = ((int[])xyarr)[j];
+ xp1 = ((int[])xp1arr)[j];
+ yp1 = ((int[])yp1arr)[j];
+ xyp1 = ((int[])xyp1arr)[j];
+ }
+
+ // If all four samples are identical, there's no need to
+ // calculate anything
+ if (xy == xp1 && xy == yp1 && xy == xyp1)
+ result[j] = xy;
+
+ // Run bilinear interpolation formula
+ else
+ result[j] = (xy * (1-xdiff) + xp1 * xdiff)
+ * (1-ydiff)
+ + (yp1 * (1-xdiff) + xyp1 * xdiff)
+ * ydiff;
+ }
+
+ dst.setPixel((int)dpts[i] + dst.getMinX(),
+ (int)dpts[i+1] + dst.getMinY(),
+ result);
+ }
+ }
+ }
+ }
+
+ /**
+ * Perform bicubic filtering
+ * based on http://local.wasp.uwa.edu.au/~pbourke/colour/bicubic/
+ *
+ * @param src the source raster
+ * @param dst the destination raster
+ * @param dpts array of points on the destination raster
+ * @param pts array of corresponding points on the source raster
+ */
+ private void filterBicubic(Raster src, WritableRaster dst, double[] dpts,
+ double[] pts)
+ {
+ Rectangle srcbounds = src.getBounds();
+ double[] result = new double[src.getNumBands()];
+ Object pixels = null;
+
+ // For all points on the destination raster, perform bicubic interpolation
+ // from corrosponding source points
+ for (int i = 0; i < dpts.length; i += 2)
+ {
+ if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(),
+ (int) Math.round(pts[i + 1]) + src.getMinY()))
+ {
+ int x = (int) Math.floor(pts[i] + src.getMinX());
+ int y = (int) Math.floor(pts[i + 1] + src.getMinY());
+ double dx = pts[i] + src.getMinX() - x;
+ double dy = pts[i + 1] + src.getMinY() - y;
+ Arrays.fill(result, 0);
+
+ for (int m = - 1; m < 3; m++)
+ for (int n = - 1; n < 3; n++)
+ {
+ // R(x) = ( P(x+2)^3 - 4 P(x+1)^3 + 6 P(x)^3 - 4 P(x-1)^3 ) / 6
+ double r1 = 0;
+ double r2 = 0;
+
+ // Calculate R(m - dx)
+ double rx = m - dx + 2;
+ r1 += rx * rx * rx;
+
+ rx = m - dx + 1;
+ if (rx > 0)
+ r1 -= 4 * rx * rx * rx;
+
+ rx = m - dx;
+ if (rx > 0)
+ r1 += 6 * rx * rx * rx;
+
+ rx = m - dx - 1;
+ if (rx > 0)
+ r1 -= 4 * rx * rx * rx;
+
+ r1 /= 6;
+
+ // Calculate R(dy - n);
+ rx = dy - n + 2;
+ if (rx > 0)
+ r2 += rx * rx * rx;
+
+ rx = dy - n + 1;
+ if (rx > 0)
+ r2 -= 4 * rx * rx * rx;
+
+ rx = dy - n;
+ if (rx > 0)
+ r2 += 6 * rx * rx * rx;
+
+ rx = dy - n - 1;
+ if (rx > 0)
+ r2 -= 4 * rx * rx * rx;
+
+ r2 /= 6;
+
+ // Calculate F(i+m, j+n) R(m - dx) R(dy - n)
+ // Check corner cases
+ int srcX = x + m;
+ if (srcX >= src.getMinX() + src.getWidth())
+ srcX = src.getMinX() + src.getWidth() - 1;
+ else if (srcX < src.getMinX())
+ srcX = src.getMinX();
+
+ int srcY = y + n;
+ if (srcY >= src.getMinY() + src.getHeight())
+ srcY = src.getMinY() + src.getHeight() - 1;
+ else if (srcY < src.getMinY())
+ srcY = src.getMinY();
+
+ // Calculate once for each band, using the smallest
+ // datatype possible
+ if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
+ || src.getTransferType() == DataBuffer.TYPE_FLOAT)
+ {
+ pixels = src.getPixel(srcX, srcY, (double[])pixels);
+ for (int j = 0; j < result.length; j++)
+ result[j] += ((double[])pixels)[j] * r1 * r2;
+ }
+ else
+ {
+ pixels = src.getPixel(srcX, srcY, (int[])pixels);
+ for (int j = 0; j < result.length; j++)
+ result[j] += ((int[])pixels)[j] * r1 * r2;
+ }
+ }
+
+ // Put it all together
+ dst.setPixel((int)dpts[i] + dst.getMinX(),
+ (int)dpts[i+1] + dst.getMinY(),
+ result);
+ }
+ }
+ }
}
diff --git a/libjava/classpath/java/awt/image/BandCombineOp.java b/libjava/classpath/java/awt/image/BandCombineOp.java
index 634125e..d9ce16f 100644
--- a/libjava/classpath/java/awt/image/BandCombineOp.java
+++ b/libjava/classpath/java/awt/image/BandCombineOp.java
@@ -1,4 +1,5 @@
-/* Copyright (C) 2004 Free Software Foundation
+/* BandCombineOp.java - perform a combination on the bands of a raster
+ Copyright (C) 2004, 2006 Free Software Foundation
This file is part of GNU Classpath.
@@ -36,10 +37,10 @@ exception statement from your version. */
package java.awt.image;
-import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.util.Arrays;
/**
* Filter Raster pixels by applying a matrix.
@@ -53,6 +54,9 @@ import java.awt.geom.Rectangle2D;
* for the destination. Therefore the destination Raster must contain the
* same number of bands as the number of rows in the filter matrix.
*
+ * This Op assumes that samples are integers; floating point sample types will
+ * be rounded to their nearest integer value during filtering.
+ *
* @author Jerry Quinn (jlquinn@optonline.net)
*/
public class BandCombineOp implements RasterOp
@@ -65,52 +69,74 @@ public class BandCombineOp implements RasterOp
*
* @param matrix The matrix to filter pixels with.
* @param hints Rendering hints to apply. Ignored.
+ * @throws ArrayIndexOutOfBoundsException if the matrix is invalid
*/
public BandCombineOp(float[][] matrix, RenderingHints hints)
{
- this.matrix = matrix;
+ this.matrix = new float[matrix.length][];
+ int width = matrix[0].length;
+ for (int i = 0; i < matrix.length; i++)
+ {
+ this.matrix[i] = new float[width + 1];
+ for (int j = 0; j < width; j++)
+ this.matrix[i][j] = matrix[i][j];
+
+ // The reference implementation pads the array with a trailing zero...
+ this.matrix[i][width] = 0;
+ }
+
this.hints = hints;
}
/**
- * Filter Raster pixels through a matrix.
- *
- * Applies the Op matrix to source pixes to produce dest pixels. Each row
- * of the matrix is multiplied by the src pixel components to produce the
- * dest pixel. If matrix is one more than the number of bands in the src,
- * the last element is implicitly multiplied by 1, i.e. added to the sum
- * for that dest component.
- *
- * If dest is null, a suitable Raster is created. This implementation uses
- * createCompatibleDestRaster.
+ * Filter Raster pixels through a matrix. Applies the Op matrix to source
+ * pixes to produce dest pixels. Each row of the matrix is multiplied by the
+ * src pixel components to produce the dest pixel. If matrix is one more than
+ * the number of bands in the src, the last element is implicitly multiplied
+ * by 1, i.e. added to the sum for that dest component. If dest is null, a
+ * suitable Raster is created. This implementation uses
+ * createCompatibleDestRaster.
*
* @param src The source Raster.
- * @param dest The destination Raster, or null.
- * @returns The destination Raster or an allocated Raster.
+ * @param dest The destination Raster, or null.
+ * @throws IllegalArgumentException if the destination raster is incompatible
+ * with the source raster.
+ * @return The filtered Raster.
* @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
- *java.awt.image.WritableRaster)
+ * java.awt.image.WritableRaster)
*/
public WritableRaster filter(Raster src, WritableRaster dest) {
if (dest == null)
dest = createCompatibleDestRaster(src);
-
+ else if (dest.getNumBands() != src.getNumBands()
+ || dest.getTransferType() != src.getTransferType())
+ throw new IllegalArgumentException("Destination raster is incompatible with source raster");
+
// Filter the pixels
- float[] spix = new float[matrix[0].length];
- float[] dpix = new float[matrix.length];
+ int[] spix = new int[matrix[0].length - 1];
+ int[] spix2 = new int[matrix[0].length - 1];
+ int[] dpix = new int[matrix.length];
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
- {
- // In case matrix rows have implicit translation
- spix[spix.length - 1] = 1.0f;
- src.getPixel(x, y, spix);
- for (int i = 0; i < matrix.length; i++)
{
- dpix[i] = 0;
- for (int j = 0; j < matrix[0].length; j++)
- dpix[i] += spix[j] * matrix[i][j];
+ // In case matrix rows have implicit translation
+ spix[spix.length - 1] = 1;
+ src.getPixel(x, y, spix);
+
+ // Do not re-calculate if pixel is identical to the last one
+ // (ie, blocks of the same colour)
+ if (!Arrays.equals(spix, spix2))
+ {
+ System.arraycopy(spix, 0, spix2, 0, spix.length);
+ for (int i = 0; i < matrix.length; i++)
+ {
+ dpix[i] = 0;
+ for (int j = 0; j < matrix[0].length - 1; j++)
+ dpix[i] += spix[j] * (int)matrix[i][j];
+ }
+ }
+ dest.setPixel(x, y, dpix);
}
- dest.setPixel(x, y, dpix);
- }
return dest;
}
@@ -125,28 +151,48 @@ public class BandCombineOp implements RasterOp
/**
* Creates a new WritableRaster that can be used as the destination for this
- * Op. This implementation creates a Banded Raster with data type FLOAT.
- * @see
- *java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
+ * Op. The number of bands in the source raster must equal the number of rows
+ * in the op matrix, which must also be equal to either the number of columns
+ * or (columns - 1) in the matrix.
+ *
+ * @param src The source raster.
+ * @return A compatible raster.
+ * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
+ * @throws IllegalArgumentException if the raster is incompatible with the
+ * matrix.
*/
public WritableRaster createCompatibleDestRaster(Raster src)
{
- return Raster.createBandedRaster(DataBuffer.TYPE_FLOAT, src.getWidth(),
- src.getHeight(), matrix.length,
- new Point(src.getMinX(), src.getMinY()));
+ // Destination raster must have same number of bands as source
+ if (src.getNumBands() != matrix.length)
+ throw new IllegalArgumentException("Number of rows in matrix specifies an "
+ + "incompatible number of bands");
+
+ // We use -1 and -2 because we previously padded the rows with a trailing 0
+ if (src.getNumBands() != matrix[0].length - 1
+ && src.getNumBands() != matrix[0].length - 2)
+ throw new IllegalArgumentException("Incompatible number of bands: "
+ + "the number of bands in the raster must equal the number of "
+ + "columns in the matrix, optionally minus one");
+
+ return src.createCompatibleWritableRaster();
}
- /** Return corresponding destination point for source point.
+ /**
+ * Return corresponding destination point for source point. Because this is
+ * not a geometric operation, it simply returns a copy of the source.
*
- * LookupOp will return the value of src unchanged.
* @param src The source point.
* @param dst The destination point.
+ * @return dst The destination point.
* @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D,
*java.awt.geom.Point2D)
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
- if (dst == null) return (Point2D)src.clone();
+ if (dst == null)
+ return (Point2D)src.clone();
+
dst.setLocation(src);
return dst;
}
@@ -159,7 +205,11 @@ public class BandCombineOp implements RasterOp
return hints;
}
- /** Return the matrix for this Op. */
+ /**
+ * Return the matrix used in this operation.
+ *
+ * @return The matrix used in this operation.
+ */
public final float[][] getMatrix()
{
return matrix;
diff --git a/libjava/classpath/java/awt/image/BufferedImage.java b/libjava/classpath/java/awt/image/BufferedImage.java
index 76848db..ef3141d 100644
--- a/libjava/classpath/java/awt/image/BufferedImage.java
+++ b/libjava/classpath/java/awt/image/BufferedImage.java
@@ -38,6 +38,7 @@ exception statement from your version. */
package java.awt.image;
+import gnu.java.awt.Buffers;
import gnu.java.awt.ComponentDataBlitOp;
import java.awt.Graphics;
@@ -79,27 +80,37 @@ public class BufferedImage extends Image
TYPE_BYTE_BINARY = 12,
TYPE_BYTE_INDEXED = 13;
- static final int[] bits3 = { 8, 8, 8 };
- static final int[] bits4 = { 8, 8, 8, 8 };
- static final int[] bits1byte = { 8 };
- static final int[] bits1ushort = { 16 };
-
- static final int[] masks_int = { 0x00ff0000,
- 0x0000ff00,
- 0x000000ff,
- DataBuffer.TYPE_INT };
- static final int[] masks_565 = { 0xf800,
- 0x07e0,
- 0x001f,
- DataBuffer.TYPE_USHORT};
- static final int[] masks_555 = { 0x7c00,
- 0x03e0,
- 0x001f,
- DataBuffer.TYPE_USHORT};
-
- Vector observers;
+ /**
+ * Vector of TileObservers (or null)
+ */
+ Vector tileObservers;
/**
+ * The image's WritableRaster
+ */
+ WritableRaster raster;
+
+ /**
+ * The associated ColorModel
+ */
+ ColorModel colorModel;
+
+ /**
+ * The image's properties (or null)
+ */
+ Hashtable properties;
+
+ /**
+ * Whether alpha is premultiplied
+ */
+ boolean isPremultiplied;
+
+ /**
+ * The predefined type, if any.
+ */
+ int type;
+
+ /**
* Creates a new <code>BufferedImage</code> with the specified width, height
* and type. Valid <code>type</code> values are:
*
@@ -119,155 +130,181 @@ public class BufferedImage extends Image
* <li>{@link #TYPE_BYTE_INDEXED}</li>
* </ul>
*
- * @param w the width (must be > 0).
- * @param h the height (must be > 0).
+ * @param width the width (must be > 0).
+ * @param height the height (must be > 0).
* @param type the image type (see the list of valid types above).
*
- * @throws IllegalArgumentException if <code>w</code> or <code>h</code> is
- * less than or equal to zero.
+ * @throws IllegalArgumentException if <code>width</code> or
+ * <code>height</code> is less than or equal to zero.
* @throws IllegalArgumentException if <code>type</code> is not one of the
* specified values.
*/
- public BufferedImage(int w, int h, int type)
+ public BufferedImage(int width, int height, int type)
{
+ SampleModel sm = null;
ColorModel cm = null;
-
- boolean alpha = false;
- boolean premultiplied = false;
- switch (type)
- {
- case TYPE_4BYTE_ABGR_PRE:
- case TYPE_INT_ARGB_PRE:
- premultiplied = true;
- // fall through
- case TYPE_INT_ARGB:
- case TYPE_4BYTE_ABGR:
- alpha = true;
- }
-
- ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
- switch (type)
+ boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE ||
+ type == BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+ switch( type )
{
- case TYPE_INT_RGB:
- case TYPE_INT_ARGB:
- case TYPE_INT_ARGB_PRE:
- case TYPE_USHORT_565_RGB:
- case TYPE_USHORT_555_RGB:
- int[] masks = null;
- switch (type)
- {
- case TYPE_INT_RGB:
- case TYPE_INT_ARGB:
- case TYPE_INT_ARGB_PRE:
- masks = masks_int;
- break;
- case TYPE_USHORT_565_RGB:
- masks = masks_565;
- break;
- case TYPE_USHORT_555_RGB:
- masks = masks_555;
- break;
- }
-
- cm = new DirectColorModel(cs,
- 32, // 32 bits in an int
- masks[0], // r
- masks[1], // g
- masks[2], // b
- alpha ? 0xff000000 : 0,
- premultiplied,
- masks[3] // data type
- );
+ case BufferedImage.TYPE_INT_RGB:
+ sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
+ width, height,
+ new int[]{ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF } ) ;
+ cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff );
break;
- case TYPE_INT_BGR:
- String msg =
- "FIXME: Programmer is confused. Why (and how) does a " +
- "TYPE_INT_BGR image use ComponentColorModel to store " +
- "8-bit values? Is data type TYPE_INT or TYPE_BYTE. What " +
- "is the difference between TYPE_INT_BGR and TYPE_3BYTE_BGR?";
- throw new UnsupportedOperationException(msg);
-
- case TYPE_3BYTE_BGR:
- case TYPE_4BYTE_ABGR:
- case TYPE_4BYTE_ABGR_PRE:
- case TYPE_BYTE_GRAY:
- case TYPE_USHORT_GRAY:
- int[] bits = null;
- int dataType = DataBuffer.TYPE_BYTE;
- switch (type) {
- case TYPE_3BYTE_BGR:
- bits = bits3;
- break;
- case TYPE_4BYTE_ABGR:
- case TYPE_4BYTE_ABGR_PRE:
- bits = bits4;
- break;
- case TYPE_BYTE_GRAY:
- bits = bits1byte;
- cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
- break;
- case TYPE_USHORT_GRAY:
- bits = bits1ushort;
- cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
- dataType = DataBuffer.TYPE_USHORT;
- break;
- }
- cm = new ComponentColorModel(cs, bits, alpha, premultiplied,
- alpha ?
- Transparency.TRANSLUCENT:
- Transparency.OPAQUE,
- dataType);
+ case BufferedImage.TYPE_3BYTE_BGR:
+ sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
+ width, height,
+ 3, width * 3,
+ new int[]{ 2, 1, 0 } );
+ cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ false, false,
+ BufferedImage.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+ break;
+
+ case BufferedImage.TYPE_INT_ARGB:
+ case BufferedImage.TYPE_INT_ARGB_PRE:
+ sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
+ width, height,
+ new int[]{ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF,
+ 0xFF000000 } );
+ if (premultiplied)
+ cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ 32, 0xff0000, 0xff00, 0xff, 0xff000000,
+ true,
+ Buffers.smallestAppropriateTransferType(32));
+ else
+ cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
+ break;
+
+ case BufferedImage.TYPE_4BYTE_ABGR:
+ case BufferedImage.TYPE_4BYTE_ABGR_PRE:
+ sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height,
+ 4, 4*width,
+ new int[]{3, 2, 1, 0});
+ cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ true, premultiplied,
+ BufferedImage.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+ break;
+
+ case BufferedImage.TYPE_INT_BGR:
+ sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT,
+ width, height,
+ new int[]{ 0x000000FF,
+ 0x0000FF00,
+ 0x00FF0000 } ) ;
+ cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 );
+ break;
+
+ case BufferedImage.TYPE_USHORT_565_RGB:
+ sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
+ width, height,
+ new int[]{ 0xF800,
+ 0x7E0,
+ 0x1F } ) ;
+ cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F );
+ break;
+ case BufferedImage.TYPE_USHORT_555_RGB:
+ sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
+ width, height,
+ new int[]{ 0x7C00,
+ 0x3E0,
+ 0x1F } ) ;
+ cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F );
break;
- case TYPE_BYTE_BINARY:
- byte[] vals = { 0, (byte) 0xff };
- cm = new IndexColorModel(8, 2, vals, vals, vals);
+
+ case BufferedImage.TYPE_BYTE_INDEXED:
+ cm = createDefaultIndexedColorModel( false );
+
+ case BufferedImage.TYPE_BYTE_GRAY:
+ sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
+ width, height,
+ 1, width, new int[]{ 0 } );
+ break;
+
+ case BufferedImage.TYPE_USHORT_GRAY:
+ sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT,
+ width, height,
+ 1, width, new int[]{ 0 } );
+ break;
+
+ case BufferedImage.TYPE_BYTE_BINARY:
+ cm = createDefaultIndexedColorModel( true );
+ sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height, 1);
break;
- case TYPE_BYTE_INDEXED:
- String msg2 = "type not implemented yet";
- throw new UnsupportedOperationException(msg2);
- // FIXME: build color-cube and create color model
+
default:
- throw new IllegalArgumentException("Unknown image type " + type);
+ sm = null;
}
+
+ if( sm == null )
+ throw new IllegalArgumentException("Unknown predefined image type.");
- init(cm,
- cm.createCompatibleWritableRaster(w, h),
- premultiplied,
- null, // no properties
- type
- );
+ if( cm == null ) // only for the grayscale types
+ {
+ int buftype;
+ int[] bits = new int[1];
+ if( type == BufferedImage.TYPE_BYTE_GRAY )
+ {
+ buftype = DataBuffer.TYPE_BYTE;
+ bits[0] = 8;
+ }
+ else
+ {
+ buftype = DataBuffer.TYPE_USHORT;
+ bits[0] = 16;
+ }
+ ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY );
+
+ cm = new ComponentColorModel( graySpace, bits, false, false,
+ Transparency.OPAQUE, buftype );
+ }
+
+ init( cm,
+ Raster.createWritableRaster(sm, new Point( 0, 0 ) ),
+ premultiplied,
+ null, // no properties
+ type );
}
public BufferedImage(int w, int h, int type,
IndexColorModel indexcolormodel)
{
if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED))
- throw new IllegalArgumentException("type must be binary or indexed");
+ throw new IllegalArgumentException("Type must be TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED");
+ if( indexcolormodel.getMapSize() > 16 && type == TYPE_BYTE_BINARY )
+ throw new IllegalArgumentException("Type TYPE_BYTE_BINARY cannot have a larger than 16-color palette.");
+ if( indexcolormodel.getMapSize() > 256 )
+ throw new IllegalArgumentException("Byte type cannot have a larger than 256-color palette.");
- init(indexcolormodel,
- indexcolormodel.createCompatibleWritableRaster(w, h),
- false, // not premultiplied (guess)
- null, // no properties
- type);
+ init( indexcolormodel,
+ indexcolormodel.createCompatibleWritableRaster(w, h),
+ indexcolormodel.isAlphaPremultiplied(),
+ null, // no properties
+ type );
}
public BufferedImage(ColorModel colormodel,
WritableRaster writableraster,
boolean premultiplied,
- Hashtable properties)
+ Hashtable<?,?> properties)
{
init(colormodel, writableraster, premultiplied, properties,
TYPE_CUSTOM);
- // TODO: perhaps try to identify type?
}
- WritableRaster raster;
- ColorModel colorModel;
- Hashtable properties;
- boolean isPremultiplied;
- int type;
-
+
private void init(ColorModel cm,
WritableRaster writableraster,
boolean premultiplied,
@@ -280,12 +317,48 @@ public class BufferedImage extends Image
isPremultiplied = premultiplied;
this.type = type;
}
-
- //public void addTileObserver(TileObserver tileobserver) {}
+
+ /**
+ * Creates the default palettes for the predefined indexed color types
+ * (256-color or black-and-white)
+ *
+ * @param binary - If <code>true</code>, a black and white palette,
+ * otherwise a default 256-color palette is returned.
+ */
+ private IndexColorModel createDefaultIndexedColorModel( boolean binary )
+ {
+ if( binary )
+ {
+ byte[] t = new byte[]{ 0, (byte)255 };
+ return new IndexColorModel( 1, 2, t, t, t );
+ }
+
+ byte[] r = new byte[256];
+ byte[] g = new byte[256];
+ byte[] b = new byte[256];
+ int index = 0;
+ for( int i = 0; i < 6; i++ )
+ for( int j = 0; j < 6; j++ )
+ for( int k = 0; k < 6; k++ )
+ {
+ r[ index ] = (byte)(i * 51);
+ g[ index ] = (byte)(j * 51);
+ b[ index ] = (byte)(k * 51);
+ index++;
+ }
+ while( index < 256 )
+ {
+ r[ index ] = g[ index ] = b[ index ] =
+ (byte)(18 + (index - 216) * 6);
+ index++;
+ }
+ return new IndexColorModel( 8, 256, r, g, b );
+ }
public void coerceData(boolean premultiplied)
{
colorModel = colorModel.coerceData(raster, premultiplied);
+ isPremultiplied = premultiplied;
}
public WritableRaster copyData(WritableRaster dest)
@@ -555,7 +628,7 @@ public class BufferedImage extends Image
};
}
- public Vector getSources()
+ public Vector<RenderedImage> getSources()
{
return null;
}
@@ -726,10 +799,10 @@ public class BufferedImage extends Image
*/
public void addTileObserver (TileObserver to)
{
- if (observers == null)
- observers = new Vector ();
+ if (tileObservers == null)
+ tileObservers = new Vector ();
- observers.add (to);
+ tileObservers.add (to);
}
/**
@@ -741,10 +814,10 @@ public class BufferedImage extends Image
*/
public void removeTileObserver (TileObserver to)
{
- if (observers == null)
+ if (tileObservers == null)
return;
- observers.remove (to);
+ tileObservers.remove (to);
}
/**
diff --git a/libjava/classpath/java/awt/image/ColorConvertOp.java b/libjava/classpath/java/awt/image/ColorConvertOp.java
index 1f85a5ec..e6c8541 100644
--- a/libjava/classpath/java/awt/image/ColorConvertOp.java
+++ b/libjava/classpath/java/awt/image/ColorConvertOp.java
@@ -38,7 +38,10 @@ exception statement from your version. */
package java.awt.image;
+import gnu.java.awt.Buffers;
+
import java.awt.Graphics2D;
+import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
@@ -47,9 +50,9 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
/**
- * ColorConvertOp is a filter for converting an image from one colorspace to
- * another colorspace. The filter can convert the image through a sequence
- * of colorspaces or just from source to destination.
+ * ColorConvertOp is a filter for converting images or rasters between
+ * colorspaces, either through a sequence of colorspaces or just from source to
+ * destination.
*
* Color conversion is done on the color components without alpha. Thus
* if a BufferedImage has alpha premultiplied, this is divided out before
@@ -63,24 +66,22 @@ import java.awt.geom.Rectangle2D;
*/
public class ColorConvertOp implements BufferedImageOp, RasterOp
{
- private ColorSpace srccs;
- private ColorSpace dstcs;
private RenderingHints hints;
- private ICC_Profile[] profiles;
+ private ICC_Profile[] profiles = null;
private ColorSpace[] spaces;
- private boolean rasterValid;
/**
- * Convert BufferedImage through a ColorSpace.
+ * Convert a BufferedImage through a ColorSpace.
*
- * This filter version is only valid for BufferedImages. The source image
- * is converted to cspace. If the destination is not null, it is then
- * converted to the destination colorspace. Normally this filter will only
- * be used with a null destination.
+ * Objects created with this constructor can be used to convert
+ * BufferedImage's to a destination ColorSpace. Attempts to convert Rasters
+ * with this constructor will result in an IllegalArgumentException when the
+ * filter(Raster, WritableRaster) method is called.
*
* @param cspace The target color space.
- * @param hints Rendering hints to use in conversion, or null.
+ * @param hints Rendering hints to use in conversion, if any (may be null)
+ * @throws NullPointerException if the ColorSpace is null.
*/
public ColorConvertOp(ColorSpace cspace, RenderingHints hints)
{
@@ -88,9 +89,27 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
throw new NullPointerException();
spaces = new ColorSpace[]{cspace};
this.hints = hints;
- rasterValid = false;
}
+ /**
+ * Convert from a source colorspace to a destination colorspace.
+ *
+ * This constructor takes two ColorSpace arguments as the source and
+ * destination color spaces. It is usually used with the
+ * filter(Raster, WritableRaster) method, in which case the source colorspace
+ * is assumed to correspond to the source Raster, and the destination
+ * colorspace with the destination Raster.
+ *
+ * If used with BufferedImages that do not match the source or destination
+ * colorspaces specified here, there is an implicit conversion from the
+ * source image to the source ColorSpace, or the destination ColorSpace to
+ * the destination image.
+ *
+ * @param srcCspace The source ColorSpace.
+ * @param dstCspace The destination ColorSpace.
+ * @param hints Rendering hints to use in conversion, if any (may be null).
+ * @throws NullPointerException if any ColorSpace is null.
+ */
public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace,
RenderingHints hints)
{
@@ -101,61 +120,77 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
}
/**
- * Convert from a source image destination image color space.
+ * Convert from a source colorspace to a destinatino colorspace.
*
* This constructor builds a ColorConvertOp from an array of ICC_Profiles.
- * The source image will be converted through the sequence of color spaces
+ * The source will be converted through the sequence of color spaces
* defined by the profiles. If the sequence of profiles doesn't give a
- * well-defined conversion, throws IllegalArgumentException.
- *
- * NOTE: Sun's docs don't clearly define what a well-defined conversion is
- * - or perhaps someone smarter can come along and sort it out.
+ * well-defined conversion, an IllegalArgumentException is thrown.
*
- * For BufferedImages, when the first and last profiles match the
- * requirements of the source and destination color space respectively, the
- * corresponding conversion is unnecessary. TODO: code this up. I don't
- * yet understand how you determine this.
+ * If used with BufferedImages that do not match the source or destination
+ * colorspaces specified here, there is an implicit conversion from the
+ * source image to the source ColorSpace, or the destination ColorSpace to
+ * the destination image.
*
* For Rasters, the first and last profiles must have the same number of
* bands as the source and destination Rasters, respectively. If this is
* not the case, or there fewer than 2 profiles, an IllegalArgumentException
* will be thrown.
*
- * @param profiles
- * @param hints
+ * @param profiles An array of ICC_Profile's to convert through.
+ * @param hints Rendering hints to use in conversion, if any (may be null).
+ * @throws NullPointerException if the profile array is null.
+ * @throws IllegalArgumentException if the array is not a well-defined
+ * conversion.
*/
public ColorConvertOp(ICC_Profile[] profiles, RenderingHints hints)
{
if (profiles == null)
throw new NullPointerException();
+
this.hints = hints;
this.profiles = profiles;
- // TODO: Determine if this is well-defined.
+
// Create colorspace array with space for src and dest colorspace
+ // Note that the ICC_ColorSpace constructor will throw an
+ // IllegalArgumentException if the profile is invalid; thus we check
+ // for a "well defined conversion"
spaces = new ColorSpace[profiles.length];
for (int i = 0; i < profiles.length; i++)
spaces[i] = new ICC_ColorSpace(profiles[i]);
}
- /** Convert from source image color space to destination image color space.
+ /**
+ * Convert from source color space to destination color space.
*
* Only valid for BufferedImage objects, this Op converts from the source
- * color space to the destination color space. The destination can't be
- * null for this operation.
+ * image's color space to the destination image's color space.
*
- * @param hints Rendering hints to use during conversion, or null.
+ * The destination in the filter(BufferedImage, BufferedImage) method cannot
+ * be null for this operation, and it also cannot be used with the
+ * filter(Raster, WritableRaster) method.
+ *
+ * @param hints Rendering hints to use in conversion, if any (may be null).
*/
public ColorConvertOp(RenderingHints hints)
{
- this.hints = hints;
- srccs = null;
- dstcs = null;
- rasterValid = false;
+ this.hints = hints;
+ spaces = new ColorSpace[0];
}
- /* (non-Javadoc)
- * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage,
- java.awt.image.BufferedImage)
+ /**
+ * Converts the source image using the conversion path 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 and destination BufferedImage (if one is supplied) must have
+ * the same dimensions.
+ *
+ * @param src The source image.
+ * @param dst The destination image.
+ * @throws IllegalArgumentException if the rasters and/or color spaces are
+ * incompatible.
+ * @return The transformed image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
@@ -163,129 +198,241 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
// For now we just suck it up and create intermediate buffers.
if (dst == null && spaces.length == 0)
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Not enough color space information "
+ + "to complete conversion.");
+
+ if (dst != null
+ && (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth()))
+ throw new IllegalArgumentException("Source and destination images have "
+ + "different dimensions");
// Make sure input isn't premultiplied by alpha
if (src.isAlphaPremultiplied())
- {
- BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel());
- copyimage(src, tmp);
- tmp.coerceData(false);
- src = tmp;
- }
+ {
+ BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel());
+ copyimage(src, tmp);
+ tmp.coerceData(false);
+ src = tmp;
+ }
- ColorModel scm = src.getColorModel();
+ // Convert through defined intermediate conversions
+ BufferedImage tmp;
for (int i = 0; i < spaces.length; i++)
- {
- BufferedImage tmp = createCompatibleDestImage(src, scm);
- copyimage(src, tmp);
- src = tmp;
- }
+ {
+ if (src.getColorModel().getColorSpace().getType() != spaces[i].getType())
+ {
+ tmp = createCompatibleDestImage(src,
+ createCompatibleColorModel(src,
+ spaces[i]));
+ copyimage(src, tmp);
+ src = tmp;
+ }
+ }
- // Intermediate conversions leave result in src
+ // No implicit conversion to destination type needed; return result from the
+ // last intermediate conversions (which was left in src)
if (dst == null)
- return src;
-
- // Apply final conversion
- copyimage(src, dst);
-
+ dst = src;
+
+ // Implicit conversion to destination image's color space
+ else
+ copyimage(src, dst);
+
return dst;
}
- /* (non-Javadoc)
- * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel)
+ /**
+ * Converts the source raster using the conversion path specified in the
+ * constructor. The resulting raster is stored in the destination raster if
+ * one is provided; otherwise a new WritableRaster is created and returned.
+ *
+ * This operation is not valid with every constructor of this class; see
+ * the constructors for details. Further, the source raster must have the
+ * same number of bands as the source ColorSpace, and the destination raster
+ * must have the same number of bands as the destination ColorSpace.
+ *
+ * The source and destination raster (if one is supplied) must also have the
+ * same dimensions.
+ *
+ * @param src The source raster.
+ * @param dest The destination raster.
+ * @throws IllegalArgumentException if the rasters and/or color spaces are
+ * incompatible.
+ * @return The transformed raster.
+ */
+ public final WritableRaster filter(Raster src, WritableRaster dest)
+ {
+ // Various checks to ensure that the rasters and color spaces are compatible
+ if (spaces.length < 2)
+ throw new IllegalArgumentException("Not enough information about " +
+ "source and destination colorspaces.");
+
+ if (spaces[0].getNumComponents() != src.getNumBands()
+ || (dest != null && spaces[spaces.length - 1].getNumComponents() != dest.getNumBands()))
+ throw new IllegalArgumentException("Source or destination raster " +
+ "contains the wrong number of bands.");
+
+ if (dest != null
+ && (src.getHeight() != dest.getHeight() || src.getWidth() != dest.getWidth()))
+ throw new IllegalArgumentException("Source and destination rasters " +
+ "have different dimensions");
+
+ // Need to iterate through each color space.
+ // spaces[0] corresponds to the ColorSpace of the source raster, and
+ // spaces[spaces.length - 1] corresponds to the ColorSpace of the
+ // destination, with any number (or zero) of intermediate conversions.
+
+ for (int i = 0; i < spaces.length - 2; i++)
+ {
+ WritableRaster tmp = createCompatibleDestRaster(src, spaces[i + 1],
+ false,
+ src.getTransferType());
+ copyraster(src, spaces[i], tmp, spaces[i + 1]);
+ src = tmp;
+ }
+
+ // The last conversion is done outside of the loop so that we can
+ // use the dest raster supplied, instead of creating our own temp raster
+ if (dest == null)
+ dest = createCompatibleDestRaster(src, spaces[spaces.length - 1], false,
+ DataBuffer.TYPE_BYTE);
+ copyraster(src, spaces[spaces.length - 2], dest, spaces[spaces.length - 1]);
+
+ return dest;
+ }
+
+ /**
+ * Creates an empty BufferedImage with the size equal to the source and the
+ * correct number of bands for the conversion defined in this Op. The newly
+ * created image is created with the specified ColorModel, or if no ColorModel
+ * is supplied, an appropriate one is chosen.
+ *
+ * @param src The source image.
+ * @param dstCM A color model for the destination image (may be null).
+ * @throws IllegalArgumentException if an appropriate colormodel cannot be
+ * chosen with the information given.
+ * @return The new compatible destination image.
*/
public BufferedImage createCompatibleDestImage(BufferedImage src,
- ColorModel dstCM)
+ ColorModel dstCM)
{
- // FIXME: set properties to those in src
+ if (dstCM == null && spaces.length == 0)
+ throw new IllegalArgumentException("Don't know the destination " +
+ "colormodel");
+
+ if (dstCM == null)
+ {
+ dstCM = createCompatibleColorModel(src, spaces[spaces.length - 1]);
+ }
+
return new BufferedImage(dstCM,
- src.getRaster().createCompatibleWritableRaster(),
- src.isPremultiplied,
- null);
+ createCompatibleDestRaster(src.getRaster(),
+ dstCM.getColorSpace(),
+ src.getColorModel().hasAlpha,
+ dstCM.getTransferType()),
+ src.isPremultiplied, null);
}
- public final ICC_Profile[] getICC_Profiles()
+ /**
+ * Creates a new WritableRaster with the size equal to the source and the
+ * correct number of bands.
+ *
+ * Note, the new Raster will always use a BYTE storage size, regardless of
+ * the color model or defined destination; this is for compatibility with
+ * the reference implementation.
+ *
+ * @param src The source Raster.
+ * @throws IllegalArgumentException if there isn't enough colorspace
+ * information to create a compatible Raster.
+ * @return The new compatible destination raster.
+ */
+ public WritableRaster createCompatibleDestRaster(Raster src)
{
- return profiles;
- }
+ if (spaces.length < 2)
+ throw new IllegalArgumentException("Not enough destination colorspace " +
+ "information");
- /** Return the rendering hints for this op. */
- public final RenderingHints getRenderingHints()
- {
- return hints;
+ // Create a new raster with the last ColorSpace in the conversion
+ // chain, and with no alpha (implied)
+ return createCompatibleDestRaster(src, spaces[spaces.length-1], false,
+ DataBuffer.TYPE_BYTE);
}
- /* (non-Javadoc)
- * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster)
+ /**
+ * Returns the array of ICC_Profiles used to create this Op, or null if the
+ * Op was created using ColorSpace arguments.
+ *
+ * @return The array of ICC_Profiles, or null.
*/
- public final WritableRaster filter(Raster src, WritableRaster dest)
+ public final ICC_Profile[] getICC_Profiles()
{
- if (!rasterValid)
- throw new IllegalArgumentException();
-
- // Need to iterate through each color space - there must be at least 2
- for (int i = 1; i < spaces.length - 1; i++)
- {
- // FIXME: this is wrong. tmp needs to have the same number of bands as
- // spaces[i] has.
- WritableRaster tmp = createCompatibleDestRaster(src);
- copyraster(src, spaces[i - 1], tmp, spaces[i]);
- src = tmp;
- }
-
- // FIXME: this is wrong. dst needs to have the same number of bands as
- // spaces[i] has.
- if (dest == null)
- dest = createCompatibleDestRaster(src);
- copyraster(src, spaces[spaces.length - 2],
- dest, spaces[spaces.length - 1]);
-
- return dest;
+ return profiles;
}
- /* (non-Javadoc)
- * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
+ /**
+ * Returns the rendering hints for this op.
+ *
+ * @return The rendering hints for this Op, or null.
*/
- public WritableRaster createCompatibleDestRaster(Raster src)
+ public final RenderingHints getRenderingHints()
{
- return src.createCompatibleWritableRaster();
+ return hints;
}
- /** Return corresponding destination point for source point.
+ /**
+ * Returns the corresponding destination point for a source point.
+ * Because this is not a geometric operation, the destination and source
+ * points will be identical.
*
- * LookupOp will return the value of src unchanged.
* @param src The source point.
- * @param dst The destination point.
- * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D)
+ * @param dst The transformed destination point.
+ * @return The transformed destination point.
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
- if (dst == null) return (Point2D)src.clone();
+ if (dst == null)
+ return (Point2D)src.clone();
+
dst.setLocation(src);
return dst;
}
- /* (non-Javadoc)
- * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage)
+ /**
+ * Returns the corresponding destination boundary of a source boundary.
+ * Because this is not a geometric operation, the destination and source
+ * boundaries will be identical.
+ *
+ * @param src The source boundary.
+ * @return The boundaries of the destination.
*/
public final Rectangle2D getBounds2D(BufferedImage src)
{
return src.getRaster().getBounds();
}
- /* (non-Javadoc)
- * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster)
+ /**
+ * Returns the corresponding destination boundary of a source boundary.
+ * Because this is not a geometric operation, the destination and source
+ * boundaries will be identical.
+ *
+ * @param src The source boundary.
+ * @return The boundaries of the destination.
*/
public final Rectangle2D getBounds2D(Raster src)
{
return src.getBounds();
}
-
- // According to Sven de Marothy, we need to copy the src into the dest
- // using Graphics2D, in order to use the rendering hints.
+
+ /**
+ * Copy a source image to a destination image, respecting their colorspaces
+ * and performing colorspace conversions if necessary.
+ *
+ * @param src The source image.
+ * @param dst The destination image.
+ */
private void copyimage(BufferedImage src, BufferedImage dst)
{
+ // This is done using Graphics2D in order to respect the rendering hints.
Graphics2D gg = dst.createGraphics();
// If no hints are set there is no need to call
@@ -297,13 +444,23 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
gg.dispose();
}
- private void copyraster(Raster src, ColorSpace scs, WritableRaster dst,
- ColorSpace dcs)
+ /**
+ * Copy a source raster to a destination raster, performing a colorspace
+ * conversion between the two. The conversion will respect the
+ * KEY_COLOR_RENDERING rendering hint if one is present.
+ *
+ * @param src The source raster.
+ * @param scs The colorspace of the source raster.
+ * @dst The destination raster.
+ * @dcs The colorspace of the destination raster.
+ */
+ private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, ColorSpace dcs)
{
float[] sbuf = new float[src.getNumBands()];
- if (hints.get(RenderingHints.KEY_COLOR_RENDERING) ==
- RenderingHints.VALUE_COLOR_RENDER_QUALITY)
+ if (hints != null
+ && hints.get(RenderingHints.KEY_COLOR_RENDERING) ==
+ RenderingHints.VALUE_COLOR_RENDER_QUALITY)
{
// use cie for accuracy
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
@@ -321,4 +478,60 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp
}
}
+ /**
+ * This method creates a color model with the same colorspace and alpha
+ * settings as the source image. The created color model will always be a
+ * ComponentColorModel and have a BYTE transfer type.
+ *
+ * @param img The source image.
+ * @param cs The ColorSpace to use.
+ * @return A color model compatible with the source image.
+ */
+ private ColorModel createCompatibleColorModel(BufferedImage img, ColorSpace cs)
+ {
+ // The choice of ComponentColorModel and DataBuffer.TYPE_BYTE is based on
+ // Mauve testing of the reference implementation.
+ return new ComponentColorModel(cs,
+ img.getColorModel().hasAlpha(),
+ img.isAlphaPremultiplied(),
+ img.getColorModel().getTransparency(),
+ DataBuffer.TYPE_BYTE);
+ }
+
+ /**
+ * This method creates a compatible Raster, given a source raster, colorspace,
+ * alpha value, and transfer type.
+ *
+ * @param src The source raster.
+ * @param cs The ColorSpace to use.
+ * @param hasAlpha Whether the raster should include a component for an alpha.
+ * @param transferType The size of a single data element.
+ * @return A compatible WritableRaster.
+ */
+ private WritableRaster createCompatibleDestRaster(Raster src, ColorSpace cs,
+ boolean hasAlpha,
+ int transferType)
+ {
+ // The use of a PixelInterleavedSampleModel weas determined using mauve
+ // tests, based on the reference implementation
+
+ int numComponents = cs.getNumComponents();
+ if (hasAlpha)
+ numComponents++;
+
+ int[] offsets = new int[numComponents];
+ for (int i = 0; i < offsets.length; i++)
+ offsets[i] = i;
+
+ DataBuffer db = Buffers.createBuffer(transferType,
+ src.getWidth() * src.getHeight() * numComponents,
+ 1);
+ return new WritableRaster(new PixelInterleavedSampleModel(transferType,
+ src.getWidth(),
+ src.getHeight(),
+ numComponents,
+ numComponents * src.getWidth(),
+ offsets),
+ db, new Point(src.getMinX(), src.getMinY()));
+ }
}
diff --git a/libjava/classpath/java/awt/image/ColorModel.java b/libjava/classpath/java/awt/image/ColorModel.java
index 9e559db..e2873c5 100644
--- a/libjava/classpath/java/awt/image/ColorModel.java
+++ b/libjava/classpath/java/awt/image/ColorModel.java
@@ -624,40 +624,40 @@ public abstract class ColorModel implements Transparency
return cspace;
}
- // Typically overridden
public ColorModel coerceData(WritableRaster raster,
- boolean isAlphaPremultiplied)
+ boolean isAlphaPremultiplied)
{
- if (this.isAlphaPremultiplied == isAlphaPremultiplied)
- return this;
+ // This method should always be overridden, but is not abstract.
+ throw new UnsupportedOperationException();
+ }
+ protected void coerceDataWorker(WritableRaster raster,
+ boolean isAlphaPremultiplied)
+ {
int w = raster.getWidth();
int h = raster.getHeight();
int x = raster.getMinX();
int y = raster.getMinY();
- int size = w*h;
+ int size = w * h;
int numColors = getNumColorComponents();
int numComponents = getNumComponents();
- int alphaScale = (1<<getComponentSize(numColors)) - 1;
+ int alphaScale = (1 << getComponentSize(numColors)) - 1;
double[] pixels = raster.getPixels(x, y, w, h, (double[]) null);
- for (int i=0; i<size; i++)
+ for (int i = 0; i < size; i++)
{
- double alpha = pixels[i*numComponents+numColors]*alphaScale;
- for (int c=0; c<numColors; c++)
- {
- int offset = i*numComponents+c;
- if (isAlphaPremultiplied)
- pixels[offset] = pixels[offset]/alpha;
- else
- pixels[offset] = pixels[offset]*alpha;
- }
+ double alpha = pixels[i * numComponents + numColors] / alphaScale;
+ for (int c = 0; c < numColors; c++)
+ {
+ int offset = i * numComponents + c;
+ if (isAlphaPremultiplied)
+ pixels[offset] = Math.round(pixels[offset] * alpha);
+ else
+ pixels[offset] = Math.round(pixels[offset] / alpha);
+ }
}
-
- raster.setPixels(0, 0, w, h, pixels);
- // FIXME: what can we return?
- return null;
+ raster.setPixels(0, 0, w, h, pixels);
}
/**
diff --git a/libjava/classpath/java/awt/image/ComponentColorModel.java b/libjava/classpath/java/awt/image/ComponentColorModel.java
index f56688f..2096800 100644
--- a/libjava/classpath/java/awt/image/ComponentColorModel.java
+++ b/libjava/classpath/java/awt/image/ComponentColorModel.java
@@ -42,9 +42,11 @@ import gnu.java.awt.Buffers;
import java.awt.Point;
import java.awt.color.ColorSpace;
+import java.util.Arrays;
public class ComponentColorModel extends ColorModel
{
+ // Find sum of all elements of the array.
private static int sum(int[] values)
{
int sum = 0;
@@ -52,6 +54,22 @@ public class ComponentColorModel extends ColorModel
sum += values[i];
return sum;
}
+
+ // Create an appropriate array of bits, given a colorspace (ie, number of
+ // bands), size of the storage data type, and presence of an alpha band.
+ private static int[] findBits(ColorSpace colorSpace, int transferType,
+ boolean hasAlpha)
+ {
+ int[] bits;
+ if (hasAlpha)
+ bits = new int[colorSpace.getNumComponents()+1];
+ else
+ bits = new int[colorSpace.getNumComponents()];
+
+ Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType));
+
+ return bits;
+ }
public ComponentColorModel(ColorSpace colorSpace, int[] bits,
boolean hasAlpha,
@@ -84,8 +102,8 @@ public class ComponentColorModel extends ColorModel
boolean isAlphaPremultiplied,
int transparency, int transferType)
{
- this(colorSpace, null, hasAlpha, isAlphaPremultiplied,
- transparency, transferType);
+ this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha,
+ isAlphaPremultiplied, transparency, transferType);
}
public int getRed(int pixel)
@@ -288,17 +306,16 @@ public class ComponentColorModel extends ColorModel
public ColorModel coerceData(WritableRaster raster,
boolean isAlphaPremultiplied) {
- if (this.isAlphaPremultiplied == isAlphaPremultiplied)
+ if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
return this;
/* TODO: provide better implementation based on the
assumptions we can make due to the specific type of the
color model. */
- super.coerceData(raster, isAlphaPremultiplied);
+ super.coerceDataWorker(raster, isAlphaPremultiplied);
- return new ComponentColorModel(cspace, bits, hasAlpha(),
- isAlphaPremultiplied, // argument
- transparency, transferType);
+ return new ComponentColorModel(cspace, hasAlpha, isAlphaPremultiplied,
+ transparency, transferType);
}
public boolean isCompatibleRaster(Raster raster)
diff --git a/libjava/classpath/java/awt/image/ConvolveOp.java b/libjava/classpath/java/awt/image/ConvolveOp.java
index ffb8348..cf30e76 100644
--- a/libjava/classpath/java/awt/image/ConvolveOp.java
+++ b/libjava/classpath/java/awt/image/ConvolveOp.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package java.awt.image;
-import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@@ -51,11 +50,13 @@ import java.awt.geom.Rectangle2D;
* with elements in the kernel to compute a new pixel.
*
* Each band in a Raster is convolved and copied to the destination Raster.
+ * For BufferedImages, convolution is applied to all components. Color
+ * conversion will be applied if needed.
*
- * For BufferedImages, convolution is applied to all components. If the
- * source is not premultiplied, the data will be premultiplied before
- * convolving. Premultiplication will be undone if the destination is not
- * premultiplied. Color conversion will be applied if needed.
+ * Note that this filter ignores whether the source or destination is alpha
+ * premultiplied. The reference spec states that data will be premultiplied
+ * prior to convolving and divided back out afterwards (if needed), but testing
+ * has shown that this is not the case with their implementation.
*
* @author jlquinn@optonline.net
*/
@@ -104,59 +105,83 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
hints = null;
}
-
- /* (non-Javadoc)
- * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage,
- * java.awt.image.BufferedImage)
+ /**
+ * Converts the source image using the kernel 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 and destination BufferedImage (if one is supplied) must have
+ * the same dimensions.
+ *
+ * @param src The source image.
+ * @param dst The destination image.
+ * @throws IllegalArgumentException if the rasters and/or color spaces are
+ * incompatible.
+ * @return The convolved image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
if (src == dst)
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Source and destination images " +
+ "cannot be the same.");
if (dst == null)
dst = createCompatibleDestImage(src, src.getColorModel());
// Make sure source image is premultiplied
BufferedImage src1 = src;
- if (!src.isPremultiplied)
+ // The spec says we should do this, but mauve testing shows that Sun's
+ // implementation does not check this.
+ /*
+ if (!src.isAlphaPremultiplied())
{
src1 = createCompatibleDestImage(src, src.getColorModel());
src.copyData(src1.getRaster());
src1.coerceData(true);
}
+ */
BufferedImage dst1 = dst;
- if (!src.getColorModel().equals(dst.getColorModel()))
+ if (src1.getColorModel().getColorSpace().getType() != dst.getColorModel().getColorSpace().getType())
dst1 = createCompatibleDestImage(src, src.getColorModel());
filter(src1.getRaster(), dst1.getRaster());
+ // Since we don't coerceData above, we don't need to divide it back out.
+ // This is wrong (one mauve test specifically tests converting a non-
+ // premultiplied image to a premultiplied image, and it shows that Sun
+ // simply ignores the premultipled flag, contrary to the spec), but we
+ // mimic it for compatibility.
+ /*
+ if (! dst.isAlphaPremultiplied())
+ dst1.coerceData(false);
+ */
+
+ // Convert between color models if needed
if (dst1 != dst)
- {
- // Convert between color models.
- // TODO Check that premultiplied alpha is handled correctly here.
- Graphics2D gg = dst.createGraphics();
- gg.setRenderingHints(hints);
- gg.drawImage(dst1, 0, 0, null);
- gg.dispose();
- }
-
+ new ColorConvertOp(hints).filter(dst1, dst);
+
return dst;
}
- /* (non-Javadoc)
- * @see
- * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage,
- * java.awt.image.ColorModel)
+ /**
+ * Creates an empty BufferedImage with the size equal to the source and the
+ * correct number of bands. The new image is created with the specified
+ * ColorModel, or if no ColorModel is supplied, an appropriate one is chosen.
+ *
+ * @param src The source image.
+ * @param dstCM A color model for the destination image (may be null).
+ * @return The new compatible destination image.
*/
public BufferedImage createCompatibleDestImage(BufferedImage src,
- ColorModel dstCM)
+ ColorModel dstCM)
{
- // FIXME: set properties to those in src
- return new BufferedImage(dstCM,
- src.getRaster().createCompatibleWritableRaster(),
- src.isPremultiplied, null);
+ if (dstCM != null)
+ return new BufferedImage(dstCM,
+ src.getRaster().createCompatibleWritableRaster(),
+ src.isAlphaPremultiplied(), null);
+
+ return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
}
/* (non-Javadoc)
@@ -168,6 +193,8 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
}
/**
+ * Get the edge condition for this Op.
+ *
* @return The edge condition.
*/
public int getEdgeCondition()
@@ -185,9 +212,22 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
return (Kernel) kernel.clone();
}
- /* (non-Javadoc)
- * @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
- * java.awt.image.WritableRaster)
+ /**
+ * Converts the source raster using the kernel specified in the constructor.
+ * The resulting raster is stored in the destination raster if one is
+ * provided; otherwise a new WritableRaster is created and returned.
+ *
+ * If the convolved value for a sample is outside the range of [0-255], it
+ * will be clipped.
+ *
+ * The source and destination raster (if one is supplied) cannot be the same,
+ * and must also have the same dimensions.
+ *
+ * @param src The source raster.
+ * @param dest The destination raster.
+ * @throws IllegalArgumentException if the rasters identical.
+ * @throws ImagingOpException if the convolution is not possible.
+ * @return The transformed raster.
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
@@ -209,6 +249,11 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
int top = kernel.getYOrigin();
int bottom = Math.max(kHeight - top - 1, 0);
+ // Calculate max sample values for clipping
+ int[] maxValue = src.getSampleModel().getSampleSize();
+ for (int i = 0; i < maxValue.length; i++)
+ maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1;
+
// process the region that is reachable...
int regionW = src.width - left - right;
int regionH = src.height - top - bottom;
@@ -228,7 +273,14 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
v += tmp[tmp.length - i - 1] * kvals[i];
// FIXME: in the above line, I've had to reverse the order of
// the samples array to make the tests pass. I haven't worked
- // out why this is necessary.
+ // out why this is necessary.
+
+ // This clipping is is undocumented, but determined by testing.
+ if (v > maxValue[b])
+ v = maxValue[b];
+ else if (v < 0)
+ v = 0;
+
dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(),
b, v);
}
@@ -310,13 +362,14 @@ public class ConvolveOp implements BufferedImageOp, RasterOp
return src.getBounds();
}
- /** Return corresponding destination point for source point.
+ /**
+ * Returns the corresponding destination point for a source point. Because
+ * this is not a geometric operation, the destination and source points will
+ * be identical.
*
- * ConvolveOp will return the value of src unchanged.
* @param src The source point.
- * @param dst The destination point.
- * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D,
- * java.awt.geom.Point2D)
+ * @param dst The transformed destination point.
+ * @return The transformed destination point.
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
diff --git a/libjava/classpath/java/awt/image/CropImageFilter.java b/libjava/classpath/java/awt/image/CropImageFilter.java
index 4fcfdec..53b4156 100644
--- a/libjava/classpath/java/awt/image/CropImageFilter.java
+++ b/libjava/classpath/java/awt/image/CropImageFilter.java
@@ -91,11 +91,12 @@ public class CropImageFilter extends ImageFilter
*
* @param props the list of properties associated with this image
*/
- public void setProperties(Hashtable props)
+ public void setProperties(Hashtable<?, ?> props)
{
- props.put("filters", "CropImageFilter");
- if (consumer != null)
- consumer.setProperties(props);
+ Hashtable<Object, Object> prop2 = (Hashtable<Object, Object>) props;
+ prop2.put("filters", "CropImageFilter");
+ if (consumer != null)
+ consumer.setProperties(prop2);
}
/**
diff --git a/libjava/classpath/java/awt/image/DirectColorModel.java b/libjava/classpath/java/awt/image/DirectColorModel.java
index 579dc97..dab1531 100644
--- a/libjava/classpath/java/awt/image/DirectColorModel.java
+++ b/libjava/classpath/java/awt/image/DirectColorModel.java
@@ -393,20 +393,20 @@ public class DirectColorModel extends PackedColorModel
return Buffers.getData(buffer);
}
- public final ColorModel coerceData (WritableRaster raster,
- boolean isAlphaPremultiplied)
+ public ColorModel coerceData (WritableRaster raster,
+ boolean isAlphaPremultiplied)
{
- if (this.isAlphaPremultiplied == isAlphaPremultiplied)
+ if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
return this;
/* TODO: provide better implementation based on the
assumptions we can make due to the specific type of the
color model. */
- super.coerceData(raster, isAlphaPremultiplied);
-
- return new ComponentColorModel(cspace, bits, hasAlpha(),
- isAlphaPremultiplied, // argument
- transparency, transferType);
+ super.coerceDataWorker(raster, isAlphaPremultiplied);
+
+ return new DirectColorModel(cspace, pixel_bits, getRedMask(),
+ getGreenMask(), getBlueMask(), getAlphaMask(),
+ isAlphaPremultiplied, transferType);
}
public boolean isCompatibleRaster(Raster raster)
diff --git a/libjava/classpath/java/awt/image/ImageConsumer.java b/libjava/classpath/java/awt/image/ImageConsumer.java
index fc5ed11..11f64f9 100644
--- a/libjava/classpath/java/awt/image/ImageConsumer.java
+++ b/libjava/classpath/java/awt/image/ImageConsumer.java
@@ -136,7 +136,7 @@ public interface ImageConsumer
*
* @param props the list of properties associated with this image
*/
- void setProperties(Hashtable props);
+ void setProperties(Hashtable<?,?> props);
/**
* This <code>ColorModel</code> should indicate the model used by
diff --git a/libjava/classpath/java/awt/image/ImageFilter.java b/libjava/classpath/java/awt/image/ImageFilter.java
index c39c4a4..0ead45a4 100644
--- a/libjava/classpath/java/awt/image/ImageFilter.java
+++ b/libjava/classpath/java/awt/image/ImageFilter.java
@@ -49,180 +49,178 @@ import java.util.Hashtable;
*/
public class ImageFilter implements ImageConsumer, Cloneable
{
- /**
- * The consumer this filter is filtering an image data stream for.
- * It is initialized in the method <code>getFilterInstance</code>.
- */
- protected ImageConsumer consumer = null;
-
- /**
- * The <code>ImageConsumer</code> can use this method to request
- * the pixels be delivered in top-down, left-right order.
- * <br>
- * The filter can respond in three different ways.
- * <ul>
- * <li>The default behavior is to forward the request to the
- * <code>ImageProducer</code>
- * using the method <code>requestTopDownLeftRightResend</code>
- * and using the filter as the consumer.</li>
- * <li>The filter has the pixels and can retransmit them in the
- * top-down, left-right order.</li>
- * <li>The filter can do nothing when this method is called.</li>
- * </ul>
- */
- public void resendTopDownLeftRight(ImageProducer ip)
- {
- ip.requestTopDownLeftRightResend(this);
- }
-
- /**
- * By default, returns a shallow copy of the object created by
- * <code>Object.clone()</code>
- *
- * @see java.lang.Object#clone ()
- */
- public Object clone()
- {
- try
- {
- return super.clone();
- }
- catch (CloneNotSupportedException e)
- {
- // This should never happen as this class implements the
- // Cloneable interface.
- throw new InternalError ();
- }
- }
-
- /**
- * This is the only method which can set the
- * <code>ImageConsumer</code> for this filter. By default a clone
- * of this filter with the appropriate consumer set is returned.
- *
- * @see #clone ()
- */
- public ImageFilter getFilterInstance(ImageConsumer ic)
- {
- if ( ic == null )
- throw new IllegalArgumentException("null argument for ImageFilter.getFilterInstance(ImageConsumer)");
-
- consumer = ic;
- ImageFilter f = (ImageFilter)clone();
- consumer = null;
- return f;
- }
-
- /**
- * An <code>ImageProducer</code> indicates the size of the image
- * being produced using this method. A filter can override this
- * method to intercept these calls from the producer in order to
- * change either the width or the height before in turn calling
- * the consumer's <code>setDimensions</code> method.
- *
- * @param width the width of the image
- * @param height the height of the image
- */
- public void setDimensions(int width, int height)
- {
- if (consumer != null)
- consumer.setDimensions(width, height);
- }
-
- /**
- * An <code>ImageProducer</code> can set a list of properties
- * associated with this image by using this method.
- *
- * @param props the list of properties associated with this image
- */
- public void setProperties(Hashtable props)
- {
- props.put("filters", "ImageFilter");
- if (consumer != null)
- consumer.setProperties(props);
- }
-
- /**
- * Override this method to process calls to this method from the
- * <code>ImageProducer</code>. By default the <code>setColorModel</code>
- * method of the consumer is called with the specified <code>model</code>.
- *
- * @param model the color model to be used most often by setPixels
- * @see ColorModel */
- public void setColorModel(ColorModel model)
- {
- if (consumer != null)
- consumer.setColorModel(model);
- }
-
- /**
- * The <code>ImageProducer</code> should call this method with a
- * bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
- * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
- * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the
- * <code>ImageConsumer</code> interface.
- *
- * @param flags a bit mask of hints
- * @see ImageConsumer
- */
- public void setHints(int flags)
- {
- if (consumer != null)
- consumer.setHints(flags);
- }
-
- /**
- * This function delivers a rectangle of pixels where any
- * pixel(m,n) is stored in the array as a <code>byte</code> at
- * index (n * scansize + m + offset).
- *
- * @param x the x coordinate of the rectangle
- * @param y the y coordinate of the rectangle
- * @param w the width of the rectangle
- * @param h the height of the rectangle
- * @param model the <code>ColorModel</code> used to translate the pixels
- * @param pixels the array of pixel values
- * @param offset the index of the first pixels in the <code>pixels</code> array
- * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
- */
- public void setPixels(int x, int y, int w, int h,
- ColorModel model, byte[] pixels, int offset, int scansize)
- {
- if (consumer != null)
- consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
- }
-
- /**
- * This function delivers a rectangle of pixels where any
- * pixel(m,n) is stored in the array as an <code>int</code> at
- * index (n * scansize + m + offset).
- *
- * @param x the x coordinate of the rectangle
- * @param y the y coordinate of the rectangle
- * @param w the width of the rectangle
- * @param h the height of the rectangle
- * @param model the <code>ColorModel</code> used to translate the pixels
- * @param pixels the array of pixel values
- * @param offset the index of the first pixels in the <code>pixels</code> array
- * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
- */
- public void setPixels(int x, int y, int w, int h,
- ColorModel model, int[] pixels, int offset, int scansize)
- {
- if (consumer != null)
- consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
- }
-
- /**
- * The <code>ImageProducer</code> calls this method to indicate a
- * single frame or the entire image is complete. The method is
- * also used to indicate an error in loading or producing the
- * image.
- */
- public void imageComplete(int status)
- {
- if (consumer != null)
- consumer.imageComplete(status);
- }
+ /**
+ * The consumer this filter is filtering an image data stream for.
+ * It is initialized in the method <code>getFilterInstance</code>.
+ */
+ protected ImageConsumer consumer = null;
+
+ /**
+ * The <code>ImageConsumer</code> can use this method to request
+ * the pixels be delivered in top-down, left-right order.
+ * <br>
+ * The filter can respond in three different ways.
+ * <ul>
+ * <li>The default behavior is to forward the request to the
+ * <code>ImageProducer</code>
+ * using the method <code>requestTopDownLeftRightResend</code>
+ * and using the filter as the consumer.</li>
+ * <li>The filter has the pixels and can retransmit them in the
+ * top-down, left-right order.</li>
+ * <li>The filter can do nothing when this method is called.</li>
+ * </ul>
+ */
+ public void resendTopDownLeftRight(ImageProducer ip)
+ {
+ ip.requestTopDownLeftRightResend(this);
+ }
+
+ /**
+ * By default, returns a shallow copy of the object created by
+ * <code>Object.clone()</code>
+ *
+ * @see java.lang.Object#clone ()
+ */
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // This should never happen as this class implements the
+ // Cloneable interface.
+ throw new InternalError ();
+ }
+ }
+
+ /**
+ * This is the only method which can set the
+ * <code>ImageConsumer</code> for this filter. By default a clone
+ * of this filter with the appropriate consumer set is returned.
+ *
+ * @see #clone ()
+ */
+ public ImageFilter getFilterInstance(ImageConsumer ic)
+ {
+ ImageFilter f = (ImageFilter)clone();
+ f.consumer = ic;
+ return f;
+ }
+
+ /**
+ * An <code>ImageProducer</code> indicates the size of the image
+ * being produced using this method. A filter can override this
+ * method to intercept these calls from the producer in order to
+ * change either the width or the height before in turn calling
+ * the consumer's <code>setDimensions</code> method.
+ *
+ * @param width the width of the image
+ * @param height the height of the image
+ */
+ public void setDimensions(int width, int height)
+ {
+ consumer.setDimensions(width, height);
+ }
+
+ /**
+ * An <code>ImageProducer</code> can set a list of properties
+ * associated with this image by using this method.
+ *
+ * @param props the list of properties associated with this image
+ */
+ public void setProperties(Hashtable<?,?> props)
+ {
+ Hashtable copy = (Hashtable) props.clone();
+ Object o = copy.get("filters");
+ if (o == null)
+ copy.put("filters", toString());
+ else if (o instanceof String)
+ copy.put("filters", ((String) o) + toString());
+
+ consumer.setProperties(copy);
+ }
+
+ /**
+ * Override this method to process calls to this method from the
+ * <code>ImageProducer</code>. By default the <code>setColorModel</code>
+ * method of the consumer is called with the specified <code>model</code>.
+ *
+ * @param model the color model to be used most often by setPixels
+ *
+ * @see ColorModel
+ */
+ public void setColorModel(ColorModel model)
+ {
+ consumer.setColorModel(model);
+ }
+
+ /**
+ * The <code>ImageProducer</code> should call this method with a
+ * bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
+ * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
+ * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the
+ * <code>ImageConsumer</code> interface.
+ *
+ * @param flags a bit mask of hints
+ * @see ImageConsumer
+ */
+ public void setHints(int flags)
+ {
+ consumer.setHints(flags);
+ }
+
+ /**
+ * This function delivers a rectangle of pixels where any
+ * pixel(m,n) is stored in the array as a <code>byte</code> at
+ * index (n * scansize + m + offset).
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param model the <code>ColorModel</code> used to translate the pixels
+ * @param pixels the array of pixel values
+ * @param offset the index of the first pixels in the <code>pixels</code> array
+ * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
+ */
+ public void setPixels(int x, int y, int w, int h,
+ ColorModel model, byte[] pixels, int offset,
+ int scansize)
+ {
+ consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
+ }
+
+ /**
+ * This function delivers a rectangle of pixels where any
+ * pixel(m,n) is stored in the array as an <code>int</code> at
+ * index (n * scansize + m + offset).
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param model the <code>ColorModel</code> used to translate the pixels
+ * @param pixels the array of pixel values
+ * @param offset the index of the first pixels in the <code>pixels</code> array
+ * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
+ */
+ public void setPixels(int x, int y, int w, int h,
+ ColorModel model, int[] pixels, int offset,
+ int scansize)
+ {
+ consumer.setPixels(x, y, w, h, model, pixels, offset, scansize);
+ }
+
+ /**
+ * The <code>ImageProducer</code> calls this method to indicate a
+ * single frame or the entire image is complete. The method is
+ * also used to indicate an error in loading or producing the
+ * image.
+ */
+ public void imageComplete(int status)
+ {
+ consumer.imageComplete(status);
+ }
}
-
diff --git a/libjava/classpath/java/awt/image/IndexColorModel.java b/libjava/classpath/java/awt/image/IndexColorModel.java
index 299b4dc..46879cc 100644
--- a/libjava/classpath/java/awt/image/IndexColorModel.java
+++ b/libjava/classpath/java/awt/image/IndexColorModel.java
@@ -134,10 +134,6 @@ public class IndexColorModel extends ColorModel
if (size < 1)
throw new IllegalArgumentException("size < 1");
map_size = size;
- if (0 <= trans && trans < size) {
- this.trans = trans;
- transparency = BITMASK;
- }
rgb = new int[size];
for (int i = 0; i < size; i++)
{
@@ -146,6 +142,9 @@ public class IndexColorModel extends ColorModel
| ((greens[i] & 0xff) << 8)
| (blues[i] & 0xff));
}
+
+ setTransparentPixel(trans);
+
// Generate a bigint with 1's for every pixel
validBits = validBits.setBit(size).subtract(BigInteger.ONE);
}
@@ -275,8 +274,6 @@ public class IndexColorModel extends ColorModel
throw new IllegalArgumentException("size < 1");
map_size = size;
opaque = !hasAlpha;
- if (0 <= trans && trans < size)
- this.trans = trans;
rgb = new int[size];
if (hasAlpha)
@@ -318,6 +315,8 @@ public class IndexColorModel extends ColorModel
transparency = BITMASK;
}
+ setTransparentPixel(trans);
+
// Generate a bigint with 1's for every pixel
validBits = validBits.setBit(size).subtract(BigInteger.ONE);
}
@@ -361,9 +360,6 @@ public class IndexColorModel extends ColorModel
throw new IllegalArgumentException("size < 1");
map_size = size;
opaque = !hasAlpha;
- if (0 <= trans && trans < size)
- this.trans = trans;
-
rgb = new int[size];
if (!hasAlpha)
for (int i = 0; i < size; i++)
@@ -371,6 +367,8 @@ public class IndexColorModel extends ColorModel
else
System.arraycopy(cmap, start, rgb, 0, size);
+ setTransparentPixel(trans);
+
// Generate a bigint with 1's for every pixel
validBits = validBits.setBit(size).subtract(BigInteger.ONE);
}
@@ -584,12 +582,7 @@ public class IndexColorModel extends ColorModel
*/
public final int getAlpha(int pixel)
{
- if (opaque && pixel != trans)
- return 255;
- if ((pixel == trans && trans != -1) || pixel >= map_size)
- return 0;
-
- return (0xFF000000 & rgb[pixel]) >> 24;
+ return (rgb[pixel] >> 24) & 0xFF;
}
/**
@@ -694,4 +687,43 @@ public class IndexColorModel extends ColorModel
return im;
}
+
+ /**
+ * Creates a {@link SampleModel} that is compatible to this color model.
+ * This will be a {@link MultiPixelPackedSampleModel} for bits/pixel of
+ * 1, 2 or 4, or a {@link ComponentColorModel} for the other cases.
+ *
+ * @param w the width of the sample model to create
+ * @param h the height of the sample model to create
+ *
+ * @return a compatible sample model
+ */
+ public SampleModel createCompatibleSampleModel(int w, int h)
+ {
+ SampleModel sm;
+ if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4)
+ sm = new MultiPixelPackedSampleModel(transferType, w, h, pixel_bits);
+ else
+ sm = new ComponentSampleModel(transferType, w, h, 1, w, new int[]{0});
+ return sm;
+ }
+
+ /**
+ * Sets the transparent pixel. This is called by the various constructors.
+ *
+ * @param t the transparent pixel
+ */
+ private void setTransparentPixel(int t)
+ {
+ if (t >= 0 && t < map_size)
+ {
+ rgb[t] &= 0xffffff; // Make the value transparent.
+ trans = t;
+ if (transparency == OPAQUE)
+ {
+ transparency = BITMASK;
+ hasAlpha = true;
+ }
+ }
+ }
}
diff --git a/libjava/classpath/java/awt/image/LookupOp.java b/libjava/classpath/java/awt/image/LookupOp.java
index 46e72fe..5b0cf78 100644
--- a/libjava/classpath/java/awt/image/LookupOp.java
+++ b/libjava/classpath/java/awt/image/LookupOp.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package java.awt.image;
-import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@@ -67,7 +66,8 @@ public class LookupOp implements BufferedImageOp, RasterOp
private LookupTable lut;
private RenderingHints hints;
- /** Construct a new LookupOp.
+ /**
+ * Construct a new LookupOp using the given LookupTable.
*
* @param lookup LookupTable to use.
* @param hints Rendering hints (can be null).
@@ -78,16 +78,40 @@ public class LookupOp implements BufferedImageOp, RasterOp
this.hints = hints;
}
- /* (non-Javadoc)
- * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage)
+ /**
+ * Converts the source image using the lookup table 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.
+ *
+ * @param src The source image.
+ * @param dst The destination image.
+ * @throws IllegalArgumentException if the rasters and/or color spaces are
+ * incompatible.
+ * @throws ArrayIndexOutOfBoundsException if a pixel in the source is not
+ * contained in the LookupTable.
+ * @return The convolved image.
*/
public final BufferedImage filter(BufferedImage src, BufferedImage dst)
{
if (src.getColorModel() instanceof IndexColorModel)
throw new IllegalArgumentException("LookupOp.filter: IndexColorModel "
+ "not allowed");
+
+ if (lut.getNumComponents() != 1
+ && lut.getNumComponents() != src.getColorModel().getNumComponents()
+ && lut.getNumComponents() != src.getColorModel().getNumColorComponents())
+ throw new IllegalArgumentException("LookupOp.filter: Incompatible " +
+ "lookup table and source image");
+
if (dst == null)
- dst = createCompatibleDestImage(src, src.getColorModel());
+ dst = createCompatibleDestImage(src, null);
+
+ else if (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth())
+ throw new IllegalArgumentException("Source and destination images are " +
+ "different sizes.");
// Set up for potential colormodel mismatch
BufferedImage tgt;
@@ -116,33 +140,35 @@ public class LookupOp implements BufferedImageOp, RasterOp
sr.getPixel(x, y, dbuf);
System.arraycopy(dbuf, 0, tmp, 0, tmpBands);
dr.setPixel(x, y, lut.lookupPixel(tmp, dbuf));
+
+ /* The reference implementation does not use LookupTable.lookupPixel,
+ * but rather it seems to copy the table into a native array. The
+ * effect of this (a probable bug in their implementation) is that
+ * an out-of-bounds lookup on a ByteLookupTable will *not* throw an
+ * out of bounds exception, but will instead return random garbage.
+ * A bad lookup on a ShortLookupTable, however, will throw an
+ * exception.
+ *
+ * Instead of mimicing this behaviour, we always throw an
+ * ArrayOutofBoundsException by virtue of using
+ * LookupTable.lookupPixle.
+ */
}
}
- else if (lut.getNumComponents() != 1
- &&
- lut.getNumComponents() != src.getColorModel().getNumComponents())
- throw new IllegalArgumentException("LookupOp.filter: "
- + "Incompatible lookup "
- + "table and source image");
-
- // No alpha to ignore
- int[] dbuf = new int[src.getColorModel().getNumComponents()];
-
- // Filter the pixels
- for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
- for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
- dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf));
-
- if (tgt != dst)
+ else
{
- // Convert between color models.
- // TODO Check that premultiplied alpha is handled correctly here.
- Graphics2D gg = dst.createGraphics();
- gg.setRenderingHints(hints);
- gg.drawImage(tgt, 0, 0, null);
- gg.dispose();
+ // No alpha to ignore
+ int[] dbuf = new int[src.getColorModel().getNumComponents()];
+
+ // Filter the pixels
+ for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
+ for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
+ dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf));
}
+ if (tgt != dst)
+ new ColorConvertOp(hints).filter(tgt, dst);
+
return dst;
}
@@ -160,18 +186,27 @@ public class LookupOp implements BufferedImageOp, RasterOp
public BufferedImage createCompatibleDestImage(BufferedImage src,
ColorModel dstCM)
{
- // FIXME: set properties to those in src
- return new BufferedImage(dstCM,
- src.getRaster().createCompatibleWritableRaster(),
- src.isPremultiplied, null);
+ if (dstCM != null)
+ return new BufferedImage(dstCM,
+ src.getRaster().createCompatibleWritableRaster(),
+ src.isAlphaPremultiplied(), null);
+
+ // This is a strange exception, done for compatibility with the reference
+ // (as demonstrated by a mauve testcase)
+ int imgType = src.getType();
+ if (imgType == BufferedImage.TYPE_USHORT_GRAY)
+ imgType = BufferedImage.TYPE_BYTE_GRAY;
+
+ return new BufferedImage(src.getWidth(), src.getHeight(), imgType);
}
- /** Return corresponding destination point for source point.
+ /**
+ * Returns the corresponding destination point for a given source point.
+ *
+ * This Op will return the source point unchanged.
*
- * LookupOp will return the value of src unchanged.
* @param src The source point.
* @param dst The destination point.
- * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D)
*/
public final Point2D getPoint2D(Point2D src, Point2D dst)
{
@@ -182,7 +217,11 @@ public class LookupOp implements BufferedImageOp, RasterOp
return dst;
}
- /** Return the LookupTable for this op. */
+ /**
+ * Return the LookupTable for this op.
+ *
+ * @return The lookup table.
+ */
public final LookupTable getTable()
{
return lut;
@@ -196,7 +235,8 @@ public class LookupOp implements BufferedImageOp, RasterOp
return hints;
}
- /** Filter a raster through a lookup table.
+ /**
+ * Filter a raster through a lookup table.
*
* Applies the lookup table for this Rasterop to each pixel of src and
* puts the results in dest. If dest is null, a new Raster is created and
@@ -206,8 +246,9 @@ public class LookupOp implements BufferedImageOp, RasterOp
* @param dest The destination raster.
* @return The WritableRaster with the filtered pixels.
* @throws IllegalArgumentException if lookup table has more than one
- * component but not the same as src and dest.
- * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster)
+ * component but not the same as src and dest.
+ * @throws ArrayIndexOutOfBoundsException if a pixel in the source is not
+ * contained in the LookupTable.
*/
public final WritableRaster filter(Raster src, WritableRaster dest)
{
@@ -216,12 +257,13 @@ public class LookupOp implements BufferedImageOp, RasterOp
dest = createCompatibleDestRaster(src);
else
if (src.getNumBands() != dest.getNumBands())
- throw new IllegalArgumentException();
-
- if (lut.getNumComponents() != 1
- && lut.getNumComponents() != src.getNumBands())
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Source and destination rasters " +
+ "are incompatible.");
+ if (lut.getNumComponents() != 1
+ && lut.getNumComponents() != src.getNumBands())
+ throw new IllegalArgumentException("Lookup table is incompatible with " +
+ "this raster.");
// Allocate pixel storage.
int[] tmp = new int[src.getNumBands()];
@@ -230,6 +272,19 @@ public class LookupOp implements BufferedImageOp, RasterOp
for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
dest.setPixel(x, y, lut.lookupPixel(src.getPixel(x, y, tmp), tmp));
+
+ /* The reference implementation does not use LookupTable.lookupPixel,
+ * but rather it seems to copy the table into a native array. The
+ * effect of this (a probable bug in their implementation) is that
+ * an out-of-bounds lookup on a ByteLookupTable will *not* throw an
+ * out of bounds exception, but will instead return random garbage.
+ * A bad lookup on a ShortLookupTable, however, will throw an
+ * exception.
+ *
+ * Instead of mimicing this behaviour, we always throw an
+ * ArrayOutofBoundsException by virtue of using
+ * LookupTable.lookupPixle.
+ */
return dest;
}
diff --git a/libjava/classpath/java/awt/image/MemoryImageSource.java b/libjava/classpath/java/awt/image/MemoryImageSource.java
index 95cd408..83a03ca 100644
--- a/libjava/classpath/java/awt/image/MemoryImageSource.java
+++ b/libjava/classpath/java/awt/image/MemoryImageSource.java
@@ -1,5 +1,5 @@
/* MemoryImageSource.java -- Java class for providing image data
- Copyright (C) 1999, 2004 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2004, 2006, Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -41,6 +41,9 @@ package java.awt.image;
import java.util.Hashtable;
import java.util.Vector;
+/**
+ * An image producer that delivers image data from an array.
+ */
public class MemoryImageSource implements ImageProducer
{
private boolean animated = false;
@@ -73,10 +76,19 @@ public class MemoryImageSource implements ImageProducer
}
/**
- * Constructs an ImageProducer from memory
+ * Constructs an ImageProducer from memory.
+ *
+ * @param w the image width.
+ * @param h the image height.
+ * @param cm the color model.
+ * @param pix the image data.
+ * @param off the offset to the first pixel in the array.
+ * @param scan the number of array elements from a pixel on one row to the
+ * corresponding pixel on the next row.
+ * @param props image properties (<code>null</code> permitted).
*/
public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix, int off,
- int scan, Hashtable props)
+ int scan, Hashtable<?,?> props)
{
width = w;
height = h;
@@ -106,10 +118,19 @@ public class MemoryImageSource implements ImageProducer
}
/**
- Constructs an ImageProducer from memory
- */
+ * Constructs an ImageProducer from memory
+ *
+ * @param w the image width.
+ * @param h the image height.
+ * @param cm the color model.
+ * @param pix the image data.
+ * @param off the offset to the first pixel in the array.
+ * @param scan the number of array elements from a pixel on one row to the
+ * corresponding pixel on the next row.
+ * @param props image properties (<code>null</code> permitted).
+ */
public MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off,
- int scan, Hashtable props)
+ int scan, Hashtable<?,?> props)
{
width = w;
height = h;
@@ -122,16 +143,32 @@ public class MemoryImageSource implements ImageProducer
}
/**
- * Constructs an ImageProducer from memory using the default RGB ColorModel
+ * Constructs an ImageProducer from memory using the default RGB ColorModel.
+ *
+ * @param w the image width.
+ * @param h the image height.
+ * @param pix the image data.
+ * @param off the offset to the first pixel in the array.
+ * @param scan the number of array elements from a pixel on one row to the
+ * corresponding pixel on the next row.
+ * @param props image properties (<code>null</code> permitted).
+
*/
public MemoryImageSource(int w, int h, int[] pix, int off, int scan,
- Hashtable props)
+ Hashtable<?,?> props)
{
this(w, h, ColorModel.getRGBdefault(), pix, off, scan, props);
}
/**
- * Constructs an ImageProducer from memory using the default RGB ColorModel
+ * Constructs an ImageProducer from memory using the default RGB ColorModel.
+ *
+ * @param w the image width.
+ * @param h the image height.
+ * @param pix the image data.
+ * @param off the offset to the first pixel in the array.
+ * @param scan the number of array elements from a pixel on one row to the
+ * corresponding pixel on the next row.
*/
public MemoryImageSource(int w, int h, int[] pix, int off, int scan)
{
@@ -141,6 +178,8 @@ public class MemoryImageSource implements ImageProducer
/**
* Used to register an <code>ImageConsumer</code> with this
* <code>ImageProducer</code>.
+ *
+ * @param ic the image consumer.
*/
public synchronized void addConsumer(ImageConsumer ic)
{
@@ -153,6 +192,8 @@ public class MemoryImageSource implements ImageProducer
/**
* Used to determine if the given <code>ImageConsumer</code> is
* already registered with this <code>ImageProducer</code>.
+ *
+ * @param ic the image consumer.
*/
public synchronized boolean isConsumer(ImageConsumer ic)
{
@@ -164,6 +205,8 @@ public class MemoryImageSource implements ImageProducer
/**
* Used to remove an <code>ImageConsumer</code> from the list of
* registered consumers for this <code>ImageProducer</code>.
+ *
+ * @param ic the image consumer.
*/
public synchronized void removeConsumer(ImageConsumer ic)
{
@@ -197,6 +240,8 @@ public class MemoryImageSource implements ImageProducer
* Used to register an <code>ImageConsumer</code> with this
* <code>ImageProducer</code> and then request that this producer
* resend the image data in the order top-down, left-right.
+ *
+ * @param ic the image consumer.
*/
public void requestTopDownLeftRightResend(ImageConsumer ic)
{
@@ -219,7 +264,7 @@ public class MemoryImageSource implements ImageProducer
* sending animation. If this flag is set then full buffers are sent
* in the newPixels methods instead of just regions.
*
- * @param fullbuffers - a flag indicating whether to send the full buffers
+ * @param fullbuffers a flag indicating whether to send the full buffers
*/
public synchronized void setFullBufferUpdates(boolean fullbuffers)
{
@@ -260,6 +305,11 @@ public class MemoryImageSource implements ImageProducer
/**
* Send an animation frame to the image consumers containing the specified
* pixels unless setFullBufferUpdates is set.
+ *
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
*/
public synchronized void newPixels(int x, int y, int w, int h)
{
@@ -306,6 +356,12 @@ public class MemoryImageSource implements ImageProducer
*
* If framenotify is set then a notification is sent when the frame
* is sent otherwise no status is sent.
+ *
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ * @param framenotify send notification?
*/
public synchronized void newPixels(int x, int y, int w, int h,
boolean framenotify)
diff --git a/libjava/classpath/java/awt/image/PixelGrabber.java b/libjava/classpath/java/awt/image/PixelGrabber.java
index 70a80af..fc5e0ef 100644
--- a/libjava/classpath/java/awt/image/PixelGrabber.java
+++ b/libjava/classpath/java/awt/image/PixelGrabber.java
@@ -55,7 +55,7 @@ public class PixelGrabber implements ImageConsumer
ColorModel model = ColorModel.getRGBdefault();
int hints;
- Hashtable props;
+ Hashtable<?,?> props;
int int_pixel_buffer[];
boolean ints_delivered = false;
@@ -442,7 +442,7 @@ public class PixelGrabber implements ImageConsumer
* @param props a list of properties associated with the image being
* produced
*/
- public synchronized void setProperties(Hashtable props)
+ public synchronized void setProperties(Hashtable<?,?> props)
{
this.props = props;
}
diff --git a/libjava/classpath/java/awt/image/RGBImageFilter.java b/libjava/classpath/java/awt/image/RGBImageFilter.java
index ecfed06..c777fec 100644
--- a/libjava/classpath/java/awt/image/RGBImageFilter.java
+++ b/libjava/classpath/java/awt/image/RGBImageFilter.java
@@ -46,228 +46,220 @@ package java.awt.image;
*/
public abstract class RGBImageFilter extends ImageFilter
{
- protected ColorModel origmodel;
+ protected ColorModel origmodel;
- protected ColorModel newmodel;
+ protected ColorModel newmodel;
- /**
- Specifies whether to apply the filter to the index entries of the
- IndexColorModel. Subclasses should set this to true if the filter
- does not depend on the pixel's coordinate.
- */
- protected boolean canFilterIndexColorModel = false;
+ /**
+ * Specifies whether to apply the filter to the index entries of the
+ * IndexColorModel. Subclasses should set this to true if the filter
+ * does not depend on the pixel's coordinate.
+ */
+ protected boolean canFilterIndexColorModel = false;
- /**
- Construct new RGBImageFilter.
- */
- public RGBImageFilter()
- {
- }
+ /**
+ * Construct new RGBImageFilter.
+ */
+ public RGBImageFilter()
+ {
+ }
- /**
- * Sets the ColorModel used to filter with. If the specified ColorModel is IndexColorModel
- * and canFilterIndexColorModel is true, we subsitute the ColorModel for a filtered one
- * here and in setPixels whenever the original one appears. Otherwise overrides the default
- * ColorModel of ImageProducer and specifies the default RGBColorModel
- *
- * @param model the color model to be used most often by setPixels
- * @see ColorModel */
- public void setColorModel(ColorModel model)
- {
- origmodel = model;
- newmodel = model;
+ /**
+ * Sets the ColorModel used to filter with. If the specified ColorModel is
+ * IndexColorModel and canFilterIndexColorModel is true, we subsitute the
+ * ColorModel for a filtered one here and in setPixels whenever the original
+ * one appears. Otherwise overrides the default ColorModel of ImageProducer
+ * and specifies the default RGBColorModel
+ *
+ * @param model the color model to be used most often by setPixels
+ *
+ * @see ColorModel
+ */
+ public void setColorModel(ColorModel model)
+ {
+ if ((model instanceof IndexColorModel) && canFilterIndexColorModel)
+ {
+ ColorModel newCM = filterIndexColorModel((IndexColorModel) model);
+ substituteColorModel(model, newCM);
+ consumer.setColorModel(newmodel);
+ }
+ else
+ {
+ consumer.setColorModel(ColorModel.getRGBdefault());
+ }
+ }
- if( ( model instanceof IndexColorModel) && canFilterIndexColorModel ) {
- newmodel = filterIndexColorModel( (IndexColorModel) model );
- if (consumer != null)
- consumer.setColorModel(newmodel);
- }
- else {
- if (consumer != null)
- consumer.setColorModel(ColorModel.getRGBdefault());
- }
- }
-
- /**
- Registers a new ColorModel to subsitute for the old ColorModel when
- setPixels encounters the a pixel with the old ColorModel. The pixel
- remains unchanged except for a new ColorModel.
-
- @param oldcm the old ColorModel
- @param newcm the new ColorModel
- */
- public void substituteColorModel(ColorModel oldcm,
- ColorModel newcm)
- {
- origmodel = oldcm;
- newmodel = newcm;
- }
-
- /**
- Filters an IndexColorModel through the filterRGB function. Uses
- coordinates of -1 to indicate its filtering an index and not a pixel.
-
- @param icm an IndexColorModel to filter
- */
- public IndexColorModel filterIndexColorModel(IndexColorModel icm)
- {
- int len = icm.getMapSize(), rgb;
- byte reds[] = new byte[len], greens[] = new byte[len], blues[] = new byte[len], alphas[] = new byte[len];
-
- icm.getAlphas( alphas );
- icm.getReds( reds );
- icm.getGreens( greens );
- icm.getBlues( blues );
-
- for( int i = 0; i < len; i++ )
- {
- rgb = filterRGB( -1, -1, makeColor ( alphas[i], reds[i], greens[i], blues[i] ) );
- alphas[i] = (byte)(( 0xff000000 & rgb ) >> 24);
- reds[i] = (byte)(( 0xff0000 & rgb ) >> 16);
- greens[i] = (byte)(( 0xff00 & rgb ) >> 8);
- blues[i] = (byte)(0xff & rgb);
- }
- return new IndexColorModel( icm.getPixelSize(), len, reds, greens, blues, alphas );
- }
-
- private int makeColor( byte a, byte r, byte g, byte b )
- {
- return ( 0xff000000 & (a << 24) | 0xff0000 & (r << 16) | 0xff00 & (g << 8) | 0xff & b );
- }
-
- /**
- This functions filters a set of RGB pixels through filterRGB.
-
- @param x the x coordinate of the rectangle
- @param y the y coordinate of the rectangle
- @param w the width of the rectangle
- @param h the height of the rectangle
- @param pixels the array of pixel values
- @param offset the index of the first pixels in the <code>pixels</code> array
- @param scansize the width to use in extracting pixels from the <code>pixels</code> array
- */
- public void filterRGBPixels(int x, int y, int w, int h, int[] pixels,
- int offset, int scansize)
- {
- for (int yp = 0; yp < h; yp++)
- {
- for (int xp = 0; xp < w; xp++)
- {
- pixels[offset + xp] = filterRGB(xp + x, yp + y, pixels[offset + xp]);
- }
- offset += scansize;
- }
- }
-
-
- /**
- * If the ColorModel is the same ColorModel which as already converted
- * then it converts it the converted ColorModel. Otherwise it passes the
- * array of pixels through filterRGBpixels.
- *
- * @param x the x coordinate of the rectangle
- * @param y the y coordinate of the rectangle
- * @param w the width of the rectangle
- * @param h the height of the rectangle
- * @param model the <code>ColorModel</code> used to translate the pixels
- * @param pixels the array of pixel values
- * @param offset the index of the first pixels in the <code>pixels</code> array
- * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
- */
- public void setPixels(int x, int y, int w, int h,
- ColorModel model, byte[] pixels,
- int offset, int scansize)
- {
- if(model == origmodel && (model instanceof IndexColorModel) && canFilterIndexColorModel)
- {
- if (consumer != null)
- consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
- }
- else
- {
- int intPixels[] =
- convertColorModelToDefault( x, y, w, h, model, pixels, offset, scansize );
- filterRGBPixels( x, y, w, h, intPixels, offset, scansize );
- if (consumer != null)
- consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), intPixels, offset, scansize);
- }
- }
-
- /**
- * This function delivers a rectangle of pixels where any
- * pixel(m,n) is stored in the array as an <code>int</code> at
- * index (n * scansize + m + offset).
- *
- * @param x the x coordinate of the rectangle
- * @param y the y coordinate of the rectangle
- * @param w the width of the rectangle
- * @param h the height of the rectangle
- * @param model the <code>ColorModel</code> used to translate the pixels
- * @param pixels the array of pixel values
- * @param offset the index of the first pixels in the <code>pixels</code> array
- * @param scansize the width to use in extracting pixels from the <code>pixels</code> array
- */
- public void setPixels(int x, int y, int w, int h,
- ColorModel model, int[] pixels,
- int offset, int scansize)
- {
- if(model == origmodel && (model instanceof IndexColorModel) && canFilterIndexColorModel)
- {
- if (consumer != null)
- consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
- }
- else
- {
- //FIXME: Store the filtered pixels in a separate temporary buffer?
- convertColorModelToDefault( x, y, w, h, model, pixels, offset, scansize );
- filterRGBPixels( x, y, w, h, pixels, offset, scansize );
- if (consumer != null)
- consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, offset, scansize);
- }
- }
-
- private int[] convertColorModelToDefault(int x, int y, int w, int h,
- ColorModel model, byte pixels[],
- int offset, int scansize)
- {
- int intPixels[] = new int[pixels.length];
- for (int i = 0; i < pixels.length; i++)
- intPixels[i] = makeColorbyDefaultCM(model, pixels[i]);
- return intPixels;
- }
+ /**
+ * Registers a new ColorModel to subsitute for the old ColorModel when
+ * setPixels encounters the a pixel with the old ColorModel. The pixel
+ * remains unchanged except for a new ColorModel.
+ *
+ * @param oldcm the old ColorModel
+ * @param newcm the new ColorModel
+ */
+ public void substituteColorModel(ColorModel oldcm, ColorModel newcm)
+ {
+ origmodel = oldcm;
+ newmodel = newcm;
+ }
- private void convertColorModelToDefault(int x, int y, int w, int h,
- ColorModel model, int pixels[],
- int offset, int scansize)
- {
- for (int i = 0; i < pixels.length; i++)
- pixels[i] = makeColorbyDefaultCM(model, pixels[i]);
- }
+ /**
+ * Filters an IndexColorModel through the filterRGB function. Uses
+ * coordinates of -1 to indicate its filtering an index and not a pixel.
+ *
+ * @param icm an IndexColorModel to filter
+ */
+ public IndexColorModel filterIndexColorModel(IndexColorModel icm)
+ {
+ int len = icm.getMapSize();
+ byte[] reds = new byte[len];
+ byte[] greens = new byte[len];
+ byte[] blues = new byte[len];
+ byte[] alphas = new byte[len];
- private int makeColorbyDefaultCM(ColorModel model, byte rgb)
- {
- return makeColor( model.getAlpha( rgb ) * 4, model.getRed( rgb ) * 4, model.getGreen( rgb ) * 4, model.getBlue( rgb ) * 4 );
- }
+ icm.getAlphas( alphas );
+ icm.getReds( reds );
+ icm.getGreens( greens );
+ icm.getBlues( blues );
- private int makeColorbyDefaultCM(ColorModel model, int rgb)
- {
- return makeColor( model.getAlpha( rgb ), model.getRed( rgb ), model.getGreen( rgb ), model.getBlue( rgb ) );
- }
+ int transparent = icm.getTransparentPixel();
+ boolean needAlpha = false;
+ for( int i = 0; i < len; i++ )
+ {
+ int rgb = filterRGB(-1, -1, icm.getRGB(i));
+ alphas[i] = (byte) (rgb >> 24);
+ if (alphas[i] != ((byte) 0xff) && i != transparent)
+ needAlpha = true;
+ reds[i] = (byte) (rgb >> 16);
+ greens[i] = (byte) (rgb >> 8);
+ blues[i] = (byte) (rgb);
+ }
+ IndexColorModel newIcm;
+ if (needAlpha)
+ newIcm = new IndexColorModel(icm.getPixelSize(), len, reds, greens,
+ blues, alphas);
+ else
+ newIcm = new IndexColorModel(icm.getPixelSize(), len, reds, greens,
+ blues, transparent);
+ return newIcm;
+ }
- private int makeColor( int a, int r, int g, int b )
- {
- return (int)( 0xff000000 & (a << 24) | 0xff0000 & (r << 16) | 0xff00 & (g << 8) | 0xff & b );
- }
+ /**
+ * This functions filters a set of RGB pixels through filterRGB.
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param pixels the array of pixel values
+ * @param offset the index of the first pixels in the
+ * <code>pixels</code> array
+ * @param scansize the width to use in extracting pixels from the
+ * <code>pixels</code> array
+ */
+ public void filterRGBPixels(int x, int y, int w, int h, int[] pixels,
+ int offset, int scansize)
+ {
+ int index = offset;
+ for (int yp = 0; yp < h; yp++)
+ {
+ for (int xp = 0; xp < w; xp++)
+ {
+ pixels[index] = filterRGB(xp + x, yp + y, pixels[index]);
+ index++;
+ }
+ index += scansize - w;
+ }
+ consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, offset,
+ scansize);
+ }
+ /**
+ * If the ColorModel is the same ColorModel which as already converted
+ * then it converts it the converted ColorModel. Otherwise it passes the
+ * array of pixels through filterRGBpixels.
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param model the <code>ColorModel</code> used to translate the pixels
+ * @param pixels the array of pixel values
+ * @param offset the index of the first pixels in the <code>pixels</code>
+ * array
+ * @param scansize the width to use in extracting pixels from the
+ * <code>pixels</code> array
+ */
+ public void setPixels(int x, int y, int w, int h, ColorModel model,
+ byte[] pixels, int offset, int scansize)
+ {
+ if (model == origmodel)
+ {
+ consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
+ }
+ else
+ {
+ int[] filtered = new int[w];
+ int index = offset;
+ for (int yp = 0; yp < h; yp++)
+ {
+ for (int xp = 0; xp < w; xp++)
+ {
+ filtered[xp] = model.getRGB((pixels[index] & 0xff));
+ index++;
+ }
+ index += scansize - w;
+ filterRGBPixels(x, y + yp, w, 1, filtered, 0, w);
+ }
+ }
+ }
- /**
- Filters a single pixel from the default ColorModel.
+ /**
+ * This function delivers a rectangle of pixels where any
+ * pixel(m,n) is stored in the array as an <code>int</code> at
+ * index (n * scansize + m + offset).
+ *
+ * @param x the x coordinate of the rectangle
+ * @param y the y coordinate of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param model the <code>ColorModel</code> used to translate the pixels
+ * @param pixels the array of pixel values
+ * @param offset the index of the first pixels in the <code>pixels</code>
+ * array
+ * @param scansize the width to use in extracting pixels from the
+ * <code>pixels</code> array
+ */
+ public void setPixels(int x, int y, int w, int h, ColorModel model,
+ int[] pixels, int offset, int scansize)
+ {
+ if (model == origmodel)
+ {
+ consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize);
+ }
+ else
+ {
+ int[] filtered = new int[w];
+ int index = offset;
+ for (int yp = 0; yp < h; yp++)
+ {
+ for (int xp = 0; xp < w; xp++)
+ {
+ filtered[xp] = model.getRGB((pixels[index] & 0xff));
+ index++;
+ }
+ index += scansize - w;
+ filterRGBPixels(x, y + yp, w, 1, filtered, 0, w);
+ }
+ }
+ }
- @param x x-coordinate
- @param y y-coordinate
- @param rgb color
- */
- public abstract int filterRGB(int x,
- int y,
- int rgb);
+ /**
+ * Filters a single pixel from the default ColorModel.
+ *
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @param rgb color
+ */
+ public abstract int filterRGB(int x, int y, int rgb);
}
diff --git a/libjava/classpath/java/awt/image/Raster.java b/libjava/classpath/java/awt/image/Raster.java
index 160f8be..d63e156 100644
--- a/libjava/classpath/java/awt/image/Raster.java
+++ b/libjava/classpath/java/awt/image/Raster.java
@@ -511,9 +511,10 @@ public class Raster
int height, int childMinX, int childMinY,
int[] bandList)
{
- /* FIXME: Throw RasterFormatException if child bounds extends
- beyond the bounds of this raster. */
-
+ if (parentX < minX || parentX + width > minX + this.width
+ || parentY < minY || parentY + height > minY + this.height)
+ throw new RasterFormatException("Child raster extends beyond parent");
+
SampleModel sm = (bandList == null) ?
sampleModel :
sampleModel.createSubsetSampleModel(bandList);
diff --git a/libjava/classpath/java/awt/image/RenderedImage.java b/libjava/classpath/java/awt/image/RenderedImage.java
index b35f860..067e9b9 100644
--- a/libjava/classpath/java/awt/image/RenderedImage.java
+++ b/libjava/classpath/java/awt/image/RenderedImage.java
@@ -46,7 +46,7 @@ import java.util.Vector;
*/
public interface RenderedImage
{
- Vector getSources();
+ Vector<RenderedImage> getSources();
Object getProperty(String name);
String[] getPropertyNames();
ColorModel getColorModel();
diff --git a/libjava/classpath/java/awt/image/ReplicateScaleFilter.java b/libjava/classpath/java/awt/image/ReplicateScaleFilter.java
index 6d5099d..5ba03f1 100644
--- a/libjava/classpath/java/awt/image/ReplicateScaleFilter.java
+++ b/libjava/classpath/java/awt/image/ReplicateScaleFilter.java
@@ -46,6 +46,7 @@ import java.util.Hashtable;
* exact method is not defined by Sun but some sort of fast Box filter should
* probably be correct.
* <br>
+ * Currently this filter does nothing and needs to be implemented.
*
* @author C. Brian Jones (cbj@gnu.org)
*/
@@ -116,11 +117,11 @@ public class ReplicateScaleFilter extends ImageFilter
}
else if (destWidth < 0)
{
- destWidth = (int) (width * ((double) destHeight / srcHeight));
+ destWidth = width * destHeight / srcHeight;
}
else if (destHeight < 0)
{
- destHeight = (int) (height * ((double) destWidth / srcWidth));
+ destHeight = height * destWidth / srcWidth;
}
if (consumer != null)
@@ -133,11 +134,12 @@ public class ReplicateScaleFilter extends ImageFilter
*
* @param props the list of properties associated with this image
*/
- public void setProperties(Hashtable props)
+ public void setProperties(Hashtable<?, ?> props)
{
- props.put("filters", "ReplicateScaleFilter");
- if (consumer != null)
- consumer.setProperties(props);
+ Hashtable<Object, Object> prop2 = (Hashtable<Object, Object>) props;
+ prop2.put("filters", "ReplicateScaleFilter");
+ if (consumer != null)
+ consumer.setProperties(prop2);
}
/**
@@ -157,19 +159,35 @@ public class ReplicateScaleFilter extends ImageFilter
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels, int offset, int scansize)
{
- double rx = ((double) srcWidth) / destWidth;
- double ry = ((double) srcHeight) / destHeight;
-
- int destScansize = (int) Math.round(scansize / rx);
-
- byte[] destPixels = replicatePixels(x, y, w, h,
- model, pixels, offset, scansize,
- rx, ry, destScansize);
-
- if (consumer != null)
- consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry),
- (int) Math.ceil(w/rx), (int) Math.ceil(h/ry),
- model, destPixels, 0, destScansize);
+ if (srcrows == null || srccols == null)
+ setupSources();
+ int dx1 = (2 * x * destWidth + srcWidth - 1) / (2 * destWidth);
+ int dy1 = (2 * y * destHeight + srcHeight - 1) / (2 * destHeight);
+ byte[] pix;
+ if (outpixbuf != null && outpixbuf instanceof byte[])
+ {
+ pix = (byte[]) outpixbuf;
+ }
+ else
+ {
+ pix = new byte[destWidth];
+ outpixbuf = pix;
+ }
+ int sy, sx;
+ for (int yy = dy1; (sy = srcrows[yy]) < y + h; yy++)
+ {
+ int offs = offset + scansize * (sy - y);
+ int xx;
+ for (xx = dx1; (sx = srccols[xx]) < x + w; xx++)
+ {
+ pix[xx] = pixels[offs + sx - x];
+ }
+ if (xx > dx1)
+ {
+ consumer.setPixels(dx1, yy, xx - dx1, 1, model, pix, dx1,
+ destWidth);
+ }
+ }
}
/**
@@ -189,59 +207,52 @@ public class ReplicateScaleFilter extends ImageFilter
public void setPixels(int x, int y, int w, int h,
ColorModel model, int[] pixels, int offset, int scansize)
{
- double rx = ((double) srcWidth) / destWidth;
- double ry = ((double) srcHeight) / destHeight;
-
- int destScansize = (int) Math.round(scansize / rx);
-
- int[] destPixels = replicatePixels(x, y, w, h,
- model, pixels, offset, scansize,
- rx, ry, destScansize);
-
- if (consumer != null)
- consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry),
- (int) Math.ceil(w/rx), (int) Math.ceil(h/ry),
- model, destPixels, 0, destScansize);
- }
-
- private byte[] replicatePixels(int srcx, int srcy, int srcw, int srch,
- ColorModel model, byte[] srcPixels,
- int srcOffset, int srcScansize,
- double rx, double ry, int destScansize)
- {
- byte[] destPixels =
- new byte[(int) Math.ceil(srcw/rx) * (int) Math.ceil(srch/ry)];
-
- int a, b;
- for (int i = 0; i < destPixels.length; i++)
- {
- a = (int) ((int) ( ((double) i) / destScansize) * ry) * srcScansize;
- b = (int) ((i % destScansize) * rx);
- if ((a + b + srcOffset) < srcPixels.length)
- destPixels[i] = srcPixels[a + b + srcOffset];
- }
-
- return destPixels;
+ if (srcrows == null || srccols == null)
+ setupSources();
+ int dx1 = (2 * x * destWidth + srcWidth - 1) / (2 * destWidth);
+ int dy1 = (2 * y * destHeight + srcHeight - 1) / (2 * destHeight);
+ int[] pix;
+ if (outpixbuf != null && outpixbuf instanceof int[])
+ {
+ pix = (int[]) outpixbuf;
+ }
+ else
+ {
+ pix = new int[destWidth];
+ outpixbuf = pix;
+ }
+ int sy, sx;
+ for (int yy = dy1; (sy = srcrows[yy]) < y + h; yy++)
+ {
+ int offs = offset + scansize * (sy - y);
+ int xx;
+ for (xx = dx1; (sx = srccols[xx]) < x + w; xx++)
+ {
+ pix[xx] = pixels[offs + sx - x];
+ }
+ if (xx > dx1)
+ {
+ consumer.setPixels(dx1, yy, xx - dx1, 1, model, pix, dx1,
+ destWidth);
+ }
+ }
}
- private int[] replicatePixels(int srcx, int srcy, int srcw, int srch,
- ColorModel model, int[] srcPixels,
- int srcOffset, int srcScansize,
- double rx, double ry, int destScansize)
- {
- int[] destPixels =
- new int[(int) Math.ceil(srcw/rx) * (int) Math.ceil(srch/ry)];
-
- int a, b;
- for (int i = 0; i < destPixels.length; i++)
- {
- a = (int) ((int) ( ((double) i) / destScansize) * ry) * srcScansize;
- b = (int) ((i % destScansize) * rx);
- if ((a + b + srcOffset) < srcPixels.length)
- destPixels[i] = srcPixels[a + b + srcOffset];
- }
-
- return destPixels;
- }
+ /**
+ * Sets up the srcrows and srccols arrays.
+ */
+ private void setupSources()
+ {
+ srcrows = new int[destHeight + 1];
+ for (int y = 0; y <= destHeight; y++)
+ {
+ srcrows[y] = (2 * y * srcHeight + srcHeight) / (2 * destHeight);
+ }
+ srccols = new int[destWidth + 1];
+ for (int x = 0; x <= destWidth; x++)
+ {
+ srccols[x] = (2 * x * srcWidth + srcWidth) / (2 * destWidth);
+ }
+ }
}
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;
}
diff --git a/libjava/classpath/java/awt/image/SampleModel.java b/libjava/classpath/java/awt/image/SampleModel.java
index cb352bb..506e78a 100644
--- a/libjava/classpath/java/awt/image/SampleModel.java
+++ b/libjava/classpath/java/awt/image/SampleModel.java
@@ -246,9 +246,7 @@ public abstract class SampleModel
public void setDataElements(int x, int y, int w, int h,
Object obj, DataBuffer data)
{
- int size = w * h;
int numDataElements = getNumDataElements();
- int dataSize = numDataElements * size;
Object pixelData;
switch (getTransferType())
@@ -257,25 +255,34 @@ public abstract class SampleModel
pixelData = new byte[numDataElements];
break;
case DataBuffer.TYPE_USHORT:
+ case DataBuffer.TYPE_SHORT:
pixelData = new short[numDataElements];
break;
case DataBuffer.TYPE_INT:
pixelData = new int[numDataElements];
break;
+ case DataBuffer.TYPE_FLOAT:
+ pixelData = new float[numDataElements];
+ break;
+ case DataBuffer.TYPE_DOUBLE:
+ pixelData = new double[numDataElements];
+ break;
default:
- // Seems like the only sensible thing to do.
- throw new ClassCastException();
+ // The RI silently igores invalid types.
+ pixelData = null;
}
- int inOffset = 0;
- for (int yy = y; yy < (y + h); yy++)
+ int inOffset = 0;
+ if (pixelData != null)
{
- for (int xx = x; xx < (x + w); xx++)
+ for (int yy=y; yy<(y+h); yy++)
{
- System.arraycopy(obj, inOffset, pixelData, 0,
- numDataElements);
- setDataElements(xx, yy, pixelData, data);
- inOffset += numDataElements;
+ for (int xx=x; xx<(x+w); xx++)
+ {
+ System.arraycopy(obj, inOffset, pixelData, 0, numDataElements);
+ setDataElements(xx, yy, pixelData, data);
+ inOffset += numDataElements;
+ }
}
}
}
diff --git a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java
index a37fc0b..9ed948c 100644
--- a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java
+++ b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java
@@ -412,110 +412,31 @@ public class SinglePixelPackedSampleModel extends SampleModel
return (samples & bitMasks[b]) >>> bitOffsets[b];
}
- /**
- * This method implements a more efficient way to set data elements than the
- * default implementation of the super class. It sets the data elements line
- * by line instead of pixel by pixel.
- *
- * @param x The x-coordinate of the data elements in <code>obj</code>.
- * @param y The y-coordinate of the data elements in <code>obj</code>.
- * @param w The width of the data elements in <code>obj</code>.
- * @param h The height of the data elements in <code>obj</code>.
- * @param obj The primitive array containing the data elements to set.
- * @param data The DataBuffer to store the data elements into.
- * @see java.awt.image.SampleModel#setDataElements(int, int, int, int,
- * java.lang.Object, java.awt.image.DataBuffer)
- */
- public void setDataElements(int x, int y, int w, int h,
- Object obj, DataBuffer data)
- {
-
- Object pixelData;
- switch (getTransferType())
- {
- case DataBuffer.TYPE_BYTE:
- pixelData = ((DataBufferByte) data).getData();
- break;
- case DataBuffer.TYPE_USHORT:
- pixelData = ((DataBufferUShort) data).getData();
- break;
- case DataBuffer.TYPE_INT:
- pixelData = ((DataBufferInt) data).getData();
- break;
- default:
- // Seems like the only sensible thing to do.
- throw new ClassCastException();
- }
-
- int inOffset = 0;
- int dataOffset = scanlineStride*y + x + data.getOffset();
- for (int yy=y; yy<(y+h); yy++)
- {
- System.arraycopy(obj,inOffset,pixelData,dataOffset,w);
- dataOffset += scanlineStride;
- inOffset += w;
- }
- }
-
-
public void setDataElements(int x, int y, Object obj, DataBuffer data)
{
- int offset = scanlineStride*y + x + data.getOffset();
int transferType = getTransferType();
- if (getTransferType() != data.getDataType())
- {
- throw new IllegalArgumentException("transfer type ("+
- getTransferType()+"), "+
- "does not match data "+
- "buffer type (" +
- data.getDataType() +
- ").");
- }
-
- try
- {
- switch (transferType)
- {
- case DataBuffer.TYPE_BYTE:
- {
- DataBufferByte out = (DataBufferByte) data;
- byte[] in = (byte[]) obj;
- out.getData()[offset] = in[0];
- return;
- }
- case DataBuffer.TYPE_USHORT:
- {
- DataBufferUShort out = (DataBufferUShort) data;
- short[] in = (short[]) obj;
- out.getData()[offset] = in[0];
- return;
- }
- case DataBuffer.TYPE_INT:
- {
- DataBufferInt out = (DataBufferInt) data;
- int[] in = (int[]) obj;
- out.getData()[offset] = in[0];
- return;
- }
- // FIXME: Fill in the other possible types.
- default:
- throw new InternalError();
- }
- }
- catch (ArrayIndexOutOfBoundsException aioobe)
- {
- String msg = "While writing data elements" +
- ", x="+x+", y="+y+
- ", width="+width+", height="+height+
- ", scanlineStride="+scanlineStride+
- ", offset="+offset+
- ", data.getSize()="+data.getSize()+
- ", data.getOffset()="+data.getOffset()+
- ": " +
- aioobe;
- throw new ArrayIndexOutOfBoundsException(msg);
- }
+ switch (transferType)
+ {
+ case DataBuffer.TYPE_BYTE:
+ {
+ byte[] in = (byte[]) obj;
+ data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff);
+ break;
+ }
+ case DataBuffer.TYPE_USHORT:
+ {
+ short[] in = (short[]) obj;
+ data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xffff);
+ break;
+ }
+ case DataBuffer.TYPE_INT:
+ {
+ int[] in = (int[]) obj;
+ data.setElem(y * scanlineStride + x, in[0]);
+ break;
+ }
+ }
}
/**
diff --git a/libjava/classpath/java/awt/image/WritableRaster.java b/libjava/classpath/java/awt/image/WritableRaster.java
index 473c6fe..bf8db14 100644
--- a/libjava/classpath/java/awt/image/WritableRaster.java
+++ b/libjava/classpath/java/awt/image/WritableRaster.java
@@ -136,8 +136,9 @@ public class WritableRaster extends Raster
{
// This mirrors the code from the super class
- // FIXME: Throw RasterFormatException if child bounds extends
- // beyond the bounds of this raster.
+ if (parentX < minX || parentX + w > minX + width
+ || parentY < minY || parentY + h > minY + height)
+ throw new RasterFormatException("Child raster extends beyond parent");
SampleModel sm = (bandList == null) ?
sampleModel :
@@ -149,6 +150,25 @@ public class WritableRaster extends Raster
sampleModelTranslateY + childMinY - parentY),
this);
}
+
+ public Raster createChild(int parentX, int parentY, int width,
+ int height, int childMinX, int childMinY,
+ int[] bandList)
+ {
+ if (parentX < minX || parentX + width > minX + this.width
+ || parentY < minY || parentY + height > minY + this.height)
+ throw new RasterFormatException("Child raster extends beyond parent");
+
+ SampleModel sm = (bandList == null) ?
+ sampleModel :
+ sampleModel.createSubsetSampleModel(bandList);
+
+ return new WritableRaster(sm, dataBuffer,
+ new Rectangle(childMinX, childMinY, width, height),
+ new Point(sampleModelTranslateX + childMinX - parentX,
+ sampleModelTranslateY + childMinY - parentY),
+ this);
+ }
public void setDataElements(int x, int y, Object inData)
{
diff --git a/libjava/classpath/java/awt/image/renderable/ParameterBlock.java b/libjava/classpath/java/awt/image/renderable/ParameterBlock.java
index 879d3c4..e484d6b 100644
--- a/libjava/classpath/java/awt/image/renderable/ParameterBlock.java
+++ b/libjava/classpath/java/awt/image/renderable/ParameterBlock.java
@@ -45,20 +45,20 @@ import java.util.Vector;
public class ParameterBlock implements Cloneable, Serializable
{
private static final long serialVersionUID = -7577115551785240750L;
- protected Vector sources;
- protected Vector parameters;
+ protected Vector<Object> sources;
+ protected Vector<Object> parameters;
public ParameterBlock()
{
- this(new Vector(), new Vector());
+ this(new Vector<Object>(), new Vector<Object>());
}
- public ParameterBlock(Vector sources)
+ public ParameterBlock(Vector<Object> sources)
{
- this(sources, new Vector());
+ this(sources, new Vector<Object>());
}
- public ParameterBlock(Vector sources, Vector parameters)
+ public ParameterBlock(Vector<Object> sources, Vector<Object> parameters)
{
this.sources = sources;
this.parameters = parameters;
@@ -80,9 +80,9 @@ public class ParameterBlock implements Cloneable, Serializable
{
ParameterBlock pb = (ParameterBlock) shallowClone();
if (sources != null)
- pb.sources = (Vector) sources.clone();
+ pb.sources = (Vector<Object>) sources.clone();
if (parameters != null)
- pb.parameters = (Vector) parameters.clone();
+ pb.parameters = (Vector<Object>) parameters.clone();
return pb;
}
@@ -119,12 +119,12 @@ public class ParameterBlock implements Cloneable, Serializable
return sources.size();
}
- public Vector getSources()
+ public Vector<Object> getSources()
{
return sources;
}
- public void setSources(Vector sources)
+ public void setSources(Vector<Object> sources)
{
this.sources = sources;
}
@@ -140,12 +140,12 @@ public class ParameterBlock implements Cloneable, Serializable
return parameters.size();
}
- public Vector getParameters()
+ public Vector<Object> getParameters()
{
return parameters;
}
- public void setParameters(Vector parameters)
+ public void setParameters(Vector<Object> parameters)
{
this.parameters = parameters;
}
diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImage.java b/libjava/classpath/java/awt/image/renderable/RenderableImage.java
index 45d2eb7..c2f6ad8 100644
--- a/libjava/classpath/java/awt/image/renderable/RenderableImage.java
+++ b/libjava/classpath/java/awt/image/renderable/RenderableImage.java
@@ -46,7 +46,7 @@ public interface RenderableImage
{
String HINTS_OBSERVED = "HINTS_OBSERVED";
- Vector getSources();
+ Vector<RenderableImage> getSources();
Object getProperty(String name);
String[] getPropertyNames();
boolean isDynamic();
diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java b/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java
index 5385a82..b9d0cd3 100644
--- a/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java
+++ b/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java
@@ -55,7 +55,7 @@ public class RenderableImageOp implements RenderableImage
this.block = (ParameterBlock) block.clone();
}
- public Vector getSources()
+ public Vector<RenderableImage> getSources()
{
if (block.sources == null)
return null;