| /* |
| * 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; |
| } |
| } |