jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java
changeset 34416 68c0d866db5d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java	Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.common;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.RenderedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.SampleModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+public abstract class SimpleRenderedImage implements RenderedImage {
+    /** The X coordinate of the image's upper-left pixel. */
+    protected int minX;
+
+    /** The Y coordinate of the image's upper-left pixel. */
+    protected int minY;
+
+    /** The image's width in pixels. */
+    protected int width;
+
+    /** The image's height in pixels. */
+    protected int height;
+
+    /** The width of a tile. */
+    protected int tileWidth;
+
+    /** The height of a tile. */
+    protected int tileHeight;
+
+    /** The X coordinate of the upper-left pixel of tile (0, 0). */
+    protected int tileGridXOffset = 0;
+
+    /** The Y coordinate of the upper-left pixel of tile (0, 0). */
+    protected int tileGridYOffset = 0;
+
+    /** The image's SampleModel. */
+    protected SampleModel sampleModel;
+
+    /** The image's ColorModel. */
+    protected ColorModel colorModel;
+
+    /** The image's sources, stored in a Vector. */
+    protected Vector<RenderedImage> sources = new Vector<RenderedImage>();
+
+    /** A Hashtable containing the image properties. */
+    protected Hashtable<String,Object> properties = new Hashtable<String,Object>();
+
+    /** Returns the X coordinate of the leftmost column of the image. */
+    public int getMinX() {
+        return minX;
+    }
+
+    /**
+     * Returns the X coordinate of the column immediatetely to the
+     * right of the rightmost column of the image.  getMaxX() is
+     * implemented in terms of getMinX() and getWidth() and so does
+     * not need to be implemented by subclasses.
+     */
+    public final int getMaxX() {
+        return getMinX() + getWidth();
+    }
+
+    /** Returns the X coordinate of the uppermost row of the image. */
+    public int getMinY() {
+        return minY;
+    }
+
+    /**
+     * Returns the Y coordinate of the row immediately below the
+     * bottom row of the image.  getMaxY() is implemented in terms of
+     * getMinY() and getHeight() and so does not need to be
+     * implemented by subclasses.
+     */
+    public final int getMaxY() {
+        return getMinY() + getHeight();
+    }
+
+    /** Returns the width of the image. */
+    public int getWidth() {
+        return width;
+    }
+
+    /** Returns the height of the image. */
+    public int getHeight() {
+        return height;
+    }
+
+    /** Returns a Rectangle indicating the image bounds. */
+    public Rectangle getBounds() {
+        return new Rectangle(getMinX(), getMinY(), getWidth(), getHeight());
+    }
+
+    /** Returns the width of a tile. */
+    public int getTileWidth() {
+        return tileWidth;
+    }
+
+    /** Returns the height of a tile. */
+    public int getTileHeight() {
+        return tileHeight;
+    }
+
+    /**
+     * Returns the X coordinate of the upper-left pixel of tile (0, 0).
+     */
+    public int getTileGridXOffset() {
+        return tileGridXOffset;
+    }
+
+    /**
+     * Returns the Y coordinate of the upper-left pixel of tile (0, 0).
+     */
+    public int getTileGridYOffset() {
+        return tileGridYOffset;
+    }
+
+    /**
+     * Returns the horizontal index of the leftmost column of tiles.
+     * getMinTileX() is implemented in terms of getMinX()
+     * and so does not need to be implemented by subclasses.
+     */
+    public int getMinTileX() {
+        return XToTileX(getMinX());
+    }
+
+    /**
+     * Returns the horizontal index of the rightmost column of tiles.
+     * getMaxTileX() is implemented in terms of getMaxX()
+     * and so does not need to be implemented by subclasses.
+     */
+    public int getMaxTileX() {
+        return XToTileX(getMaxX() - 1);
+    }
+
+    /**
+     * Returns the number of tiles along the tile grid in the
+     * horizontal direction.  getNumXTiles() is implemented in terms
+     * of getMinTileX() and getMaxTileX() and so does not need to be
+     * implemented by subclasses.
+     */
+    public int getNumXTiles() {
+        return getMaxTileX() - getMinTileX() + 1;
+    }
+
+    /**
+     * Returns the vertical index of the uppermost row of tiles.  getMinTileY()
+     * is implemented in terms of getMinY() and so does not need to be
+     * implemented by subclasses.
+     */
+    public int getMinTileY() {
+        return YToTileY(getMinY());
+    }
+
+    /**
+     * Returns the vertical index of the bottom row of tiles.  getMaxTileY()
+     * is implemented in terms of getMaxY() and so does not need to
+     * be implemented by subclasses.
+     */
+    public int getMaxTileY() {
+        return YToTileY(getMaxY() - 1);
+    }
+
+    /**
+     * Returns the number of tiles along the tile grid in the vertical
+     * direction.  getNumYTiles() is implemented in terms
+     * of getMinTileY() and getMaxTileY() and so does not need to be
+     * implemented by subclasses.
+     */
+    public int getNumYTiles() {
+        return getMaxTileY() - getMinTileY() + 1;
+    }
+
+    /** Returns the SampleModel of the image. */
+    public SampleModel getSampleModel() {
+        return sampleModel;
+    }
+
+    /** Returns the ColorModel of the image. */
+    public ColorModel getColorModel() {
+        return colorModel;
+    }
+
+    /**
+     * Gets a property from the property set of this image.  If the
+     * property name is not recognized,
+     * <code>java.awt.Image.UndefinedProperty</code> will be returned.
+     *
+     * @param name the name of the property to get, as a
+     * <code>String</code>.  @return a reference to the property
+     * <code>Object</code>, or the value
+     * <code>java.awt.Image.UndefinedProperty.</code>
+     */
+    public Object getProperty(String name) {
+        name = name.toLowerCase();
+        Object value = properties.get(name);
+        return value != null ? value : java.awt.Image.UndefinedProperty;
+    }
+
+    /**
+     * Returns a list of the properties recognized by this image.  If
+     * no properties are available, <code>null</code> will be
+     * returned.
+     *
+     * @return an array of <code>String</code>s representing valid
+     *         property names.
+     */
+    public String[] getPropertyNames() {
+        String[] names = null;
+
+        if(properties.size() > 0) {
+            names = new String[properties.size()];
+            int index = 0;
+
+            Enumeration<String> e = properties.keys();
+            while (e.hasMoreElements()) {
+                String name = e.nextElement();
+                names[index++] = name;
+            }
+        }
+
+        return names;
+    }
+
+    /**
+     * Returns an array of <code>String</code>s recognized as names by
+     * this property source that begin with the supplied prefix.  If
+     * no property names match, <code>null</code> will be returned.
+     * The comparison is done in a case-independent manner.
+     *
+     * <p> The default implementation calls
+     * <code>getPropertyNames()</code> and searches the list of names
+     * for matches.
+     *
+     * @return an array of <code>String</code>s giving the valid
+     * property names.
+     */
+    public String[] getPropertyNames(String prefix) {
+        String propertyNames[] = getPropertyNames();
+        if (propertyNames == null) {
+            return null;
+        }
+
+        prefix = prefix.toLowerCase();
+
+        Vector<String> names = new Vector<String>();
+        for (int i = 0; i < propertyNames.length; i++) {
+            if (propertyNames[i].startsWith(prefix)) {
+                names.addElement(propertyNames[i]);
+            }
+        }
+
+        if (names.size() == 0) {
+            return null;
+        }
+
+        // Copy the strings from the Vector over to a String array.
+        String prefixNames[] = new String[names.size()];
+        int count = 0;
+        for (Iterator<String> it = names.iterator(); it.hasNext(); ) {
+            prefixNames[count++] = it.next();
+        }
+
+        return prefixNames;
+    }
+
+    // Utility methods.
+
+    /**
+     * Converts a pixel's X coordinate into a horizontal tile index
+     * relative to a given tile grid layout specified by its X offset
+     * and tile width.
+     */
+    public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
+        x -= tileGridXOffset;
+        if (x < 0) {
+            x += 1 - tileWidth; // Force round to -infinity
+        }
+        return x/tileWidth;
+    }
+
+    /**
+     * Converts a pixel's Y coordinate into a vertical tile index
+     * relative to a given tile grid layout specified by its Y offset
+     * and tile height.
+     */
+    public static int YToTileY(int y, int tileGridYOffset, int tileHeight) {
+        y -= tileGridYOffset;
+        if (y < 0) {
+            y += 1 - tileHeight; // Force round to -infinity
+        }
+        return y/tileHeight;
+    }
+
+    /**
+     * Converts a pixel's X coordinate into a horizontal tile index.
+     * This is a convenience method.  No attempt is made to detect
+     * out-of-range coordinates.
+     *
+     * @param x the X coordinate of a pixel.
+     * @return the X index of the tile containing the pixel.
+     */
+    public int XToTileX(int x) {
+        return XToTileX(x, getTileGridXOffset(), getTileWidth());
+    }
+
+    /**
+     * Converts a pixel's Y coordinate into a vertical tile index.
+     * This is a convenience method.  No attempt is made to detect
+     * out-of-range coordinates.
+     *
+     * @param y the Y coordinate of a pixel.
+     * @return the Y index of the tile containing the pixel.
+     */
+    public int YToTileY(int y) {
+        return YToTileY(y, getTileGridYOffset(), getTileHeight());
+    }
+
+    /**
+     * Converts a horizontal tile index into the X coordinate of its
+     * upper left pixel relative to a given tile grid layout specified
+     * by its X offset and tile width.
+     */
+    public static int tileXToX(int tx, int tileGridXOffset, int tileWidth) {
+        return tx*tileWidth + tileGridXOffset;
+    }
+
+    /**
+     * Converts a vertical tile index into the Y coordinate of
+     * its upper left pixel relative to a given tile grid layout
+     * specified by its Y offset and tile height.
+     */
+    public static int tileYToY(int ty, int tileGridYOffset, int tileHeight) {
+        return ty*tileHeight + tileGridYOffset;
+    }
+
+    /**
+     * Converts a horizontal tile index into the X coordinate of its
+     * upper left pixel.  This is a convenience method.  No attempt is made
+     * to detect out-of-range indices.
+     *
+     * @param tx the horizontal index of a tile.
+     * @return the X coordinate of the tile's upper left pixel.
+     */
+    public int tileXToX(int tx) {
+        return tx*tileWidth + tileGridXOffset;
+    }
+
+    /**
+     * Converts a vertical tile index into the Y coordinate of its
+     * upper left pixel.  This is a convenience method.  No attempt is made
+     * to detect out-of-range indices.
+     *
+     * @param ty the vertical index of a tile.
+     * @return the Y coordinate of the tile's upper left pixel.
+     */
+    public int tileYToY(int ty) {
+        return ty*tileHeight + tileGridYOffset;
+    }
+
+    public Vector<RenderedImage> getSources() {
+        return null;
+    }
+
+    /**
+     * Returns the entire image in a single Raster.  For images with
+     * multiple tiles this will require making a copy.
+     *
+     * <p> The returned Raster is semantically a copy.  This means
+     * that updates to the source image will not be reflected in the
+     * returned Raster.  For non-writable (immutable) source images,
+     * the returned value may be a reference to the image's internal
+     * data.  The returned Raster should be considered non-writable;
+     * any attempt to alter its pixel data (such as by casting it to
+     * WritableRaster or obtaining and modifying its DataBuffer) may
+     * result in undefined behavior.  The copyData method should be
+     * used if the returned Raster is to be modified.
+     *
+     * @return a Raster containing a copy of this image's data.
+     */
+    public Raster getData() {
+        Rectangle rect = new Rectangle(getMinX(), getMinY(),
+                                       getWidth(), getHeight());
+        return getData(rect);
+    }
+
+    /**
+     * Returns an arbitrary rectangular region of the RenderedImage
+     * in a Raster.  The rectangle of interest will be clipped against
+     * the image bounds.
+     *
+     * <p> The returned Raster is semantically a copy.  This means
+     * that updates to the source image will not be reflected in the
+     * returned Raster.  For non-writable (immutable) source images,
+     * the returned value may be a reference to the image's internal
+     * data.  The returned Raster should be considered non-writable;
+     * any attempt to alter its pixel data (such as by casting it to
+     * WritableRaster or obtaining and modifying its DataBuffer) may
+     * result in undefined behavior.  The copyData method should be
+     * used if the returned Raster is to be modified.
+     *
+     * @param bounds the region of the RenderedImage to be returned.
+     */
+    public Raster getData(Rectangle bounds) {
+        // Get the image bounds.
+        Rectangle imageBounds = getBounds();
+
+        // Check for parameter validity.
+        if(bounds == null) {
+            bounds = imageBounds;
+        } else if(!bounds.intersects(imageBounds)) {
+            throw new IllegalArgumentException("The provided region doesn't intersect with the image bounds.");
+        }
+
+        // Determine tile limits for the prescribed bounds.
+        int startX = XToTileX(bounds.x);
+        int startY = YToTileY(bounds.y);
+        int endX = XToTileX(bounds.x + bounds.width - 1);
+        int endY = YToTileY(bounds.y + bounds.height - 1);
+
+        // If the bounds are contained in a single tile, return a child
+        // of that tile's Raster.
+        if ((startX == endX) && (startY == endY)) {
+            Raster tile = getTile(startX, startY);
+            return tile.createChild(bounds.x, bounds.y,
+                                    bounds.width, bounds.height,
+                                    bounds.x, bounds.y, null);
+        } else {
+            // Recalculate the tile limits if the data bounds are not a
+            // subset of the image bounds.
+            if(!imageBounds.contains(bounds)) {
+                Rectangle xsect = bounds.intersection(imageBounds);
+                startX = XToTileX(xsect.x);
+                startY = YToTileY(xsect.y);
+                endX = XToTileX(xsect.x + xsect.width - 1);
+                endY = YToTileY(xsect.y + xsect.height - 1);
+            }
+
+            // Create a WritableRaster of the desired size
+            SampleModel sm =
+                sampleModel.createCompatibleSampleModel(bounds.width,
+                                                        bounds.height);
+
+            // Translate it
+            WritableRaster dest =
+                Raster.createWritableRaster(sm, bounds.getLocation());
+
+            // Loop over the tiles in the intersection.
+            for (int j = startY; j <= endY; j++) {
+                for (int i = startX; i <= endX; i++) {
+                    // Retrieve the tile.
+                    Raster tile = getTile(i, j);
+
+                    // Create a child of the tile for the intersection of
+                    // the tile bounds and the bounds of the requested area.
+                    Rectangle tileRect = tile.getBounds();
+                    Rectangle intersectRect =
+                        bounds.intersection(tile.getBounds());
+                    Raster liveRaster = tile.createChild(intersectRect.x,
+                                                         intersectRect.y,
+                                                         intersectRect.width,
+                                                         intersectRect.height,
+                                                         intersectRect.x,
+                                                         intersectRect.y,
+                                                         null);
+
+                    // Copy the data from the child.
+                    dest.setRect(liveRaster);
+                }
+            }
+
+            return dest;
+        }
+    }
+
+    /**
+     * Copies an arbitrary rectangular region of the RenderedImage
+     * into a caller-supplied WritableRaster.  The region to be
+     * computed is determined by clipping the bounds of the supplied
+     * WritableRaster against the bounds of the image.  The supplied
+     * WritableRaster must have a SampleModel that is compatible with
+     * that of the image.
+     *
+     * <p> If the raster argument is null, the entire image will
+     * be copied into a newly-created WritableRaster with a SampleModel
+     * that is compatible with that of the image.
+     *
+     * @param dest a WritableRaster to hold the returned portion of
+     *        the image.
+     * @return a reference to the supplied WritableRaster, or to a
+     *         new WritableRaster if the supplied one was null.
+     */
+    public WritableRaster copyData(WritableRaster dest) {
+        // Get the image bounds.
+        Rectangle imageBounds = getBounds();
+
+        Rectangle bounds;
+        if (dest == null) {
+            // Create a WritableRaster for the entire image.
+            bounds = imageBounds;
+            Point p = new Point(minX, minY);
+            SampleModel sm =
+                sampleModel.createCompatibleSampleModel(width, height);
+            dest = Raster.createWritableRaster(sm, p);
+        } else {
+            bounds = dest.getBounds();
+        }
+
+        // Determine tile limits for the intersection of the prescribed
+        // bounds with the image bounds.
+        Rectangle xsect = imageBounds.contains(bounds) ?
+            bounds : bounds.intersection(imageBounds);
+        int startX = XToTileX(xsect.x);
+        int startY = YToTileY(xsect.y);
+        int endX = XToTileX(xsect.x + xsect.width - 1);
+        int endY = YToTileY(xsect.y + xsect.height - 1);
+
+        // Loop over the tiles in the intersection.
+        for (int j = startY; j <= endY; j++) {
+            for (int i = startX; i <= endX; i++) {
+                // Retrieve the tile.
+                Raster tile = getTile(i, j);
+
+                // Create a child of the tile for the intersection of
+                // the tile bounds and the bounds of the requested area.
+                Rectangle tileRect = tile.getBounds();
+                Rectangle intersectRect =
+                    bounds.intersection(tile.getBounds());
+                Raster liveRaster = tile.createChild(intersectRect.x,
+                                                     intersectRect.y,
+                                                     intersectRect.width,
+                                                     intersectRect.height,
+                                                     intersectRect.x,
+                                                     intersectRect.y,
+                                                     null);
+
+                // Copy the data from the child.
+                dest.setRect(liveRaster);
+            }
+        }
+
+        return dest;
+    }
+}