| /* |
| * Copyright (c) 1997, 2008, 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.Transparency; |
| import java.awt.color.ColorSpace; |
| import java.awt.Graphics2D; |
| import java.awt.GraphicsConfiguration; |
| import java.awt.GraphicsEnvironment; |
| import java.awt.ImageCapabilities; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.geom.Point2D; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.Hashtable; |
| import java.util.Vector; |
| |
| import sun.awt.image.BytePackedRaster; |
| import sun.awt.image.ShortComponentRaster; |
| import sun.awt.image.ByteComponentRaster; |
| import sun.awt.image.IntegerComponentRaster; |
| import sun.awt.image.OffScreenImageSource; |
| |
| /** |
| * |
| * The <code>BufferedImage</code> subclass describes an {@link |
| * java.awt.Image Image} with an accessible buffer of image data. |
| * A <code>BufferedImage</code> 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</code> must match the number and types required by the |
| * <code>ColorModel</code> to represent its color and alpha components. |
| * All <code>BufferedImage</code> objects have an upper left corner |
| * coordinate of (0, 0). Any <code>Raster</code> used to construct a |
| * <code>BufferedImage</code> must therefore have minX=0 and minY=0. |
| * |
| * <p> |
| * This class relies on the data fetching and setting methods |
| * of <code>Raster</code>, |
| * and on the color characterization methods of <code>ColorModel</code>. |
| * |
| * @see ColorModel |
| * @see Raster |
| * @see WritableRaster |
| */ |
| |
| public class BufferedImage extends java.awt.Image |
| implements WritableRenderedImage, Transparency |
| { |
| int imageType = TYPE_CUSTOM; |
| ColorModel colorModel; |
| WritableRaster raster; |
| OffScreenImageSource osis; |
| Hashtable properties; |
| |
| boolean isAlphaPremultiplied;// If true, alpha has been premultiplied in |
| // color channels |
| |
| /** |
| * 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</code> |
| * 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</code> argument to a <code>BufferedImage</code> |
| * 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</code> |
| * 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</code>. |
| * 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</code> 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</code> 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</code>. |
| * 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</code>. |
| * 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</code> 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</code> with a CS_GRAY |
| * <code>ColorSpace</code>. |
| * 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</code> argument to the |
| * <code>BufferedImage</code> constructor that takes an |
| * <code>imageType</code> argument but no <code>ColorModel</code> |
| * argument, a 1-bit image is created with an |
| * <code>IndexColorModel</code> with two colors in the default |
| * sRGB <code>ColorSpace</code>: {0, 0, 0} and |
| * {255, 255, 255}. |
| * |
| * <p> Images with 2 or 4 bits per pixel may be constructed via |
| * the <code>BufferedImage</code> constructor that takes a |
| * <code>ColorModel</code> argument by supplying a |
| * <code>ColorModel</code> with an appropriate map size. |
| * |
| * <p> Images with 8 bits per pixel should use the image types |
| * <code>TYPE_BYTE_INDEXED</code> or <code>TYPE_BYTE_GRAY</code> |
| * depending on their <code>ColorModel</code>. |
| |
| * <p> When color data is stored in an image of this type, |
| * the closest color in the colormap is determined |
| * by the <code>IndexColorModel</code> and the resulting index is stored. |
| * Approximation and loss of alpha or color components |
| * can result, depending on the colors in the |
| * <code>IndexColorModel</code> colormap. |
| */ |
| public static final int TYPE_BYTE_BINARY = 12; |
| |
| /** |
| * Represents an indexed byte image. When this type is used as the |
| * <code>imageType</code> argument to the <code>BufferedImage</code> |
| * constructor that takes an <code>imageType</code> argument |
| * but no <code>ColorModel</code> argument, an |
| * <code>IndexColorModel</code> 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</code> and the resulting index is stored. |
| * Approximation and loss of alpha or color components |
| * can result, depending on the colors in the |
| * <code>IndexColorModel</code> 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; |
| |
| |
| static private native void initIDs(); |
| static { |
| ColorModel.loadLibraries(); |
| initIDs(); |
| } |
| |
| /** |
| * Constructs a <code>BufferedImage</code> of one of the predefined |
| * image types. The <code>ColorSpace</code> 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</code> 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</code> 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</code> with a specified |
| * <code>ColorModel</code> and <code>Raster</code>. If the number and |
| * types of bands in the <code>SampleModel</code> of the |
| * <code>Raster</code> do not match the number and types required by |
| * the <code>ColorModel</code> to represent its color and alpha |
| * components, a {@link RasterFormatException} is thrown. This |
| * method can multiply or divide the color <code>Raster</code> data by |
| * alpha to match the <code>alphaPremultiplied</code> state |
| * in the <code>ColorModel</code>. Properties for this |
| * <code>BufferedImage</code> can be established by passing |
| * in a {@link Hashtable} of <code>String</code>/<code>Object</code> |
| * pairs. |
| * @param cm <code>ColorModel</code> for the new image |
| * @param raster <code>Raster</code> for the image data |
| * @param isRasterPremultiplied if <code>true</code>, the data in |
| * the raster has been premultiplied with alpha. |
| * @param properties <code>Hashtable</code> of |
| * <code>String</code>/<code>Object</code> pairs. |
| * @exception <code>RasterFormatException</code> if the number and |
| * types of bands in the <code>SampleModel</code> of the |
| * <code>Raster</code> do not match the number and types required by |
| * the <code>ColorModel</code> to represent its color and alpha |
| * components. |
| * @exception <code>IllegalArgumentException</code> if |
| * <code>raster</code> is incompatible with <code>cm</code> |
| * @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; |
| this.properties = properties; |
| 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</code>. |
| * @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</code>. |
| * @return the <code>ColorModel</code> of this |
| * <code>BufferedImage</code>. |
| */ |
| public ColorModel getColorModel() { |
| return colorModel; |
| } |
| |
| /** |
| * Returns the {@link WritableRaster}. |
| * @return the <code>WriteableRaster</code> of this |
| * <code>BufferedImage</code>. |
| */ |
| public WritableRaster getRaster() { |
| return raster; |
| } |
| |
| |
| /** |
| * Returns a <code>WritableRaster</code> representing the alpha |
| * channel for <code>BufferedImage</code> objects |
| * with <code>ColorModel</code> objects that support a separate |
| * spatial alpha channel, such as <code>ComponentColorModel</code> and |
| * <code>DirectColorModel</code>. Returns <code>null</code> if there |
| * is no alpha channel associated with the <code>ColorModel</code> in |
| * this image. This method assumes that for all |
| * <code>ColorModel</code> objects other than |
| * <code>IndexColorModel</code>, if the <code>ColorModel</code> |
| * 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</code> that |
| * has alpha in the lookup table, this method returns |
| * <code>null</code> since there is no spatially discrete alpha |
| * channel. This method creates a new |
| * <code>WritableRaster</code>, but shares the data array. |
| * @return a <code>WritableRaster</code> or <code>null</code> if this |
| * <code>BufferedImage</code> has no alpha channel associated |
| * with its <code>ColorModel</code>. |
| */ |
| 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</code>. There are only 8-bits of |
| * precision for each color component in the returned data when using |
| * this method. |
| * |
| * <p> |
| * |
| * An <code>ArrayOutOfBoundsException</code> 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</code>. 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: |
| * </p> |
| * |
| * <pre> |
| * pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre> |
| * |
| * <p> |
| * |
| * An <code>ArrayOutOfBoundsException</code> 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</code>, the rgb pixels are |
| * written here |
| * @param offset offset into the <code>rgbArray</code> |
| * @param scansize scanline stride for the <code>rgbArray</code> |
| * @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</code> 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</code>, the index with the nearest |
| * color is chosen. |
| * |
| * <p> |
| * |
| * An <code>ArrayOutOfBoundsException</code> 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 synchronized 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</code>. 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</code> 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</code> |
| * @param scansize scanline stride for the <code>rgbArray</code> |
| * @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</code>. |
| * @return the width of this <code>BufferedImage</code> |
| */ |
| public int getWidth() { |
| return raster.getWidth(); |
| } |
| |
| /** |
| * Returns the height of the <code>BufferedImage</code>. |
| * @return the height of this <code>BufferedImage</code> |
| */ |
| public int getHeight() { |
| return raster.getHeight(); |
| } |
| |
| /** |
| * Returns the width of the <code>BufferedImage</code>. |
| * @param observer ignored |
| * @return the width of this <code>BufferedImage</code> |
| */ |
| public int getWidth(ImageObserver observer) { |
| return raster.getWidth(); |
| } |
| |
| /** |
| * Returns the height of the <code>BufferedImage</code>. |
| * @param observer ignored |
| * @return the height of this <code>BufferedImage</code> |
| */ |
| 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</code> field. If the properties |
| * for this image are not yet known, then this method returns |
| * <code>null</code> and the <code>ImageObserver</code> 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</code> that receives |
| * notification regarding image information |
| * @return an {@link Object} that is the property referred to by the |
| * specified <code>name</code> or <code>null</code> if the |
| * properties of this image are not yet known. |
| * @throws <code>NullPointerException</code> 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</code> that is the property referred to by |
| * the specified <code>name</code>. |
| * @throws <code>NullPointerException</code> 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</code>. |
| * @return a <code>Graphics2D</code>, which can be used to draw into |
| * this image. |
| */ |
| public java.awt.Graphics getGraphics() { |
| return createGraphics(); |
| } |
| |
| /** |
| * Creates a <code>Graphics2D</code>, which can be used to draw into |
| * this <code>BufferedImage</code>. |
| * @return a <code>Graphics2D</code>, 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</code> 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</code> that is the subimage of this |
| * <code>BufferedImage</code>. |
| * @exception <code>RasterFormatException</code> if the specified |
| * area is not contained within this <code>BufferedImage</code>. |
| */ |
| 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</code> if there is no alpha. |
| * @return <code>true</code> if the alpha has been premultiplied; |
| * <code>false</code> otherwise. |
| */ |
| public boolean isAlphaPremultiplied() { |
| return colorModel.isAlphaPremultiplied(); |
| } |
| |
| /** |
| * Forces the data to match the state specified in the |
| * <code>isAlphaPremultiplied</code> 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</code> if the alpha has been |
| * premultiplied; <code>false</code> 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</code> representation of this |
| * <code>BufferedImage</code> object and its values. |
| * @return a <code>String</code> representing this |
| * <code>BufferedImage</code>. |
| */ |
| 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</code>. This |
| * method returns <code>null</code> if the <code>BufferedImage</code> |
| * has no information about its immediate sources. It returns an |
| * empty <code>Vector</code> if the <code>BufferedImage</code> has no |
| * immediate sources. |
| * @return a <code>Vector</code> containing immediate sources of |
| * this <code>BufferedImage</code> object's image date, or |
| * <code>null</code> if this <code>BufferedImage</code> has |
| * no information about its immediate sources, or an empty |
| * <code>Vector</code> if this <code>BufferedImage</code> |
| * 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</code>, if no property names are recognized. |
| * @return a <code>String</code> array containing all of the property |
| * names that <code>getProperty(String)</code> recognizes; |
| * or <code>null</code> if no property names are recognized. |
| */ |
| public String[] getPropertyNames() { |
| return null; |
| } |
| |
| /** |
| * Returns the minimum x coordinate of this |
| * <code>BufferedImage</code>. This is always zero. |
| * @return the minimum x coordinate of this |
| * <code>BufferedImage</code>. |
| */ |
| public int getMinX() { |
| return raster.getMinX(); |
| } |
| |
| /** |
| * Returns the minimum y coordinate of this |
| * <code>BufferedImage</code>. This is always zero. |
| * @return the minimum y coordinate of this |
| * <code>BufferedImage</code>. |
| */ |
| public int getMinY() { |
| return raster.getMinY(); |
| } |
| |
| /** |
| * Returns the <code>SampleModel</code> associated with this |
| * <code>BufferedImage</code>. |
| * @return the <code>SampleModel</code> of this |
| * <code>BufferedImage</code>. |
| */ |
| 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 mininum 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>, <code>tileY</code>). Note |
| * that <code>tileX</code> and <code>tileY</code> are indices |
| * into the tile array, not pixel locations. The <code>Raster</code> |
| * 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</code> that is the tile defined by the |
| * arguments <code>tileX</code> and <code>tileY</code>. |
| * @exception <code>ArrayIndexOutOfBoundsException</code> if both |
| * <code>tileX</code> and <code>tileY</code> 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</code> |
| * returned is a copy of the image data is not updated if the |
| * image is changed. |
| * @return a <code>Raster</code> 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</code>. The <code>Raster</code> 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</code> to be |
| * returned. |
| * @return a <code>Raster</code> that is a copy of the image data of |
| * the specified region of the <code>BufferedImage</code> |
| * @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</code> and copies it into a specified |
| * <code>WritableRaster</code>. The region to be computed is |
| * determined from the bounds of the specified |
| * <code>WritableRaster</code>. The specified |
| * <code>WritableRaster</code> must have a |
| * <code>SampleModel</code> that is compatible with this image. If |
| * <code>outRaster</code> is <code>null</code>, |
| * an appropriate <code>WritableRaster</code> is created. |
| * @param outRaster a <code>WritableRaster</code> to hold the returned |
| * part of the image, or <code>null</code> |
| * @return a reference to the supplied or created |
| * <code>WritableRaster</code>. |
| */ |
| 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</code> <code>r</code>, which is |
| * assumed to be in the same coordinate space as the |
| * <code>BufferedImage</code>. The operation is clipped to the bounds |
| * of the <code>BufferedImage</code>. |
| * @param r the specified <code>Raster</code> |
| * @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</code>. |
| */ |
| 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</code> if the tile specified by the specified |
| * indices is checked out for writing; <code>false</code> |
| * otherwise. |
| * @exception <code>ArrayIndexOutOfBoundsException</code> if both |
| * <code>tileX</code> and <code>tileY</code> 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</code> if none are |
| * checked out. |
| * @return a <code>Point</code> array that indicates the tiles that |
| * are checked out for writing, or <code>null</code> 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</code> if any tile is checked out for writing; |
| * <code>false</code> otherwise. |
| */ |
| public boolean hasTileWriters () { |
| return true; |
| } |
| |
| /** |
| * Checks out a tile for writing. All registered |
| * <code>TileObservers</code> 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</code> 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</code> |
| * 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</code>. |
| * @see Transparency#OPAQUE |
| * @see Transparency#BITMASK |
| * @see Transparency#TRANSLUCENT |
| * @since 1.5 |
| */ |
| public int getTransparency() { |
| return colorModel.getTransparency(); |
| } |
| } |