| /* | |
| * Copyright (c) 1997, 2024, 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.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. | |
| * @throws 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. | |
| * @throws 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 ClassLoader std = System.class.getClassLoader(); | |
| return (cmClass.getClassLoader() == std) && | |
| (smClass.getClassLoader() == std) && | |
| (wrClass.getClassLoader() == std); | |
| } | |
| /** | |
| * 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 ArrayIndexOutOfBoundsException} 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 ArrayIndexOutOfBoundsException} 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 ArrayIndexOutOfBoundsException} 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 ArrayIndexOutOfBoundsException} 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}. | |
| * @throws 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.getMinX(); | |
| } | |
| /** | |
| * 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.getMinY(); | |
| } | |
| /** | |
| * 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}. | |
| * @throws 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) { | |
| raster.setRect(r); | |
| } | |
| /** | |
| * Adds a tile observer. If the observer is already present, | |
| * it receives multiple notifications. | |
| * <p> | |
| * This method ignores its parameters and does nothing, | |
| * since {@code BufferedImage} is always checked out | |
| * for writing and cannot be made read-only, | |
| * so there can never be events to dispatch. | |
| * | |
| * @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. | |
| * <p> | |
| * This method ignores the given observer, | |
| * since {@link #addTileObserver(TileObserver)} adds none. | |
| * | |
| * @param to the specified {@code TileObserver}. | |
| */ | |
| public void removeTileObserver (TileObserver to) { | |
| } | |
| /** | |
| * Returns whether or not a tile is currently checked out for writing. | |
| * The only tile in a {@code BufferedImage} is at (0,0) and it is always | |
| * writable, so calling this method with (0,0) will always return | |
| * {@code true}, and any other coordinate will cause an exception | |
| * to be thrown. | |
| * | |
| * @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. | |
| * @throws IllegalArgumentException if either | |
| * {@code tileX} or {@code tileY} is 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. | |
| * <p> | |
| * Since a {@code BufferedImage} consists of a single tile, | |
| * and that tile is always checked out for writing, | |
| * this method returns an array of one point. | |
| * Further, the offset shall be consistent with | |
| * {@link #getMinTileX()} and {@link #getMinTileY()}, | |
| * which are always (0,0) in {@code BufferedImage}. | |
| * That will always be the coordinates of the single | |
| * returned {@code Point}. | |
| * | |
| * @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(); // Default to (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. | |
| * <p> | |
| * This method unconditionally returns the | |
| * {@linkplain #getRaster() single tile} without checking | |
| * the passed values. No listeners are notified since the | |
| * returned tile is always checked out for writing. | |
| * | |
| * @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. | |
| * <p> | |
| * This method immediately returns without checking the passed values. | |
| * No listeners are notified since the {@linkplain #getRaster() single tile} | |
| * is always checked out for writing. | |
| * | |
| * @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(); | |
| } | |
| } |