/*
 
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
 
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
*
 
* This code is free software; you can redistribute it and/or modify it
 
* under the terms of the GNU General Public License version 2 only, as
 
* published by the Free Software Foundation.
  
Oracle designates this
 
* particular file as subject to the "Classpath" exception as provided
 
* by Oracle in the LICENSE file that accompanied this code.
 
*
 
* This code is distributed in the hope that it will be useful, but WITHOUT
 
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
* FITNESS FOR A PARTICULAR PURPOSE.
  
See the GNU General Public License
 
* version 2 for more details (a copy is included in the LICENSE file that
 
* accompanied this code).
 
*
 
* You should have received a copy of the GNU General Public License version
 
* 2 along with this work; if not, write to the Free Software Foundation,
 
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
*
 
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 
* or visit www.oracle.com if you need additional information or have any
 
* questions.
 
*/

package java.awt.image;

import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;

import sun.awt.image.ByteComponentRaster;
import sun.awt.image.BytePackedRaster;
import sun.awt.image.IntegerComponentRaster;
import sun.awt.image.OffScreenImageSource;
import sun.awt.image.ShortComponentRaster;

/**
 
*
 
* The <code>BufferedImage</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,&nbsp;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
{
    
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
     
*
 
 
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
     
*
 
 
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
     
*
 
 
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
     
*
 
 
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
     
*
 
 
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
     
*
 
 
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
     
*
 
 
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,&nbsp;0,&nbsp;0} and
     
* {255,&nbsp;255,&nbsp;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 widthwidth of the created image
     
* @param height
    
height of the created image
     
* @param imageType type of the created image
     
* @see ColorSpace
     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*/

    
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 widthwidth 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.
     
*
 

     
*
 

     
*/

    
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 RasterFormatException 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 IllegalArgumentException 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;
        
if (properties != null && !properties.isEmpty()) {
            
this.properties = new Hashtable<>();
            
for (final Object key : properties.keySet()) {
                
if (key instanceof String) {
                    
this.properties.put((String) key, properties.get(key));
                
}
            
}
        
}
        
int numBands = raster.getNumBands();
        
boolean isAlphaPre = cm.isAlphaPremultiplied();
        
final boolean isStandard = isStandard(cm, raster);
        
ColorSpace cs;

        
// Force the raster data alpha state to match the premultiplied
        
// state in the color model
        
coerceData(isRasterPremultiplied);

        
SampleModel sm = raster.getSampleModel();
        
cs = cm.getColorSpace();
        
int csType = cs.getType();
        
if (csType != ColorSpace.TYPE_RGB) {
            
if (csType == ColorSpace.TYPE_GRAY &&
                
isStandard &&
                
cm instanceof ComponentColorModel) {
                
// Check if this might be a child raster (fix for bug 4240596)
                
if (sm instanceof ComponentSampleModel &&
                    
((ComponentSampleModel)sm).getPixelStride() != numBands) {
                    
imageType = TYPE_CUSTOM;
                
} else if (raster instanceof ByteComponentRaster &&
                       
raster.getNumBands() == 1 &&
                       
cm.getComponentSize(0) == 8 &&
                       
((ByteComponentRaster)raster).getPixelStride() == 1) {
                    
imageType = TYPE_BYTE_GRAY;
                
} else if (raster instanceof ShortComponentRaster &&
                       
raster.getNumBands() == 1 &&
                       
cm.getComponentSize(0) == 16 &&
                       
((ShortComponentRaster)raster).getPixelStride() == 1) {
                    
imageType = TYPE_USHORT_GRAY;
                
}
            
} else {
                
imageType = TYPE_CUSTOM;
            
}
            
return;
        
}

        
if ((raster instanceof IntegerComponentRaster) &&
            
(numBands == 3 || numBands == 4)) {
            
IntegerComponentRaster iraster =
                
(IntegerComponentRaster) raster;
            
// Check if the raster params and the color model
            
// are correct
            
int pixSize = cm.getPixelSize();
            
if (iraster.getPixelStride() == 1 &&
                
isStandard &&
                
cm instanceof DirectColorModel
  
&&
                
(pixSize == 32 || pixSize == 24))
            
{
                
// Now check on the DirectColorModel params
                
DirectColorModel dcm = (DirectColorModel) cm;
                
int rmask = dcm.getRedMask();
                
int gmask = dcm.getGreenMask();
                
int bmask = dcm.getBlueMask();
                
if (rmask == DCM_RED_MASK && gmask == DCM_GREEN_MASK &&
                    
bmask == DCM_BLUE_MASK)
                
{
                    
if (dcm.getAlphaMask() == DCM_ALPHA_MASK) {
                        
imageType = (isAlphaPre
                                     
? TYPE_INT_ARGB_PRE
                                     
: TYPE_INT_ARGB);
                    
}
                    
else {
                        
// No Alpha
                        
if (!dcm.hasAlpha()) {
                            
imageType = TYPE_INT_RGB;
                        
}
                    
}
                
}
   
// if (dcm.getRedMask() == DCM_RED_MASK &&
                
else if (rmask == DCM_BGR_RED_MASK && gmask == DCM_BGR_GRN_MASK
                         
&& bmask == DCM_BGR_BLU_MASK) {
                    
if (!dcm.hasAlpha()) {
                        
imageType = TYPE_INT_BGR;
                    
}
                
}
  
// if (rmask == DCM_BGR_RED_MASK &&
            
}
   
// if (iraster.getPixelStride() == 1
        
}
   
// ((raster instanceof IntegerComponentRaster) &&
        
else if ((cm instanceof IndexColorModel) && (numBands == 1) &&
                 
isStandard &&
                 
(!cm.hasAlpha() || !isAlphaPre))
        
{
            
IndexColorModel icm = (IndexColorModel) cm;
            
int pixSize = icm.getPixelSize();

            
if (raster instanceof BytePackedRaster) {
                
imageType = TYPE_BYTE_BINARY;
            
}
   
// if (raster instanceof BytePackedRaster)
            
else if (raster instanceof ByteComponentRaster) {
                
ByteComponentRaster braster = (ByteComponentRaster) raster;
                
if (braster.getPixelStride() == 1 && pixSize <= 8) {
                    
imageType = TYPE_BYTE_INDEXED;
                
}
            
}
        
}
   
// else if (cm instanceof IndexColorModel) && (numBands == 1))
        
else if ((raster instanceof ShortComponentRaster)
                 
&& (cm instanceof DirectColorModel)
                 
&& isStandard
                 
&& (numBands == 3)
                 
&& !cm.hasAlpha())
        
{
            
DirectColorModel dcm = (DirectColorModel) cm;
            
if (dcm.getRedMask() == DCM_565_RED_MASK) {
                
if (dcm.getGreenMask() == DCM_565_GRN_MASK &&
                    
dcm.getBlueMask()
  
== DCM_565_BLU_MASK) {
                    
imageType = TYPE_USHORT_565_RGB;
                
}
            
}
            
else if (dcm.getRedMask() == DCM_555_RED_MASK) {
                
if (dcm.getGreenMask() == DCM_555_GRN_MASK &&
                    
dcm.getBlueMask() == DCM_555_BLU_MASK) {
                    
imageType = TYPE_USHORT_555_RGB;
                
}
            
}
        
}
   
// else if ((cm instanceof IndexColorModel) && (numBands == 1))
        
else if ((raster instanceof ByteComponentRaster)
                 
&& (cm instanceof ComponentColorModel)
                 
&& isStandard
                 
&& (raster.getSampleModel() instanceof PixelInterleavedSampleModel)
                 
&& (numBands == 3 || numBands == 4))
        
{
            
ComponentColorModel ccm = (ComponentColorModel) cm;
            
PixelInterleavedSampleModel csm =
                
(PixelInterleavedSampleModel)raster.getSampleModel();
            
ByteComponentRaster braster = (ByteComponentRaster) raster;
            
int[] offs = csm.getBandOffsets();
            
if (ccm.getNumComponents() != numBands) {
                
throw new RasterFormatException("Number of components in "+
                                                
"ColorModel ("+
                                                
ccm.getNumComponents()+
                                                
") does not match # in "+
                                                
" Raster ("+numBands+")");
            
}
            
int[] nBits = ccm.getComponentSize();
            
boolean is8bit = true;
            
for (int i=0; i < numBands; i++) {
                
if (nBits[i] != 8) {
                    
is8bit = false;
                    
break;
                
}
            
}
            
if (is8bit &&
                
braster.getPixelStride() == numBands &&
                
offs[0] == numBands-1 &&
                
offs[1] == numBands-2 &&
                
offs[2] == numBands-3)
            
{
                
if (numBands == 3 && !ccm.hasAlpha()) {
                    
imageType = TYPE_3BYTE_BGR;
                
}
                
else if (offs[3] == 0 && ccm.hasAlpha()) {
                    
imageType = (isAlphaPre
                                 
? TYPE_4BYTE_ABGR_PRE
                                 
: TYPE_4BYTE_ABGR);
                
}
            
}
        
}
   
// else if ((raster instanceof ByteComponentRaster) &&
    
}

    
private static boolean isStandard(ColorModel cm, WritableRaster wr) {
        
final Class<? extends ColorModel> cmClass = cm.getClass();
        
final Class<? extends WritableRaster> wrClass = wr.getClass();
        
final Class<? extends SampleModel> smClass = wr.getSampleModel().getClass();

        
final PrivilegedAction<Boolean> checkClassLoadersAction =
                
new PrivilegedAction<Boolean>()
        
{

            
@Override
            
public Boolean run() {
                
final ClassLoader std = System.class.getClassLoader();

                
return (cmClass.getClassLoader() == std) &&
                        
(smClass.getClassLoader() == std) &&
                        
(wrClass.getClassLoader() == std);
            
}
        
};
        
return AccessController.doPrivileged(checkClassLoadersAction);
    
}

    
/**
     
* Returns the image type.
  
If it is not one of the known types,
     
* TYPE_CUSTOM is returned.
     
* @return the image type of this <code>BufferedImage</code>.
     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*
 

     
*/

    
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,&nbsp;y) in the
     
* image, the ARGB pixel can be accessed in this way:
     
*
     
* <pre>
     
*
    
pixel
   
= rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre>
     
*
     
* <p>
     
*
     
* An <code>ArrayOutOfBoundsException</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,&nbsp;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 NullPointerException if the property name is null.
     
* @see ImageObserver
     
*
 

     
*/

    
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 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</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 RasterFormatException 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() {
        
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</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 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,&nbsp;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,&nbsp;0).
  
This is always zero.
     
* @return the y offset of the tile grid.
     
*/

    
public int getTileGridYOffset() {
        
return raster.getSampleModelTranslateY();
    
}

    
/**
     
* Returns tile (<code>tileX</code>,&nbsp;<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 ArrayIndexOutOfBoundsException 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 ArrayIndexOutOfBoundsException 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();
    
}
}