--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/java/awt/image/BufferedImage.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1641 @@
+/*
+ * Copyright (c) 1997, 2017, 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 java.awt.image;
+
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.Vector;
+
+import sun.awt.image.ByteComponentRaster;
+import sun.awt.image.BytePackedRaster;
+import sun.awt.image.IntegerComponentRaster;
+import sun.awt.image.OffScreenImageSource;
+import sun.awt.image.ShortComponentRaster;
+
+/**
+ *
+ * The {@code BufferedImage} subclass describes an {@link
+ * java.awt.Image Image} with an accessible buffer of image data.
+ * A {@code BufferedImage} is comprised of a {@link ColorModel} and a
+ * {@link Raster} of image data.
+ * The number and types of bands in the {@link SampleModel} of the
+ * {@code Raster} must match the number and types required by the
+ * {@code ColorModel} to represent its color and alpha components.
+ * All {@code BufferedImage} objects have an upper left corner
+ * coordinate of (0, 0). Any {@code Raster} used to construct a
+ * {@code BufferedImage} must therefore have minX=0 and minY=0.
+ *
+ * <p>
+ * This class relies on the data fetching and setting methods
+ * of {@code Raster},
+ * and on the color characterization methods of {@code ColorModel}.
+ *
+ * @see ColorModel
+ * @see Raster
+ * @see WritableRaster
+ */
+public class BufferedImage extends java.awt.Image
+ implements WritableRenderedImage, Transparency
+{
+ private int imageType = TYPE_CUSTOM;
+ private ColorModel colorModel;
+ private final WritableRaster raster;
+ private OffScreenImageSource osis;
+ private Hashtable<String, Object> properties;
+
+ /**
+ * Image Type Constants
+ */
+
+ /**
+ * Image type is not recognized so it must be a customized
+ * image. This type is only used as a return value for the getType()
+ * method.
+ */
+ public static final int TYPE_CUSTOM = 0;
+
+ /**
+ * Represents an image with 8-bit RGB color components packed into
+ * integer pixels. The image has a {@link DirectColorModel} without
+ * alpha.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_INT_RGB = 1;
+
+ /**
+ * Represents an image with 8-bit RGBA color components packed into
+ * integer pixels. The image has a {@code DirectColorModel}
+ * with alpha. The color data in this image is considered not to be
+ * premultiplied with alpha. When this type is used as the
+ * {@code imageType} argument to a {@code BufferedImage}
+ * constructor, the created image is consistent with images
+ * created in the JDK1.1 and earlier releases.
+ */
+ public static final int TYPE_INT_ARGB = 2;
+
+ /**
+ * Represents an image with 8-bit RGBA color components packed into
+ * integer pixels. The image has a {@code DirectColorModel}
+ * with alpha. The color data in this image is considered to be
+ * premultiplied with alpha.
+ */
+ public static final int TYPE_INT_ARGB_PRE = 3;
+
+ /**
+ * Represents an image with 8-bit RGB color components, corresponding
+ * to a Windows- or Solaris- style BGR color model, with the colors
+ * Blue, Green, and Red packed into integer pixels. There is no alpha.
+ * The image has a {@link DirectColorModel}.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_INT_BGR = 4;
+
+ /**
+ * Represents an image with 8-bit RGB color components, corresponding
+ * to a Windows-style BGR color model) with the colors Blue, Green,
+ * and Red stored in 3 bytes. There is no alpha. The image has a
+ * {@code ComponentColorModel}.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_3BYTE_BGR = 5;
+
+ /**
+ * Represents an image with 8-bit RGBA color components with the colors
+ * Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The
+ * image has a {@code ComponentColorModel} with alpha. The
+ * color data in this image is considered not to be premultiplied with
+ * alpha. The byte data is interleaved in a single
+ * byte array in the order A, B, G, R
+ * from lower to higher byte addresses within each pixel.
+ */
+ public static final int TYPE_4BYTE_ABGR = 6;
+
+ /**
+ * Represents an image with 8-bit RGBA color components with the colors
+ * Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The
+ * image has a {@code ComponentColorModel} with alpha. The color
+ * data in this image is considered to be premultiplied with alpha.
+ * The byte data is interleaved in a single byte array in the order
+ * A, B, G, R from lower to higher byte addresses within each pixel.
+ */
+ public static final int TYPE_4BYTE_ABGR_PRE = 7;
+
+ /**
+ * Represents an image with 5-6-5 RGB color components (5-bits red,
+ * 6-bits green, 5-bits blue) with no alpha. This image has
+ * a {@code DirectColorModel}.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_USHORT_565_RGB = 8;
+
+ /**
+ * Represents an image with 5-5-5 RGB color components (5-bits red,
+ * 5-bits green, 5-bits blue) with no alpha. This image has
+ * a {@code DirectColorModel}.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_USHORT_555_RGB = 9;
+
+ /**
+ * Represents a unsigned byte grayscale image, non-indexed. This
+ * image has a {@code ComponentColorModel} with a CS_GRAY
+ * {@link ColorSpace}.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_BYTE_GRAY = 10;
+
+ /**
+ * Represents an unsigned short grayscale image, non-indexed). This
+ * image has a {@code ComponentColorModel} with a CS_GRAY
+ * {@code ColorSpace}.
+ * When data with non-opaque alpha is stored
+ * in an image of this type,
+ * the color data must be adjusted to a non-premultiplied form
+ * and the alpha discarded,
+ * as described in the
+ * {@link java.awt.AlphaComposite} documentation.
+ */
+ public static final int TYPE_USHORT_GRAY = 11;
+
+ /**
+ * Represents an opaque byte-packed 1, 2, or 4 bit image. The
+ * image has an {@link IndexColorModel} without alpha. When this
+ * type is used as the {@code imageType} argument to the
+ * {@code BufferedImage} constructor that takes an
+ * {@code imageType} argument but no {@code ColorModel}
+ * argument, a 1-bit image is created with an
+ * {@code IndexColorModel} with two colors in the default
+ * sRGB {@code ColorSpace}: {0, 0, 0} and
+ * {255, 255, 255}.
+ *
+ * <p> Images with 2 or 4 bits per pixel may be constructed via
+ * the {@code BufferedImage} constructor that takes a
+ * {@code ColorModel} argument by supplying a
+ * {@code ColorModel} with an appropriate map size.
+ *
+ * <p> Images with 8 bits per pixel should use the image types
+ * {@code TYPE_BYTE_INDEXED} or {@code TYPE_BYTE_GRAY}
+ * depending on their {@code ColorModel}.
+
+ * <p> When color data is stored in an image of this type,
+ * the closest color in the colormap is determined
+ * by the {@code IndexColorModel} and the resulting index is stored.
+ * Approximation and loss of alpha or color components
+ * can result, depending on the colors in the
+ * {@code IndexColorModel} colormap.
+ */
+ public static final int TYPE_BYTE_BINARY = 12;
+
+ /**
+ * Represents an indexed byte image. When this type is used as the
+ * {@code imageType} argument to the {@code BufferedImage}
+ * constructor that takes an {@code imageType} argument
+ * but no {@code ColorModel} argument, an
+ * {@code IndexColorModel} is created with
+ * a 256-color 6/6/6 color cube palette with the rest of the colors
+ * from 216-255 populated by grayscale values in the
+ * default sRGB ColorSpace.
+ *
+ * <p> When color data is stored in an image of this type,
+ * the closest color in the colormap is determined
+ * by the {@code IndexColorModel} and the resulting index is stored.
+ * Approximation and loss of alpha or color components
+ * can result, depending on the colors in the
+ * {@code IndexColorModel} colormap.
+ */
+ public static final int TYPE_BYTE_INDEXED = 13;
+
+ private static final int DCM_RED_MASK = 0x00ff0000;
+ private static final int DCM_GREEN_MASK = 0x0000ff00;
+ private static final int DCM_BLUE_MASK = 0x000000ff;
+ private static final int DCM_ALPHA_MASK = 0xff000000;
+ private static final int DCM_565_RED_MASK = 0xf800;
+ private static final int DCM_565_GRN_MASK = 0x07E0;
+ private static final int DCM_565_BLU_MASK = 0x001F;
+ private static final int DCM_555_RED_MASK = 0x7C00;
+ private static final int DCM_555_GRN_MASK = 0x03E0;
+ private static final int DCM_555_BLU_MASK = 0x001F;
+ private static final int DCM_BGR_RED_MASK = 0x0000ff;
+ private static final int DCM_BGR_GRN_MASK = 0x00ff00;
+ private static final int DCM_BGR_BLU_MASK = 0xff0000;
+
+
+ private static native void initIDs();
+ static {
+ ColorModel.loadLibraries();
+ initIDs();
+ }
+
+ /**
+ * Constructs a {@code BufferedImage} of one of the predefined
+ * image types. The {@code ColorSpace} for the image is the
+ * default sRGB space.
+ * @param width width of the created image
+ * @param height height of the created image
+ * @param imageType type of the created image
+ * @see ColorSpace
+ * @see #TYPE_INT_RGB
+ * @see #TYPE_INT_ARGB
+ * @see #TYPE_INT_ARGB_PRE
+ * @see #TYPE_INT_BGR
+ * @see #TYPE_3BYTE_BGR
+ * @see #TYPE_4BYTE_ABGR
+ * @see #TYPE_4BYTE_ABGR_PRE
+ * @see #TYPE_BYTE_GRAY
+ * @see #TYPE_USHORT_GRAY
+ * @see #TYPE_BYTE_BINARY
+ * @see #TYPE_BYTE_INDEXED
+ * @see #TYPE_USHORT_565_RGB
+ * @see #TYPE_USHORT_555_RGB
+ */
+ public BufferedImage(int width,
+ int height,
+ int imageType) {
+ switch (imageType) {
+ case TYPE_INT_RGB:
+ {
+ colorModel = new DirectColorModel(24,
+ 0x00ff0000, // Red
+ 0x0000ff00, // Green
+ 0x000000ff, // Blue
+ 0x0 // Alpha
+ );
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_INT_ARGB:
+ {
+ colorModel = ColorModel.getRGBdefault();
+
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_INT_ARGB_PRE:
+ {
+ colorModel = new
+ DirectColorModel(
+ ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ 32,
+ 0x00ff0000,// Red
+ 0x0000ff00,// Green
+ 0x000000ff,// Blue
+ 0xff000000,// Alpha
+ true, // Alpha Premultiplied
+ DataBuffer.TYPE_INT
+ );
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_INT_BGR:
+ {
+ colorModel = new DirectColorModel(24,
+ 0x000000ff, // Red
+ 0x0000ff00, // Green
+ 0x00ff0000 // Blue
+ );
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_3BYTE_BGR:
+ {
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ int[] nBits = {8, 8, 8};
+ int[] bOffs = {2, 1, 0};
+ colorModel = new ComponentColorModel(cs, nBits, false, false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+ raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
+ width, height,
+ width*3, 3,
+ bOffs, null);
+ }
+ break;
+
+ case TYPE_4BYTE_ABGR:
+ {
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ int[] nBits = {8, 8, 8, 8};
+ int[] bOffs = {3, 2, 1, 0};
+ colorModel = new ComponentColorModel(cs, nBits, true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+ raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
+ width, height,
+ width*4, 4,
+ bOffs, null);
+ }
+ break;
+
+ case TYPE_4BYTE_ABGR_PRE:
+ {
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ int[] nBits = {8, 8, 8, 8};
+ int[] bOffs = {3, 2, 1, 0};
+ colorModel = new ComponentColorModel(cs, nBits, true, true,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+ raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
+ width, height,
+ width*4, 4,
+ bOffs, null);
+ }
+ break;
+
+ case TYPE_BYTE_GRAY:
+ {
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+ int[] nBits = {8};
+ colorModel = new ComponentColorModel(cs, nBits, false, true,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_USHORT_GRAY:
+ {
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+ int[] nBits = {16};
+ colorModel = new ComponentColorModel(cs, nBits, false, true,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_USHORT);
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_BYTE_BINARY:
+ {
+ byte[] arr = {(byte)0, (byte)0xff};
+
+ colorModel = new IndexColorModel(1, 2, arr, arr, arr);
+ raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE,
+ width, height, 1, 1, null);
+ }
+ break;
+
+ case TYPE_BYTE_INDEXED:
+ {
+ // Create a 6x6x6 color cube
+ int[] cmap = new int[256];
+ int i=0;
+ for (int r=0; r < 256; r += 51) {
+ for (int g=0; g < 256; g += 51) {
+ for (int b=0; b < 256; b += 51) {
+ cmap[i++] = (r<<16)|(g<<8)|b;
+ }
+ }
+ }
+ // And populate the rest of the cmap with gray values
+ int grayIncr = 256/(256-i);
+
+ // The gray ramp will be between 18 and 252
+ int gray = grayIncr*3;
+ for (; i < 256; i++) {
+ cmap[i] = (gray<<16)|(gray<<8)|gray;
+ gray += grayIncr;
+ }
+
+ colorModel = new IndexColorModel(8, 256, cmap, 0, false, -1,
+ DataBuffer.TYPE_BYTE);
+ raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
+ width, height, 1, null);
+ }
+ break;
+
+ case TYPE_USHORT_565_RGB:
+ {
+ colorModel = new DirectColorModel(16,
+ DCM_565_RED_MASK,
+ DCM_565_GRN_MASK,
+ DCM_565_BLU_MASK
+ );
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ case TYPE_USHORT_555_RGB:
+ {
+ colorModel = new DirectColorModel(15,
+ DCM_555_RED_MASK,
+ DCM_555_GRN_MASK,
+ DCM_555_BLU_MASK
+ );
+ raster = colorModel.createCompatibleWritableRaster(width,
+ height);
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException ("Unknown image type " +
+ imageType);
+ }
+
+ this.imageType = imageType;
+ }
+
+ /**
+ * Constructs a {@code BufferedImage} of one of the predefined
+ * image types:
+ * TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED.
+ *
+ * <p> If the image type is TYPE_BYTE_BINARY, the number of
+ * entries in the color model is used to determine whether the
+ * image should have 1, 2, or 4 bits per pixel. If the color model
+ * has 1 or 2 entries, the image will have 1 bit per pixel. If it
+ * has 3 or 4 entries, the image with have 2 bits per pixel. If
+ * it has between 5 and 16 entries, the image will have 4 bits per
+ * pixel. Otherwise, an IllegalArgumentException will be thrown.
+ *
+ * @param width width of the created image
+ * @param height height of the created image
+ * @param imageType type of the created image
+ * @param cm {@code IndexColorModel} of the created image
+ * @throws IllegalArgumentException if the imageType is not
+ * TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED or if the imageType is
+ * TYPE_BYTE_BINARY and the color map has more than 16 entries.
+ * @see #TYPE_BYTE_BINARY
+ * @see #TYPE_BYTE_INDEXED
+ */
+ public BufferedImage (int width,
+ int height,
+ int imageType,
+ IndexColorModel cm) {
+ if (cm.hasAlpha() && cm.isAlphaPremultiplied()) {
+ throw new IllegalArgumentException("This image types do not have "+
+ "premultiplied alpha.");
+ }
+
+ switch(imageType) {
+ case TYPE_BYTE_BINARY:
+ int bits; // Will be set below
+ int mapSize = cm.getMapSize();
+ if (mapSize <= 2) {
+ bits = 1;
+ } else if (mapSize <= 4) {
+ bits = 2;
+ } else if (mapSize <= 16) {
+ bits = 4;
+ } else {
+ throw new IllegalArgumentException
+ ("Color map for TYPE_BYTE_BINARY " +
+ "must have no more than 16 entries");
+ }
+ raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE,
+ width, height, 1, bits, null);
+ break;
+
+ case TYPE_BYTE_INDEXED:
+ raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
+ width, height, 1, null);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid image type (" +
+ imageType+"). Image type must"+
+ " be either TYPE_BYTE_BINARY or "+
+ " TYPE_BYTE_INDEXED");
+ }
+
+ if (!cm.isCompatibleRaster(raster)) {
+ throw new IllegalArgumentException("Incompatible image type and IndexColorModel");
+ }
+
+ colorModel = cm;
+ this.imageType = imageType;
+ }
+
+ /**
+ * Constructs a new {@code BufferedImage} with a specified
+ * {@code ColorModel} and {@code Raster}. If the number and
+ * types of bands in the {@code SampleModel} of the
+ * {@code Raster} do not match the number and types required by
+ * the {@code ColorModel} to represent its color and alpha
+ * components, a {@link RasterFormatException} is thrown. This
+ * method can multiply or divide the color {@code Raster} data by
+ * alpha to match the {@code alphaPremultiplied} state
+ * in the {@code ColorModel}. Properties for this
+ * {@code BufferedImage} can be established by passing
+ * in a {@link Hashtable} of {@code String}/{@code Object}
+ * pairs.
+ * @param cm {@code ColorModel} for the new image
+ * @param raster {@code Raster} for the image data
+ * @param isRasterPremultiplied if {@code true}, the data in
+ * the raster has been premultiplied with alpha.
+ * @param properties {@code Hashtable} of
+ * {@code String}/{@code Object} pairs.
+ * @exception RasterFormatException if the number and
+ * types of bands in the {@code SampleModel} of the
+ * {@code Raster} do not match the number and types required by
+ * the {@code ColorModel} to represent its color and alpha
+ * components.
+ * @exception IllegalArgumentException if
+ * {@code raster} is incompatible with {@code cm}
+ * @see ColorModel
+ * @see Raster
+ * @see WritableRaster
+ */
+
+
+/*
+ *
+ * FOR NOW THE CODE WHICH DEFINES THE RASTER TYPE IS DUPLICATED BY DVF
+ * SEE THE METHOD DEFINERASTERTYPE @ RASTEROUTPUTMANAGER
+ *
+ */
+ public BufferedImage (ColorModel cm,
+ WritableRaster raster,
+ boolean isRasterPremultiplied,
+ Hashtable<?,?> properties) {
+
+ if (!cm.isCompatibleRaster(raster)) {
+ throw new
+ IllegalArgumentException("Raster "+raster+
+ " is incompatible with ColorModel "+
+ cm);
+ }
+
+ if ((raster.minX != 0) || (raster.minY != 0)) {
+ throw new
+ IllegalArgumentException("Raster "+raster+
+ " has minX or minY not equal to zero: "
+ + raster.minX + " " + raster.minY);
+ }
+
+ colorModel = cm;
+ this.raster = raster;
+ if (properties != null && !properties.isEmpty()) {
+ this.properties = new Hashtable<>();
+ for (final Object key : properties.keySet()) {
+ if (key instanceof String) {
+ this.properties.put((String) key, properties.get(key));
+ }
+ }
+ }
+ int numBands = raster.getNumBands();
+ boolean isAlphaPre = cm.isAlphaPremultiplied();
+ final boolean isStandard = isStandard(cm, raster);
+ ColorSpace cs;
+
+ // Force the raster data alpha state to match the premultiplied
+ // state in the color model
+ coerceData(isRasterPremultiplied);
+
+ SampleModel sm = raster.getSampleModel();
+ cs = cm.getColorSpace();
+ int csType = cs.getType();
+ if (csType != ColorSpace.TYPE_RGB) {
+ if (csType == ColorSpace.TYPE_GRAY &&
+ isStandard &&
+ cm instanceof ComponentColorModel) {
+ // Check if this might be a child raster (fix for bug 4240596)
+ if (sm instanceof ComponentSampleModel &&
+ ((ComponentSampleModel)sm).getPixelStride() != numBands) {
+ imageType = TYPE_CUSTOM;
+ } else if (raster instanceof ByteComponentRaster &&
+ raster.getNumBands() == 1 &&
+ cm.getComponentSize(0) == 8 &&
+ ((ByteComponentRaster)raster).getPixelStride() == 1) {
+ imageType = TYPE_BYTE_GRAY;
+ } else if (raster instanceof ShortComponentRaster &&
+ raster.getNumBands() == 1 &&
+ cm.getComponentSize(0) == 16 &&
+ ((ShortComponentRaster)raster).getPixelStride() == 1) {
+ imageType = TYPE_USHORT_GRAY;
+ }
+ } else {
+ imageType = TYPE_CUSTOM;
+ }
+ return;
+ }
+
+ if ((raster instanceof IntegerComponentRaster) &&
+ (numBands == 3 || numBands == 4)) {
+ IntegerComponentRaster iraster =
+ (IntegerComponentRaster) raster;
+ // Check if the raster params and the color model
+ // are correct
+ int pixSize = cm.getPixelSize();
+ if (iraster.getPixelStride() == 1 &&
+ isStandard &&
+ cm instanceof DirectColorModel &&
+ (pixSize == 32 || pixSize == 24))
+ {
+ // Now check on the DirectColorModel params
+ DirectColorModel dcm = (DirectColorModel) cm;
+ int rmask = dcm.getRedMask();
+ int gmask = dcm.getGreenMask();
+ int bmask = dcm.getBlueMask();
+ if (rmask == DCM_RED_MASK && gmask == DCM_GREEN_MASK &&
+ bmask == DCM_BLUE_MASK)
+ {
+ if (dcm.getAlphaMask() == DCM_ALPHA_MASK) {
+ imageType = (isAlphaPre
+ ? TYPE_INT_ARGB_PRE
+ : TYPE_INT_ARGB);
+ }
+ else {
+ // No Alpha
+ if (!dcm.hasAlpha()) {
+ imageType = TYPE_INT_RGB;
+ }
+ }
+ } // if (dcm.getRedMask() == DCM_RED_MASK &&
+ else if (rmask == DCM_BGR_RED_MASK && gmask == DCM_BGR_GRN_MASK
+ && bmask == DCM_BGR_BLU_MASK) {
+ if (!dcm.hasAlpha()) {
+ imageType = TYPE_INT_BGR;
+ }
+ } // if (rmask == DCM_BGR_RED_MASK &&
+ } // if (iraster.getPixelStride() == 1
+ } // ((raster instanceof IntegerComponentRaster) &&
+ else if ((cm instanceof IndexColorModel) && (numBands == 1) &&
+ isStandard &&
+ (!cm.hasAlpha() || !isAlphaPre))
+ {
+ IndexColorModel icm = (IndexColorModel) cm;
+ int pixSize = icm.getPixelSize();
+
+ if (raster instanceof BytePackedRaster) {
+ imageType = TYPE_BYTE_BINARY;
+ } // if (raster instanceof BytePackedRaster)
+ else if (raster instanceof ByteComponentRaster) {
+ ByteComponentRaster braster = (ByteComponentRaster) raster;
+ if (braster.getPixelStride() == 1 && pixSize <= 8) {
+ imageType = TYPE_BYTE_INDEXED;
+ }
+ }
+ } // else if (cm instanceof IndexColorModel) && (numBands == 1))
+ else if ((raster instanceof ShortComponentRaster)
+ && (cm instanceof DirectColorModel)
+ && isStandard
+ && (numBands == 3)
+ && !cm.hasAlpha())
+ {
+ DirectColorModel dcm = (DirectColorModel) cm;
+ if (dcm.getRedMask() == DCM_565_RED_MASK) {
+ if (dcm.getGreenMask() == DCM_565_GRN_MASK &&
+ dcm.getBlueMask() == DCM_565_BLU_MASK) {
+ imageType = TYPE_USHORT_565_RGB;
+ }
+ }
+ else if (dcm.getRedMask() == DCM_555_RED_MASK) {
+ if (dcm.getGreenMask() == DCM_555_GRN_MASK &&
+ dcm.getBlueMask() == DCM_555_BLU_MASK) {
+ imageType = TYPE_USHORT_555_RGB;
+ }
+ }
+ } // else if ((cm instanceof IndexColorModel) && (numBands == 1))
+ else if ((raster instanceof ByteComponentRaster)
+ && (cm instanceof ComponentColorModel)
+ && isStandard
+ && (raster.getSampleModel() instanceof PixelInterleavedSampleModel)
+ && (numBands == 3 || numBands == 4))
+ {
+ ComponentColorModel ccm = (ComponentColorModel) cm;
+ PixelInterleavedSampleModel csm =
+ (PixelInterleavedSampleModel)raster.getSampleModel();
+ ByteComponentRaster braster = (ByteComponentRaster) raster;
+ int[] offs = csm.getBandOffsets();
+ if (ccm.getNumComponents() != numBands) {
+ throw new RasterFormatException("Number of components in "+
+ "ColorModel ("+
+ ccm.getNumComponents()+
+ ") does not match # in "+
+ " Raster ("+numBands+")");
+ }
+ int[] nBits = ccm.getComponentSize();
+ boolean is8bit = true;
+ for (int i=0; i < numBands; i++) {
+ if (nBits[i] != 8) {
+ is8bit = false;
+ break;
+ }
+ }
+ if (is8bit &&
+ braster.getPixelStride() == numBands &&
+ offs[0] == numBands-1 &&
+ offs[1] == numBands-2 &&
+ offs[2] == numBands-3)
+ {
+ if (numBands == 3 && !ccm.hasAlpha()) {
+ imageType = TYPE_3BYTE_BGR;
+ }
+ else if (offs[3] == 0 && ccm.hasAlpha()) {
+ imageType = (isAlphaPre
+ ? TYPE_4BYTE_ABGR_PRE
+ : TYPE_4BYTE_ABGR);
+ }
+ }
+ } // else if ((raster instanceof ByteComponentRaster) &&
+ }
+
+ private static boolean isStandard(ColorModel cm, WritableRaster wr) {
+ final Class<? extends ColorModel> cmClass = cm.getClass();
+ final Class<? extends WritableRaster> wrClass = wr.getClass();
+ final Class<? extends SampleModel> smClass = wr.getSampleModel().getClass();
+
+ final PrivilegedAction<Boolean> checkClassLoadersAction =
+ new PrivilegedAction<Boolean>()
+ {
+
+ @Override
+ public Boolean run() {
+ final ClassLoader std = System.class.getClassLoader();
+
+ return (cmClass.getClassLoader() == std) &&
+ (smClass.getClassLoader() == std) &&
+ (wrClass.getClassLoader() == std);
+ }
+ };
+ return AccessController.doPrivileged(checkClassLoadersAction);
+ }
+
+ /**
+ * Returns the image type. If it is not one of the known types,
+ * TYPE_CUSTOM is returned.
+ * @return the image type of this {@code BufferedImage}.
+ * @see #TYPE_INT_RGB
+ * @see #TYPE_INT_ARGB
+ * @see #TYPE_INT_ARGB_PRE
+ * @see #TYPE_INT_BGR
+ * @see #TYPE_3BYTE_BGR
+ * @see #TYPE_4BYTE_ABGR
+ * @see #TYPE_4BYTE_ABGR_PRE
+ * @see #TYPE_BYTE_GRAY
+ * @see #TYPE_BYTE_BINARY
+ * @see #TYPE_BYTE_INDEXED
+ * @see #TYPE_USHORT_GRAY
+ * @see #TYPE_USHORT_565_RGB
+ * @see #TYPE_USHORT_555_RGB
+ * @see #TYPE_CUSTOM
+ */
+ public int getType() {
+ return imageType;
+ }
+
+ /**
+ * Returns the {@code ColorModel}.
+ * @return the {@code ColorModel} of this
+ * {@code BufferedImage}.
+ */
+ public ColorModel getColorModel() {
+ return colorModel;
+ }
+
+ /**
+ * Returns the {@link WritableRaster}.
+ * @return the {@code WritableRaster} of this
+ * {@code BufferedImage}.
+ */
+ public WritableRaster getRaster() {
+ return raster;
+ }
+
+
+ /**
+ * Returns a {@code WritableRaster} representing the alpha
+ * channel for {@code BufferedImage} objects
+ * with {@code ColorModel} objects that support a separate
+ * spatial alpha channel, such as {@code ComponentColorModel} and
+ * {@code DirectColorModel}. Returns {@code null} if there
+ * is no alpha channel associated with the {@code ColorModel} in
+ * this image. This method assumes that for all
+ * {@code ColorModel} objects other than
+ * {@code IndexColorModel}, if the {@code ColorModel}
+ * supports alpha, there is a separate alpha channel
+ * which is stored as the last band of image data.
+ * If the image uses an {@code IndexColorModel} that
+ * has alpha in the lookup table, this method returns
+ * {@code null} since there is no spatially discrete alpha
+ * channel. This method creates a new
+ * {@code WritableRaster}, but shares the data array.
+ * @return a {@code WritableRaster} or {@code null} if this
+ * {@code BufferedImage} has no alpha channel associated
+ * with its {@code ColorModel}.
+ */
+ public WritableRaster getAlphaRaster() {
+ return colorModel.getAlphaRaster(raster);
+ }
+
+ /**
+ * Returns an integer pixel in the default RGB color model
+ * (TYPE_INT_ARGB) and default sRGB colorspace. Color
+ * conversion takes place if this default model does not match
+ * the image {@code ColorModel}. There are only 8-bits of
+ * precision for each color component in the returned data when using
+ * this method.
+ *
+ * <p>
+ *
+ * An {@code ArrayOutOfBoundsException} may be thrown
+ * if the coordinates are not in bounds.
+ * However, explicit bounds checking is not guaranteed.
+ *
+ * @param x the X coordinate of the pixel from which to get
+ * the pixel in the default RGB color model and sRGB
+ * color space
+ * @param y the Y coordinate of the pixel from which to get
+ * the pixel in the default RGB color model and sRGB
+ * color space
+ * @return an integer pixel in the default RGB color model and
+ * default sRGB colorspace.
+ * @see #setRGB(int, int, int)
+ * @see #setRGB(int, int, int, int, int[], int, int)
+ */
+ public int getRGB(int x, int y) {
+ return colorModel.getRGB(raster.getDataElements(x, y, null));
+ }
+
+ /**
+ * Returns an array of integer pixels in the default RGB color model
+ * (TYPE_INT_ARGB) and default sRGB color space,
+ * from a portion of the image data. Color conversion takes
+ * place if the default model does not match the image
+ * {@code ColorModel}. There are only 8-bits of precision for
+ * each color component in the returned data when
+ * using this method. With a specified coordinate (x, y) in the
+ * image, the ARGB pixel can be accessed in this way:
+ *
+ * <pre>
+ * pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre>
+ *
+ * <p>
+ *
+ * An {@code ArrayOutOfBoundsException} may be thrown
+ * if the region is not in bounds.
+ * However, explicit bounds checking is not guaranteed.
+ *
+ * @param startX the starting X coordinate
+ * @param startY the starting Y coordinate
+ * @param w width of region
+ * @param h height of region
+ * @param rgbArray if not {@code null}, the rgb pixels are
+ * written here
+ * @param offset offset into the {@code rgbArray}
+ * @param scansize scanline stride for the {@code rgbArray}
+ * @return array of RGB pixels.
+ * @see #setRGB(int, int, int)
+ * @see #setRGB(int, int, int, int, int[], int, int)
+ */
+ public int[] getRGB(int startX, int startY, int w, int h,
+ int[] rgbArray, int offset, int scansize) {
+ int yoff = offset;
+ int off;
+ Object data;
+ int nbands = raster.getNumBands();
+ int dataType = raster.getDataBuffer().getDataType();
+ switch (dataType) {
+ case DataBuffer.TYPE_BYTE:
+ data = new byte[nbands];
+ break;
+ case DataBuffer.TYPE_USHORT:
+ data = new short[nbands];
+ break;
+ case DataBuffer.TYPE_INT:
+ data = new int[nbands];
+ break;
+ case DataBuffer.TYPE_FLOAT:
+ data = new float[nbands];
+ break;
+ case DataBuffer.TYPE_DOUBLE:
+ data = new double[nbands];
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown data buffer type: "+
+ dataType);
+ }
+
+ if (rgbArray == null) {
+ rgbArray = new int[offset+h*scansize];
+ }
+
+ for (int y = startY; y < startY+h; y++, yoff+=scansize) {
+ off = yoff;
+ for (int x = startX; x < startX+w; x++) {
+ rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x,
+ y,
+ data));
+ }
+ }
+
+ return rgbArray;
+ }
+
+
+ /**
+ * Sets a pixel in this {@code BufferedImage} to the specified
+ * RGB value. The pixel is assumed to be in the default RGB color
+ * model, TYPE_INT_ARGB, and default sRGB color space. For images
+ * with an {@code IndexColorModel}, the index with the nearest
+ * color is chosen.
+ *
+ * <p>
+ *
+ * An {@code ArrayOutOfBoundsException} may be thrown
+ * if the coordinates are not in bounds.
+ * However, explicit bounds checking is not guaranteed.
+ *
+ * @param x the X coordinate of the pixel to set
+ * @param y the Y coordinate of the pixel to set
+ * @param rgb the RGB value
+ * @see #getRGB(int, int)
+ * @see #getRGB(int, int, int, int, int[], int, int)
+ */
+ public void setRGB(int x, int y, int rgb) {
+ raster.setDataElements(x, y, colorModel.getDataElements(rgb, null));
+ }
+
+ /**
+ * Sets an array of integer pixels in the default RGB color model
+ * (TYPE_INT_ARGB) and default sRGB color space,
+ * into a portion of the image data. Color conversion takes place
+ * if the default model does not match the image
+ * {@code ColorModel}. There are only 8-bits of precision for
+ * each color component in the returned data when
+ * using this method. With a specified coordinate (x, y) in the
+ * this image, the ARGB pixel can be accessed in this way:
+ * <pre>
+ * pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)];
+ * </pre>
+ * WARNING: No dithering takes place.
+ *
+ * <p>
+ *
+ * An {@code ArrayOutOfBoundsException} may be thrown
+ * if the region is not in bounds.
+ * However, explicit bounds checking is not guaranteed.
+ *
+ * @param startX the starting X coordinate
+ * @param startY the starting Y coordinate
+ * @param w width of the region
+ * @param h height of the region
+ * @param rgbArray the rgb pixels
+ * @param offset offset into the {@code rgbArray}
+ * @param scansize scanline stride for the {@code rgbArray}
+ * @see #getRGB(int, int)
+ * @see #getRGB(int, int, int, int, int[], int, int)
+ */
+ public void setRGB(int startX, int startY, int w, int h,
+ int[] rgbArray, int offset, int scansize) {
+ int yoff = offset;
+ int off;
+ Object pixel = null;
+
+ for (int y = startY; y < startY+h; y++, yoff+=scansize) {
+ off = yoff;
+ for (int x = startX; x < startX+w; x++) {
+ pixel = colorModel.getDataElements(rgbArray[off++], pixel);
+ raster.setDataElements(x, y, pixel);
+ }
+ }
+ }
+
+
+ /**
+ * Returns the width of the {@code BufferedImage}.
+ * @return the width of this {@code BufferedImage}
+ */
+ public int getWidth() {
+ return raster.getWidth();
+ }
+
+ /**
+ * Returns the height of the {@code BufferedImage}.
+ * @return the height of this {@code BufferedImage}
+ */
+ public int getHeight() {
+ return raster.getHeight();
+ }
+
+ /**
+ * Returns the width of the {@code BufferedImage}.
+ * @param observer ignored
+ * @return the width of this {@code BufferedImage}
+ */
+ public int getWidth(ImageObserver observer) {
+ return raster.getWidth();
+ }
+
+ /**
+ * Returns the height of the {@code BufferedImage}.
+ * @param observer ignored
+ * @return the height of this {@code BufferedImage}
+ */
+ public int getHeight(ImageObserver observer) {
+ return raster.getHeight();
+ }
+
+ /**
+ * Returns the object that produces the pixels for the image.
+ * @return the {@link ImageProducer} that is used to produce the
+ * pixels for this image.
+ * @see ImageProducer
+ */
+ public ImageProducer getSource() {
+ if (osis == null) {
+ if (properties == null) {
+ properties = new Hashtable<>();
+ }
+ osis = new OffScreenImageSource(this, properties);
+ }
+ return osis;
+ }
+
+
+ /**
+ * Returns a property of the image by name. Individual property names
+ * are defined by the various image formats. If a property is not
+ * defined for a particular image, this method returns the
+ * {@code UndefinedProperty} field. If the properties
+ * for this image are not yet known, then this method returns
+ * {@code null} and the {@code ImageObserver} object is
+ * notified later. The property name "comment" should be used to
+ * store an optional comment that can be presented to the user as a
+ * description of the image, its source, or its author.
+ * @param name the property name
+ * @param observer the {@code ImageObserver} that receives
+ * notification regarding image information
+ * @return an {@link Object} that is the property referred to by the
+ * specified {@code name} or {@code null} if the
+ * properties of this image are not yet known.
+ * @throws NullPointerException if the property name is null.
+ * @see ImageObserver
+ * @see java.awt.Image#UndefinedProperty
+ */
+ public Object getProperty(String name, ImageObserver observer) {
+ return getProperty(name);
+ }
+
+ /**
+ * Returns a property of the image by name.
+ * @param name the property name
+ * @return an {@code Object} that is the property referred to by
+ * the specified {@code name}.
+ * @throws NullPointerException if the property name is null.
+ */
+ public Object getProperty(String name) {
+ if (name == null) {
+ throw new NullPointerException("null property name is not allowed");
+ }
+ if (properties == null) {
+ return java.awt.Image.UndefinedProperty;
+ }
+ Object o = properties.get(name);
+ if (o == null) {
+ o = java.awt.Image.UndefinedProperty;
+ }
+ return o;
+ }
+
+ /**
+ * This method returns a {@link Graphics2D}, but is here
+ * for backwards compatibility. {@link #createGraphics() createGraphics} is more
+ * convenient, since it is declared to return a
+ * {@code Graphics2D}.
+ * @return a {@code Graphics2D}, which can be used to draw into
+ * this image.
+ */
+ public java.awt.Graphics getGraphics() {
+ return createGraphics();
+ }
+
+ /**
+ * Creates a {@code Graphics2D}, which can be used to draw into
+ * this {@code BufferedImage}.
+ * @return a {@code Graphics2D}, used for drawing into this
+ * image.
+ */
+ public Graphics2D createGraphics() {
+ GraphicsEnvironment env =
+ GraphicsEnvironment.getLocalGraphicsEnvironment();
+ return env.createGraphics(this);
+ }
+
+ /**
+ * Returns a subimage defined by a specified rectangular region.
+ * The returned {@code BufferedImage} shares the same
+ * data array as the original image.
+ * @param x the X coordinate of the upper-left corner of the
+ * specified rectangular region
+ * @param y the Y coordinate of the upper-left corner of the
+ * specified rectangular region
+ * @param w the width of the specified rectangular region
+ * @param h the height of the specified rectangular region
+ * @return a {@code BufferedImage} that is the subimage of this
+ * {@code BufferedImage}.
+ * @exception RasterFormatException if the specified
+ * area is not contained within this {@code BufferedImage}.
+ */
+ public BufferedImage getSubimage (int x, int y, int w, int h) {
+ return new BufferedImage (colorModel,
+ raster.createWritableChild(x, y, w, h,
+ 0, 0, null),
+ colorModel.isAlphaPremultiplied(),
+ properties);
+ }
+
+ /**
+ * Returns whether or not the alpha has been premultiplied. It
+ * returns {@code false} if there is no alpha.
+ * @return {@code true} if the alpha has been premultiplied;
+ * {@code false} otherwise.
+ */
+ public boolean isAlphaPremultiplied() {
+ return colorModel.isAlphaPremultiplied();
+ }
+
+ /**
+ * Forces the data to match the state specified in the
+ * {@code isAlphaPremultiplied} variable. It may multiply or
+ * divide the color raster data by alpha, or do nothing if the data is
+ * in the correct state.
+ * @param isAlphaPremultiplied {@code true} if the alpha has been
+ * premultiplied; {@code false} otherwise.
+ */
+ public void coerceData (boolean isAlphaPremultiplied) {
+ if (colorModel.hasAlpha() &&
+ colorModel.isAlphaPremultiplied() != isAlphaPremultiplied) {
+ // Make the color model do the conversion
+ colorModel = colorModel.coerceData (raster, isAlphaPremultiplied);
+ }
+ }
+
+ /**
+ * Returns a {@code String} representation of this
+ * {@code BufferedImage} object and its values.
+ * @return a {@code String} representing this
+ * {@code BufferedImage}.
+ */
+ public String toString() {
+ return "BufferedImage@"+Integer.toHexString(hashCode())
+ +": type = "+imageType
+ +" "+colorModel+" "+raster;
+ }
+
+ /**
+ * Returns a {@link Vector} of {@link RenderedImage} objects that are
+ * the immediate sources, not the sources of these immediate sources,
+ * of image data for this {@code BufferedImage}. This
+ * method returns {@code null} if the {@code BufferedImage}
+ * has no information about its immediate sources. It returns an
+ * empty {@code Vector} if the {@code BufferedImage} has no
+ * immediate sources.
+ * @return a {@code Vector} containing immediate sources of
+ * this {@code BufferedImage} object's image date, or
+ * {@code null} if this {@code BufferedImage} has
+ * no information about its immediate sources, or an empty
+ * {@code Vector} if this {@code BufferedImage}
+ * has no immediate sources.
+ */
+ public Vector<RenderedImage> getSources() {
+ return null;
+ }
+
+ /**
+ * Returns an array of names recognized by
+ * {@link #getProperty(String) getProperty(String)}
+ * or {@code null}, if no property names are recognized.
+ * @return a {@code String} array containing all of the property
+ * names that {@code getProperty(String)} recognizes;
+ * or {@code null} if no property names are recognized.
+ */
+ public String[] getPropertyNames() {
+ if (properties == null || properties.isEmpty()) {
+ return null;
+ }
+ final Set<String> keys = properties.keySet();
+ return keys.toArray(new String[keys.size()]);
+ }
+
+ /**
+ * Returns the minimum x coordinate of this
+ * {@code BufferedImage}. This is always zero.
+ * @return the minimum x coordinate of this
+ * {@code BufferedImage}.
+ */
+ public int getMinX() {
+ return raster.getMinX();
+ }
+
+ /**
+ * Returns the minimum y coordinate of this
+ * {@code BufferedImage}. This is always zero.
+ * @return the minimum y coordinate of this
+ * {@code BufferedImage}.
+ */
+ public int getMinY() {
+ return raster.getMinY();
+ }
+
+ /**
+ * Returns the {@code SampleModel} associated with this
+ * {@code BufferedImage}.
+ * @return the {@code SampleModel} of this
+ * {@code BufferedImage}.
+ */
+ public SampleModel getSampleModel() {
+ return raster.getSampleModel();
+ }
+
+ /**
+ * Returns the number of tiles in the x direction.
+ * This is always one.
+ * @return the number of tiles in the x direction.
+ */
+ public int getNumXTiles() {
+ return 1;
+ }
+
+ /**
+ * Returns the number of tiles in the y direction.
+ * This is always one.
+ * @return the number of tiles in the y direction.
+ */
+ public int getNumYTiles() {
+ return 1;
+ }
+
+ /**
+ * Returns the minimum tile index in the x direction.
+ * This is always zero.
+ * @return the minimum tile index in the x direction.
+ */
+ public int getMinTileX() {
+ return 0;
+ }
+
+ /**
+ * Returns the minimum tile index in the y direction.
+ * This is always zero.
+ * @return the minimum tile index in the y direction.
+ */
+ public int getMinTileY() {
+ return 0;
+ }
+
+ /**
+ * Returns the tile width in pixels.
+ * @return the tile width in pixels.
+ */
+ public int getTileWidth() {
+ return raster.getWidth();
+ }
+
+ /**
+ * Returns the tile height in pixels.
+ * @return the tile height in pixels.
+ */
+ public int getTileHeight() {
+ return raster.getHeight();
+ }
+
+ /**
+ * Returns the x offset of the tile grid relative to the origin,
+ * For example, the x coordinate of the location of tile
+ * (0, 0). This is always zero.
+ * @return the x offset of the tile grid.
+ */
+ public int getTileGridXOffset() {
+ return raster.getSampleModelTranslateX();
+ }
+
+ /**
+ * Returns the y offset of the tile grid relative to the origin,
+ * For example, the y coordinate of the location of tile
+ * (0, 0). This is always zero.
+ * @return the y offset of the tile grid.
+ */
+ public int getTileGridYOffset() {
+ return raster.getSampleModelTranslateY();
+ }
+
+ /**
+ * Returns tile ({@code tileX}, {@code tileY}). Note
+ * that {@code tileX} and {@code tileY} are indices
+ * into the tile array, not pixel locations. The {@code Raster}
+ * that is returned is live, which means that it is updated if the
+ * image is changed.
+ * @param tileX the x index of the requested tile in the tile array
+ * @param tileY the y index of the requested tile in the tile array
+ * @return a {@code Raster} that is the tile defined by the
+ * arguments {@code tileX} and {@code tileY}.
+ * @exception ArrayIndexOutOfBoundsException if both
+ * {@code tileX} and {@code tileY} are not
+ * equal to 0
+ */
+ public Raster getTile(int tileX, int tileY) {
+ if (tileX == 0 && tileY == 0) {
+ return raster;
+ }
+ throw new ArrayIndexOutOfBoundsException("BufferedImages only have"+
+ " one tile with index 0,0");
+ }
+
+ /**
+ * Returns the image as one large tile. The {@code Raster}
+ * returned is a copy of the image data is not updated if the
+ * image is changed.
+ * @return a {@code Raster} that is a copy of the image data.
+ * @see #setData(Raster)
+ */
+ public Raster getData() {
+
+ // REMIND : this allocates a whole new tile if raster is a
+ // subtile. (It only copies in the requested area)
+ // We should do something smarter.
+ int width = raster.getWidth();
+ int height = raster.getHeight();
+ int startX = raster.getMinX();
+ int startY = raster.getMinY();
+ WritableRaster wr =
+ Raster.createWritableRaster(raster.getSampleModel(),
+ new Point(raster.getSampleModelTranslateX(),
+ raster.getSampleModelTranslateY()));
+
+ Object tdata = null;
+
+ for (int i = startY; i < startY+height; i++) {
+ tdata = raster.getDataElements(startX,i,width,1,tdata);
+ wr.setDataElements(startX,i,width,1, tdata);
+ }
+ return wr;
+ }
+
+ /**
+ * Computes and returns an arbitrary region of the
+ * {@code BufferedImage}. The {@code Raster} returned is a
+ * copy of the image data and is not updated if the image is
+ * changed.
+ * @param rect the region of the {@code BufferedImage} to be
+ * returned.
+ * @return a {@code Raster} that is a copy of the image data of
+ * the specified region of the {@code BufferedImage}
+ * @see #setData(Raster)
+ */
+ public Raster getData(Rectangle rect) {
+ SampleModel sm = raster.getSampleModel();
+ SampleModel nsm = sm.createCompatibleSampleModel(rect.width,
+ rect.height);
+ WritableRaster wr = Raster.createWritableRaster(nsm,
+ rect.getLocation());
+ int width = rect.width;
+ int height = rect.height;
+ int startX = rect.x;
+ int startY = rect.y;
+
+ Object tdata = null;
+
+ for (int i = startY; i < startY+height; i++) {
+ tdata = raster.getDataElements(startX,i,width,1,tdata);
+ wr.setDataElements(startX,i,width,1, tdata);
+ }
+ return wr;
+ }
+
+ /**
+ * Computes an arbitrary rectangular region of the
+ * {@code BufferedImage} and copies it into a specified
+ * {@code WritableRaster}. The region to be computed is
+ * determined from the bounds of the specified
+ * {@code WritableRaster}. The specified
+ * {@code WritableRaster} must have a
+ * {@code SampleModel} that is compatible with this image. If
+ * {@code outRaster} is {@code null},
+ * an appropriate {@code WritableRaster} is created.
+ * @param outRaster a {@code WritableRaster} to hold the returned
+ * part of the image, or {@code null}
+ * @return a reference to the supplied or created
+ * {@code WritableRaster}.
+ */
+ public WritableRaster copyData(WritableRaster outRaster) {
+ if (outRaster == null) {
+ return (WritableRaster) getData();
+ }
+ int width = outRaster.getWidth();
+ int height = outRaster.getHeight();
+ int startX = outRaster.getMinX();
+ int startY = outRaster.getMinY();
+
+ Object tdata = null;
+
+ for (int i = startY; i < startY+height; i++) {
+ tdata = raster.getDataElements(startX,i,width,1,tdata);
+ outRaster.setDataElements(startX,i,width,1, tdata);
+ }
+
+ return outRaster;
+ }
+
+ /**
+ * Sets a rectangular region of the image to the contents of the
+ * specified {@code Raster r}, which is
+ * assumed to be in the same coordinate space as the
+ * {@code BufferedImage}. The operation is clipped to the bounds
+ * of the {@code BufferedImage}.
+ * @param r the specified {@code Raster}
+ * @see #getData
+ * @see #getData(Rectangle)
+ */
+ public void setData(Raster r) {
+ int width = r.getWidth();
+ int height = r.getHeight();
+ int startX = r.getMinX();
+ int startY = r.getMinY();
+
+ int[] tdata = null;
+
+ // Clip to the current Raster
+ Rectangle rclip = new Rectangle(startX, startY, width, height);
+ Rectangle bclip = new Rectangle(0, 0, raster.width, raster.height);
+ Rectangle intersect = rclip.intersection(bclip);
+ if (intersect.isEmpty()) {
+ return;
+ }
+ width = intersect.width;
+ height = intersect.height;
+ startX = intersect.x;
+ startY = intersect.y;
+
+ // remind use get/setDataElements for speed if Rasters are
+ // compatible
+ for (int i = startY; i < startY+height; i++) {
+ tdata = r.getPixels(startX,i,width,1,tdata);
+ raster.setPixels(startX,i,width,1, tdata);
+ }
+ }
+
+
+ /**
+ * Adds a tile observer. If the observer is already present,
+ * it receives multiple notifications.
+ * @param to the specified {@link TileObserver}
+ */
+ public void addTileObserver (TileObserver to) {
+ }
+
+ /**
+ * Removes a tile observer. If the observer was not registered,
+ * nothing happens. If the observer was registered for multiple
+ * notifications, it is now registered for one fewer notification.
+ * @param to the specified {@code TileObserver}.
+ */
+ public void removeTileObserver (TileObserver to) {
+ }
+
+ /**
+ * Returns whether or not a tile is currently checked out for writing.
+ * @param tileX the x index of the tile.
+ * @param tileY the y index of the tile.
+ * @return {@code true} if the tile specified by the specified
+ * indices is checked out for writing; {@code false}
+ * otherwise.
+ * @exception ArrayIndexOutOfBoundsException if both
+ * {@code tileX} and {@code tileY} are not equal
+ * to 0
+ */
+ public boolean isTileWritable (int tileX, int tileY) {
+ if (tileX == 0 && tileY == 0) {
+ return true;
+ }
+ throw new IllegalArgumentException("Only 1 tile in image");
+ }
+
+ /**
+ * Returns an array of {@link Point} objects indicating which tiles
+ * are checked out for writing. Returns {@code null} if none are
+ * checked out.
+ * @return a {@code Point} array that indicates the tiles that
+ * are checked out for writing, or {@code null} if no
+ * tiles are checked out for writing.
+ */
+ public Point[] getWritableTileIndices() {
+ Point[] p = new Point[1];
+ p[0] = new Point(0, 0);
+
+ return p;
+ }
+
+ /**
+ * Returns whether or not any tile is checked out for writing.
+ * Semantically equivalent to
+ * <pre>
+ * (getWritableTileIndices() != null).
+ * </pre>
+ * @return {@code true} if any tile is checked out for writing;
+ * {@code false} otherwise.
+ */
+ public boolean hasTileWriters () {
+ return true;
+ }
+
+ /**
+ * Checks out a tile for writing. All registered
+ * {@code TileObservers} are notified when a tile goes from having
+ * no writers to having one writer.
+ * @param tileX the x index of the tile
+ * @param tileY the y index of the tile
+ * @return a {@code WritableRaster} that is the tile, indicated by
+ * the specified indices, to be checked out for writing.
+ */
+ public WritableRaster getWritableTile (int tileX, int tileY) {
+ return raster;
+ }
+
+ /**
+ * Relinquishes permission to write to a tile. If the caller
+ * continues to write to the tile, the results are undefined.
+ * Calls to this method should only appear in matching pairs
+ * with calls to {@link #getWritableTile(int, int) getWritableTile(int, int)}. Any other leads
+ * to undefined results. All registered {@code TileObservers}
+ * are notified when a tile goes from having one writer to having no
+ * writers.
+ * @param tileX the x index of the tile
+ * @param tileY the y index of the tile
+ */
+ public void releaseWritableTile (int tileX, int tileY) {
+ }
+
+ /**
+ * Returns the transparency. Returns either OPAQUE, BITMASK,
+ * or TRANSLUCENT.
+ * @return the transparency of this {@code BufferedImage}.
+ * @see Transparency#OPAQUE
+ * @see Transparency#BITMASK
+ * @see Transparency#TRANSLUCENT
+ * @since 1.5
+ */
+ public int getTransparency() {
+ return colorModel.getTransparency();
+ }
+}