src/java.desktop/share/classes/java/awt/image/BufferedImage.java
changeset 47216 71c04702a3d5
parent 47152 9a0442e2131c
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.awt.image;
       
    27 
       
    28 import java.awt.Graphics2D;
       
    29 import java.awt.GraphicsEnvironment;
       
    30 import java.awt.Point;
       
    31 import java.awt.Rectangle;
       
    32 import java.awt.Transparency;
       
    33 import java.awt.color.ColorSpace;
       
    34 import java.security.AccessController;
       
    35 import java.security.PrivilegedAction;
       
    36 import java.util.Hashtable;
       
    37 import java.util.Set;
       
    38 import java.util.Vector;
       
    39 
       
    40 import sun.awt.image.ByteComponentRaster;
       
    41 import sun.awt.image.BytePackedRaster;
       
    42 import sun.awt.image.IntegerComponentRaster;
       
    43 import sun.awt.image.OffScreenImageSource;
       
    44 import sun.awt.image.ShortComponentRaster;
       
    45 
       
    46 /**
       
    47  *
       
    48  * The {@code BufferedImage} subclass describes an {@link
       
    49  * java.awt.Image Image} with an accessible buffer of image data.
       
    50  * A {@code BufferedImage} is comprised of a {@link ColorModel} and a
       
    51  * {@link Raster} of image data.
       
    52  * The number and types of bands in the {@link SampleModel} of the
       
    53  * {@code Raster} must match the number and types required by the
       
    54  * {@code ColorModel} to represent its color and alpha components.
       
    55  * All {@code BufferedImage} objects have an upper left corner
       
    56  * coordinate of (0, 0).  Any {@code Raster} used to construct a
       
    57  * {@code BufferedImage} must therefore have minX=0 and minY=0.
       
    58  *
       
    59  * <p>
       
    60  * This class relies on the data fetching and setting methods
       
    61  * of {@code Raster},
       
    62  * and on the color characterization methods of {@code ColorModel}.
       
    63  *
       
    64  * @see ColorModel
       
    65  * @see Raster
       
    66  * @see WritableRaster
       
    67  */
       
    68 public class BufferedImage extends java.awt.Image
       
    69                            implements WritableRenderedImage, Transparency
       
    70 {
       
    71     private int imageType = TYPE_CUSTOM;
       
    72     private ColorModel colorModel;
       
    73     private final WritableRaster raster;
       
    74     private OffScreenImageSource osis;
       
    75     private Hashtable<String, Object> properties;
       
    76 
       
    77     /**
       
    78      * Image Type Constants
       
    79      */
       
    80 
       
    81     /**
       
    82      * Image type is not recognized so it must be a customized
       
    83      * image.  This type is only used as a return value for the getType()
       
    84      * method.
       
    85      */
       
    86     public static final int TYPE_CUSTOM = 0;
       
    87 
       
    88     /**
       
    89      * Represents an image with 8-bit RGB color components packed into
       
    90      * integer pixels.  The image has a {@link DirectColorModel} without
       
    91      * alpha.
       
    92      * When data with non-opaque alpha is stored
       
    93      * in an image of this type,
       
    94      * the color data must be adjusted to a non-premultiplied form
       
    95      * and the alpha discarded,
       
    96      * as described in the
       
    97      * {@link java.awt.AlphaComposite} documentation.
       
    98      */
       
    99     public static final int TYPE_INT_RGB = 1;
       
   100 
       
   101     /**
       
   102      * Represents an image with 8-bit RGBA color components packed into
       
   103      * integer pixels.  The image has a {@code DirectColorModel}
       
   104      * with alpha. The color data in this image is considered not to be
       
   105      * premultiplied with alpha.  When this type is used as the
       
   106      * {@code imageType} argument to a {@code BufferedImage}
       
   107      * constructor, the created image is consistent with images
       
   108      * created in the JDK1.1 and earlier releases.
       
   109      */
       
   110     public static final int TYPE_INT_ARGB = 2;
       
   111 
       
   112     /**
       
   113      * Represents an image with 8-bit RGBA color components packed into
       
   114      * integer pixels.  The image has a {@code DirectColorModel}
       
   115      * with alpha.  The color data in this image is considered to be
       
   116      * premultiplied with alpha.
       
   117      */
       
   118     public static final int TYPE_INT_ARGB_PRE = 3;
       
   119 
       
   120     /**
       
   121      * Represents an image with 8-bit RGB color components, corresponding
       
   122      * to a Windows- or Solaris- style BGR color model, with the colors
       
   123      * Blue, Green, and Red packed into integer pixels.  There is no alpha.
       
   124      * The image has a {@link DirectColorModel}.
       
   125      * When data with non-opaque alpha is stored
       
   126      * in an image of this type,
       
   127      * the color data must be adjusted to a non-premultiplied form
       
   128      * and the alpha discarded,
       
   129      * as described in the
       
   130      * {@link java.awt.AlphaComposite} documentation.
       
   131      */
       
   132     public static final int TYPE_INT_BGR = 4;
       
   133 
       
   134     /**
       
   135      * Represents an image with 8-bit RGB color components, corresponding
       
   136      * to a Windows-style BGR color model) with the colors Blue, Green,
       
   137      * and Red stored in 3 bytes.  There is no alpha.  The image has a
       
   138      * {@code ComponentColorModel}.
       
   139      * When data with non-opaque alpha is stored
       
   140      * in an image of this type,
       
   141      * the color data must be adjusted to a non-premultiplied form
       
   142      * and the alpha discarded,
       
   143      * as described in the
       
   144      * {@link java.awt.AlphaComposite} documentation.
       
   145      */
       
   146     public static final int TYPE_3BYTE_BGR = 5;
       
   147 
       
   148     /**
       
   149      * Represents an image with 8-bit RGBA color components with the colors
       
   150      * Blue, Green, and Red stored in 3 bytes and 1 byte of alpha.  The
       
   151      * image has a {@code ComponentColorModel} with alpha.  The
       
   152      * color data in this image is considered not to be premultiplied with
       
   153      * alpha.  The byte data is interleaved in a single
       
   154      * byte array in the order A, B, G, R
       
   155      * from lower to higher byte addresses within each pixel.
       
   156      */
       
   157     public static final int TYPE_4BYTE_ABGR = 6;
       
   158 
       
   159     /**
       
   160      * Represents an image with 8-bit RGBA color components with the colors
       
   161      * Blue, Green, and Red stored in 3 bytes and 1 byte of alpha.  The
       
   162      * image has a {@code ComponentColorModel} with alpha. The color
       
   163      * data in this image is considered to be premultiplied with alpha.
       
   164      * The byte data is interleaved in a single byte array in the order
       
   165      * A, B, G, R from lower to higher byte addresses within each pixel.
       
   166      */
       
   167     public static final int TYPE_4BYTE_ABGR_PRE = 7;
       
   168 
       
   169     /**
       
   170      * Represents an image with 5-6-5 RGB color components (5-bits red,
       
   171      * 6-bits green, 5-bits blue) with no alpha.  This image has
       
   172      * a {@code DirectColorModel}.
       
   173      * When data with non-opaque alpha is stored
       
   174      * in an image of this type,
       
   175      * the color data must be adjusted to a non-premultiplied form
       
   176      * and the alpha discarded,
       
   177      * as described in the
       
   178      * {@link java.awt.AlphaComposite} documentation.
       
   179      */
       
   180     public static final int TYPE_USHORT_565_RGB = 8;
       
   181 
       
   182     /**
       
   183      * Represents an image with 5-5-5 RGB color components (5-bits red,
       
   184      * 5-bits green, 5-bits blue) with no alpha.  This image has
       
   185      * a {@code DirectColorModel}.
       
   186      * When data with non-opaque alpha is stored
       
   187      * in an image of this type,
       
   188      * the color data must be adjusted to a non-premultiplied form
       
   189      * and the alpha discarded,
       
   190      * as described in the
       
   191      * {@link java.awt.AlphaComposite} documentation.
       
   192      */
       
   193     public static final int TYPE_USHORT_555_RGB = 9;
       
   194 
       
   195     /**
       
   196      * Represents a unsigned byte grayscale image, non-indexed.  This
       
   197      * image has a {@code ComponentColorModel} with a CS_GRAY
       
   198      * {@link ColorSpace}.
       
   199      * When data with non-opaque alpha is stored
       
   200      * in an image of this type,
       
   201      * the color data must be adjusted to a non-premultiplied form
       
   202      * and the alpha discarded,
       
   203      * as described in the
       
   204      * {@link java.awt.AlphaComposite} documentation.
       
   205      */
       
   206     public static final int TYPE_BYTE_GRAY = 10;
       
   207 
       
   208     /**
       
   209      * Represents an unsigned short grayscale image, non-indexed).  This
       
   210      * image has a {@code ComponentColorModel} with a CS_GRAY
       
   211      * {@code ColorSpace}.
       
   212      * When data with non-opaque alpha is stored
       
   213      * in an image of this type,
       
   214      * the color data must be adjusted to a non-premultiplied form
       
   215      * and the alpha discarded,
       
   216      * as described in the
       
   217      * {@link java.awt.AlphaComposite} documentation.
       
   218      */
       
   219     public static final int TYPE_USHORT_GRAY = 11;
       
   220 
       
   221     /**
       
   222      * Represents an opaque byte-packed 1, 2, or 4 bit image.  The
       
   223      * image has an {@link IndexColorModel} without alpha.  When this
       
   224      * type is used as the {@code imageType} argument to the
       
   225      * {@code BufferedImage} constructor that takes an
       
   226      * {@code imageType} argument but no {@code ColorModel}
       
   227      * argument, a 1-bit image is created with an
       
   228      * {@code IndexColorModel} with two colors in the default
       
   229      * sRGB {@code ColorSpace}: {0,&nbsp;0,&nbsp;0} and
       
   230      * {255,&nbsp;255,&nbsp;255}.
       
   231      *
       
   232      * <p> Images with 2 or 4 bits per pixel may be constructed via
       
   233      * the {@code BufferedImage} constructor that takes a
       
   234      * {@code ColorModel} argument by supplying a
       
   235      * {@code ColorModel} with an appropriate map size.
       
   236      *
       
   237      * <p> Images with 8 bits per pixel should use the image types
       
   238      * {@code TYPE_BYTE_INDEXED} or {@code TYPE_BYTE_GRAY}
       
   239      * depending on their {@code ColorModel}.
       
   240 
       
   241      * <p> When color data is stored in an image of this type,
       
   242      * the closest color in the colormap is determined
       
   243      * by the {@code IndexColorModel} and the resulting index is stored.
       
   244      * Approximation and loss of alpha or color components
       
   245      * can result, depending on the colors in the
       
   246      * {@code IndexColorModel} colormap.
       
   247      */
       
   248     public static final int TYPE_BYTE_BINARY = 12;
       
   249 
       
   250     /**
       
   251      * Represents an indexed byte image.  When this type is used as the
       
   252      * {@code imageType} argument to the {@code BufferedImage}
       
   253      * constructor that takes an {@code imageType} argument
       
   254      * but no {@code ColorModel} argument, an
       
   255      * {@code IndexColorModel} is created with
       
   256      * a 256-color 6/6/6 color cube palette with the rest of the colors
       
   257      * from 216-255 populated by grayscale values in the
       
   258      * default sRGB ColorSpace.
       
   259      *
       
   260      * <p> When color data is stored in an image of this type,
       
   261      * the closest color in the colormap is determined
       
   262      * by the {@code IndexColorModel} and the resulting index is stored.
       
   263      * Approximation and loss of alpha or color components
       
   264      * can result, depending on the colors in the
       
   265      * {@code IndexColorModel} colormap.
       
   266      */
       
   267     public static final int TYPE_BYTE_INDEXED = 13;
       
   268 
       
   269     private static final int DCM_RED_MASK   = 0x00ff0000;
       
   270     private static final int DCM_GREEN_MASK = 0x0000ff00;
       
   271     private static final int DCM_BLUE_MASK  = 0x000000ff;
       
   272     private static final int DCM_ALPHA_MASK = 0xff000000;
       
   273     private static final int DCM_565_RED_MASK = 0xf800;
       
   274     private static final int DCM_565_GRN_MASK = 0x07E0;
       
   275     private static final int DCM_565_BLU_MASK = 0x001F;
       
   276     private static final int DCM_555_RED_MASK = 0x7C00;
       
   277     private static final int DCM_555_GRN_MASK = 0x03E0;
       
   278     private static final int DCM_555_BLU_MASK = 0x001F;
       
   279     private static final int DCM_BGR_RED_MASK = 0x0000ff;
       
   280     private static final int DCM_BGR_GRN_MASK = 0x00ff00;
       
   281     private static final int DCM_BGR_BLU_MASK = 0xff0000;
       
   282 
       
   283 
       
   284     private static native void initIDs();
       
   285     static {
       
   286         ColorModel.loadLibraries();
       
   287         initIDs();
       
   288     }
       
   289 
       
   290     /**
       
   291      * Constructs a {@code BufferedImage} of one of the predefined
       
   292      * image types.  The {@code ColorSpace} for the image is the
       
   293      * default sRGB space.
       
   294      * @param width     width of the created image
       
   295      * @param height    height of the created image
       
   296      * @param imageType type of the created image
       
   297      * @see ColorSpace
       
   298      * @see #TYPE_INT_RGB
       
   299      * @see #TYPE_INT_ARGB
       
   300      * @see #TYPE_INT_ARGB_PRE
       
   301      * @see #TYPE_INT_BGR
       
   302      * @see #TYPE_3BYTE_BGR
       
   303      * @see #TYPE_4BYTE_ABGR
       
   304      * @see #TYPE_4BYTE_ABGR_PRE
       
   305      * @see #TYPE_BYTE_GRAY
       
   306      * @see #TYPE_USHORT_GRAY
       
   307      * @see #TYPE_BYTE_BINARY
       
   308      * @see #TYPE_BYTE_INDEXED
       
   309      * @see #TYPE_USHORT_565_RGB
       
   310      * @see #TYPE_USHORT_555_RGB
       
   311      */
       
   312     public BufferedImage(int width,
       
   313                          int height,
       
   314                          int imageType) {
       
   315         switch (imageType) {
       
   316         case TYPE_INT_RGB:
       
   317             {
       
   318                 colorModel = new DirectColorModel(24,
       
   319                                                   0x00ff0000,   // Red
       
   320                                                   0x0000ff00,   // Green
       
   321                                                   0x000000ff,   // Blue
       
   322                                                   0x0           // Alpha
       
   323                                                   );
       
   324                 raster = colorModel.createCompatibleWritableRaster(width,
       
   325                                                                    height);
       
   326             }
       
   327         break;
       
   328 
       
   329         case TYPE_INT_ARGB:
       
   330             {
       
   331                 colorModel = ColorModel.getRGBdefault();
       
   332 
       
   333                 raster = colorModel.createCompatibleWritableRaster(width,
       
   334                                                                    height);
       
   335             }
       
   336         break;
       
   337 
       
   338         case TYPE_INT_ARGB_PRE:
       
   339             {
       
   340                 colorModel = new
       
   341                     DirectColorModel(
       
   342                                      ColorSpace.getInstance(ColorSpace.CS_sRGB),
       
   343                                      32,
       
   344                                      0x00ff0000,// Red
       
   345                                      0x0000ff00,// Green
       
   346                                      0x000000ff,// Blue
       
   347                                      0xff000000,// Alpha
       
   348                                      true,       // Alpha Premultiplied
       
   349                                      DataBuffer.TYPE_INT
       
   350                                      );
       
   351                 raster = colorModel.createCompatibleWritableRaster(width,
       
   352                                                                    height);
       
   353             }
       
   354         break;
       
   355 
       
   356         case TYPE_INT_BGR:
       
   357             {
       
   358                 colorModel = new DirectColorModel(24,
       
   359                                                   0x000000ff,   // Red
       
   360                                                   0x0000ff00,   // Green
       
   361                                                   0x00ff0000    // Blue
       
   362                                                   );
       
   363                 raster = colorModel.createCompatibleWritableRaster(width,
       
   364                                                                    height);
       
   365             }
       
   366         break;
       
   367 
       
   368         case TYPE_3BYTE_BGR:
       
   369             {
       
   370                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
       
   371                 int[] nBits = {8, 8, 8};
       
   372                 int[] bOffs = {2, 1, 0};
       
   373                 colorModel = new ComponentColorModel(cs, nBits, false, false,
       
   374                                                      Transparency.OPAQUE,
       
   375                                                      DataBuffer.TYPE_BYTE);
       
   376                 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
       
   377                                                         width, height,
       
   378                                                         width*3, 3,
       
   379                                                         bOffs, null);
       
   380             }
       
   381         break;
       
   382 
       
   383         case TYPE_4BYTE_ABGR:
       
   384             {
       
   385                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
       
   386                 int[] nBits = {8, 8, 8, 8};
       
   387                 int[] bOffs = {3, 2, 1, 0};
       
   388                 colorModel = new ComponentColorModel(cs, nBits, true, false,
       
   389                                                      Transparency.TRANSLUCENT,
       
   390                                                      DataBuffer.TYPE_BYTE);
       
   391                 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
       
   392                                                         width, height,
       
   393                                                         width*4, 4,
       
   394                                                         bOffs, null);
       
   395             }
       
   396         break;
       
   397 
       
   398         case TYPE_4BYTE_ABGR_PRE:
       
   399             {
       
   400                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
       
   401                 int[] nBits = {8, 8, 8, 8};
       
   402                 int[] bOffs = {3, 2, 1, 0};
       
   403                 colorModel = new ComponentColorModel(cs, nBits, true, true,
       
   404                                                      Transparency.TRANSLUCENT,
       
   405                                                      DataBuffer.TYPE_BYTE);
       
   406                 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
       
   407                                                         width, height,
       
   408                                                         width*4, 4,
       
   409                                                         bOffs, null);
       
   410             }
       
   411         break;
       
   412 
       
   413         case TYPE_BYTE_GRAY:
       
   414             {
       
   415                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
       
   416                 int[] nBits = {8};
       
   417                 colorModel = new ComponentColorModel(cs, nBits, false, true,
       
   418                                                      Transparency.OPAQUE,
       
   419                                                      DataBuffer.TYPE_BYTE);
       
   420                 raster = colorModel.createCompatibleWritableRaster(width,
       
   421                                                                    height);
       
   422             }
       
   423         break;
       
   424 
       
   425         case TYPE_USHORT_GRAY:
       
   426             {
       
   427                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
       
   428                 int[] nBits = {16};
       
   429                 colorModel = new ComponentColorModel(cs, nBits, false, true,
       
   430                                                      Transparency.OPAQUE,
       
   431                                                      DataBuffer.TYPE_USHORT);
       
   432                 raster = colorModel.createCompatibleWritableRaster(width,
       
   433                                                                    height);
       
   434             }
       
   435         break;
       
   436 
       
   437         case TYPE_BYTE_BINARY:
       
   438             {
       
   439                 byte[] arr = {(byte)0, (byte)0xff};
       
   440 
       
   441                 colorModel = new IndexColorModel(1, 2, arr, arr, arr);
       
   442                 raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE,
       
   443                                                    width, height, 1, 1, null);
       
   444             }
       
   445         break;
       
   446 
       
   447         case TYPE_BYTE_INDEXED:
       
   448             {
       
   449                 // Create a 6x6x6 color cube
       
   450                 int[] cmap = new int[256];
       
   451                 int i=0;
       
   452                 for (int r=0; r < 256; r += 51) {
       
   453                     for (int g=0; g < 256; g += 51) {
       
   454                         for (int b=0; b < 256; b += 51) {
       
   455                             cmap[i++] = (r<<16)|(g<<8)|b;
       
   456                         }
       
   457                     }
       
   458                 }
       
   459                 // And populate the rest of the cmap with gray values
       
   460                 int grayIncr = 256/(256-i);
       
   461 
       
   462                 // The gray ramp will be between 18 and 252
       
   463                 int gray = grayIncr*3;
       
   464                 for (; i < 256; i++) {
       
   465                     cmap[i] = (gray<<16)|(gray<<8)|gray;
       
   466                     gray += grayIncr;
       
   467                 }
       
   468 
       
   469                 colorModel = new IndexColorModel(8, 256, cmap, 0, false, -1,
       
   470                                                  DataBuffer.TYPE_BYTE);
       
   471                 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
       
   472                                                       width, height, 1, null);
       
   473             }
       
   474         break;
       
   475 
       
   476         case TYPE_USHORT_565_RGB:
       
   477             {
       
   478                 colorModel = new DirectColorModel(16,
       
   479                                                   DCM_565_RED_MASK,
       
   480                                                   DCM_565_GRN_MASK,
       
   481                                                   DCM_565_BLU_MASK
       
   482                                                   );
       
   483                 raster = colorModel.createCompatibleWritableRaster(width,
       
   484                                                                    height);
       
   485             }
       
   486             break;
       
   487 
       
   488         case TYPE_USHORT_555_RGB:
       
   489             {
       
   490                 colorModel = new DirectColorModel(15,
       
   491                                                   DCM_555_RED_MASK,
       
   492                                                   DCM_555_GRN_MASK,
       
   493                                                   DCM_555_BLU_MASK
       
   494                                                   );
       
   495                 raster = colorModel.createCompatibleWritableRaster(width,
       
   496                                                                    height);
       
   497             }
       
   498             break;
       
   499 
       
   500         default:
       
   501             throw new IllegalArgumentException ("Unknown image type " +
       
   502                                                 imageType);
       
   503         }
       
   504 
       
   505         this.imageType = imageType;
       
   506     }
       
   507 
       
   508     /**
       
   509      * Constructs a {@code BufferedImage} of one of the predefined
       
   510      * image types:
       
   511      * TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED.
       
   512      *
       
   513      * <p> If the image type is TYPE_BYTE_BINARY, the number of
       
   514      * entries in the color model is used to determine whether the
       
   515      * image should have 1, 2, or 4 bits per pixel.  If the color model
       
   516      * has 1 or 2 entries, the image will have 1 bit per pixel.  If it
       
   517      * has 3 or 4 entries, the image with have 2 bits per pixel.  If
       
   518      * it has between 5 and 16 entries, the image will have 4 bits per
       
   519      * pixel.  Otherwise, an IllegalArgumentException will be thrown.
       
   520      *
       
   521      * @param width     width of the created image
       
   522      * @param height    height of the created image
       
   523      * @param imageType type of the created image
       
   524      * @param cm        {@code IndexColorModel} of the created image
       
   525      * @throws IllegalArgumentException   if the imageType is not
       
   526      * TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED or if the imageType is
       
   527      * TYPE_BYTE_BINARY and the color map has more than 16 entries.
       
   528      * @see #TYPE_BYTE_BINARY
       
   529      * @see #TYPE_BYTE_INDEXED
       
   530      */
       
   531     public BufferedImage (int width,
       
   532                           int height,
       
   533                           int imageType,
       
   534                           IndexColorModel cm) {
       
   535         if (cm.hasAlpha() && cm.isAlphaPremultiplied()) {
       
   536             throw new IllegalArgumentException("This image types do not have "+
       
   537                                                "premultiplied alpha.");
       
   538         }
       
   539 
       
   540         switch(imageType) {
       
   541         case TYPE_BYTE_BINARY:
       
   542             int bits; // Will be set below
       
   543             int mapSize = cm.getMapSize();
       
   544             if (mapSize <= 2) {
       
   545                 bits = 1;
       
   546             } else if (mapSize <= 4) {
       
   547                 bits = 2;
       
   548             } else if (mapSize <= 16) {
       
   549                 bits = 4;
       
   550             } else {
       
   551                 throw new IllegalArgumentException
       
   552                     ("Color map for TYPE_BYTE_BINARY " +
       
   553                      "must have no more than 16 entries");
       
   554             }
       
   555             raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE,
       
   556                                                 width, height, 1, bits, null);
       
   557             break;
       
   558 
       
   559         case TYPE_BYTE_INDEXED:
       
   560             raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
       
   561                                                     width, height, 1, null);
       
   562             break;
       
   563         default:
       
   564             throw new IllegalArgumentException("Invalid image type (" +
       
   565                                                imageType+").  Image type must"+
       
   566                                                " be either TYPE_BYTE_BINARY or "+
       
   567                                                " TYPE_BYTE_INDEXED");
       
   568         }
       
   569 
       
   570         if (!cm.isCompatibleRaster(raster)) {
       
   571             throw new IllegalArgumentException("Incompatible image type and IndexColorModel");
       
   572         }
       
   573 
       
   574         colorModel = cm;
       
   575         this.imageType = imageType;
       
   576     }
       
   577 
       
   578     /**
       
   579      * Constructs a new {@code BufferedImage} with a specified
       
   580      * {@code ColorModel} and {@code Raster}.  If the number and
       
   581      * types of bands in the {@code SampleModel} of the
       
   582      * {@code Raster} do not match the number and types required by
       
   583      * the {@code ColorModel} to represent its color and alpha
       
   584      * components, a {@link RasterFormatException} is thrown.  This
       
   585      * method can multiply or divide the color {@code Raster} data by
       
   586      * alpha to match the {@code alphaPremultiplied} state
       
   587      * in the {@code ColorModel}.  Properties for this
       
   588      * {@code BufferedImage} can be established by passing
       
   589      * in a {@link Hashtable} of {@code String}/{@code Object}
       
   590      * pairs.
       
   591      * @param cm {@code ColorModel} for the new image
       
   592      * @param raster     {@code Raster} for the image data
       
   593      * @param isRasterPremultiplied   if {@code true}, the data in
       
   594      *                  the raster has been premultiplied with alpha.
       
   595      * @param properties {@code Hashtable} of
       
   596      *                  {@code String}/{@code Object} pairs.
       
   597      * @exception RasterFormatException if the number and
       
   598      * types of bands in the {@code SampleModel} of the
       
   599      * {@code Raster} do not match the number and types required by
       
   600      * the {@code ColorModel} to represent its color and alpha
       
   601      * components.
       
   602      * @exception IllegalArgumentException if
       
   603      *          {@code raster} is incompatible with {@code cm}
       
   604      * @see ColorModel
       
   605      * @see Raster
       
   606      * @see WritableRaster
       
   607      */
       
   608 
       
   609 
       
   610 /*
       
   611  *
       
   612  *  FOR NOW THE CODE WHICH DEFINES THE RASTER TYPE IS DUPLICATED BY DVF
       
   613  *  SEE THE METHOD DEFINERASTERTYPE @ RASTEROUTPUTMANAGER
       
   614  *
       
   615  */
       
   616     public BufferedImage (ColorModel cm,
       
   617                           WritableRaster raster,
       
   618                           boolean isRasterPremultiplied,
       
   619                           Hashtable<?,?> properties) {
       
   620 
       
   621         if (!cm.isCompatibleRaster(raster)) {
       
   622             throw new
       
   623                 IllegalArgumentException("Raster "+raster+
       
   624                                          " is incompatible with ColorModel "+
       
   625                                          cm);
       
   626         }
       
   627 
       
   628         if ((raster.minX != 0) || (raster.minY != 0)) {
       
   629             throw new
       
   630                 IllegalArgumentException("Raster "+raster+
       
   631                                          " has minX or minY not equal to zero: "
       
   632                                          + raster.minX + " " + raster.minY);
       
   633         }
       
   634 
       
   635         colorModel = cm;
       
   636         this.raster  = raster;
       
   637         if (properties != null && !properties.isEmpty()) {
       
   638             this.properties = new Hashtable<>();
       
   639             for (final Object key : properties.keySet()) {
       
   640                 if (key instanceof String) {
       
   641                     this.properties.put((String) key, properties.get(key));
       
   642                 }
       
   643             }
       
   644         }
       
   645         int numBands = raster.getNumBands();
       
   646         boolean isAlphaPre = cm.isAlphaPremultiplied();
       
   647         final boolean isStandard = isStandard(cm, raster);
       
   648         ColorSpace cs;
       
   649 
       
   650         // Force the raster data alpha state to match the premultiplied
       
   651         // state in the color model
       
   652         coerceData(isRasterPremultiplied);
       
   653 
       
   654         SampleModel sm = raster.getSampleModel();
       
   655         cs = cm.getColorSpace();
       
   656         int csType = cs.getType();
       
   657         if (csType != ColorSpace.TYPE_RGB) {
       
   658             if (csType == ColorSpace.TYPE_GRAY &&
       
   659                 isStandard &&
       
   660                 cm instanceof ComponentColorModel) {
       
   661                 // Check if this might be a child raster (fix for bug 4240596)
       
   662                 if (sm instanceof ComponentSampleModel &&
       
   663                     ((ComponentSampleModel)sm).getPixelStride() != numBands) {
       
   664                     imageType = TYPE_CUSTOM;
       
   665                 } else if (raster instanceof ByteComponentRaster &&
       
   666                        raster.getNumBands() == 1 &&
       
   667                        cm.getComponentSize(0) == 8 &&
       
   668                        ((ByteComponentRaster)raster).getPixelStride() == 1) {
       
   669                     imageType = TYPE_BYTE_GRAY;
       
   670                 } else if (raster instanceof ShortComponentRaster &&
       
   671                        raster.getNumBands() == 1 &&
       
   672                        cm.getComponentSize(0) == 16 &&
       
   673                        ((ShortComponentRaster)raster).getPixelStride() == 1) {
       
   674                     imageType = TYPE_USHORT_GRAY;
       
   675                 }
       
   676             } else {
       
   677                 imageType = TYPE_CUSTOM;
       
   678             }
       
   679             return;
       
   680         }
       
   681 
       
   682         if ((raster instanceof IntegerComponentRaster) &&
       
   683             (numBands == 3 || numBands == 4)) {
       
   684             IntegerComponentRaster iraster =
       
   685                 (IntegerComponentRaster) raster;
       
   686             // Check if the raster params and the color model
       
   687             // are correct
       
   688             int pixSize = cm.getPixelSize();
       
   689             if (iraster.getPixelStride() == 1 &&
       
   690                 isStandard &&
       
   691                 cm instanceof DirectColorModel  &&
       
   692                 (pixSize == 32 || pixSize == 24))
       
   693             {
       
   694                 // Now check on the DirectColorModel params
       
   695                 DirectColorModel dcm = (DirectColorModel) cm;
       
   696                 int rmask = dcm.getRedMask();
       
   697                 int gmask = dcm.getGreenMask();
       
   698                 int bmask = dcm.getBlueMask();
       
   699                 if (rmask == DCM_RED_MASK && gmask == DCM_GREEN_MASK &&
       
   700                     bmask == DCM_BLUE_MASK)
       
   701                 {
       
   702                     if (dcm.getAlphaMask() == DCM_ALPHA_MASK) {
       
   703                         imageType = (isAlphaPre
       
   704                                      ? TYPE_INT_ARGB_PRE
       
   705                                      : TYPE_INT_ARGB);
       
   706                     }
       
   707                     else {
       
   708                         // No Alpha
       
   709                         if (!dcm.hasAlpha()) {
       
   710                             imageType = TYPE_INT_RGB;
       
   711                         }
       
   712                     }
       
   713                 }   // if (dcm.getRedMask() == DCM_RED_MASK &&
       
   714                 else if (rmask == DCM_BGR_RED_MASK && gmask == DCM_BGR_GRN_MASK
       
   715                          && bmask == DCM_BGR_BLU_MASK) {
       
   716                     if (!dcm.hasAlpha()) {
       
   717                         imageType = TYPE_INT_BGR;
       
   718                     }
       
   719                 }  // if (rmask == DCM_BGR_RED_MASK &&
       
   720             }   // if (iraster.getPixelStride() == 1
       
   721         }   // ((raster instanceof IntegerComponentRaster) &&
       
   722         else if ((cm instanceof IndexColorModel) && (numBands == 1) &&
       
   723                  isStandard &&
       
   724                  (!cm.hasAlpha() || !isAlphaPre))
       
   725         {
       
   726             IndexColorModel icm = (IndexColorModel) cm;
       
   727             int pixSize = icm.getPixelSize();
       
   728 
       
   729             if (raster instanceof BytePackedRaster) {
       
   730                 imageType = TYPE_BYTE_BINARY;
       
   731             }   // if (raster instanceof BytePackedRaster)
       
   732             else if (raster instanceof ByteComponentRaster) {
       
   733                 ByteComponentRaster braster = (ByteComponentRaster) raster;
       
   734                 if (braster.getPixelStride() == 1 && pixSize <= 8) {
       
   735                     imageType = TYPE_BYTE_INDEXED;
       
   736                 }
       
   737             }
       
   738         }   // else if (cm instanceof IndexColorModel) && (numBands == 1))
       
   739         else if ((raster instanceof ShortComponentRaster)
       
   740                  && (cm instanceof DirectColorModel)
       
   741                  && isStandard
       
   742                  && (numBands == 3)
       
   743                  && !cm.hasAlpha())
       
   744         {
       
   745             DirectColorModel dcm = (DirectColorModel) cm;
       
   746             if (dcm.getRedMask() == DCM_565_RED_MASK) {
       
   747                 if (dcm.getGreenMask() == DCM_565_GRN_MASK &&
       
   748                     dcm.getBlueMask()  == DCM_565_BLU_MASK) {
       
   749                     imageType = TYPE_USHORT_565_RGB;
       
   750                 }
       
   751             }
       
   752             else if (dcm.getRedMask() == DCM_555_RED_MASK) {
       
   753                 if (dcm.getGreenMask() == DCM_555_GRN_MASK &&
       
   754                     dcm.getBlueMask() == DCM_555_BLU_MASK) {
       
   755                     imageType = TYPE_USHORT_555_RGB;
       
   756                 }
       
   757             }
       
   758         }   // else if ((cm instanceof IndexColorModel) && (numBands == 1))
       
   759         else if ((raster instanceof ByteComponentRaster)
       
   760                  && (cm instanceof ComponentColorModel)
       
   761                  && isStandard
       
   762                  && (raster.getSampleModel() instanceof PixelInterleavedSampleModel)
       
   763                  && (numBands == 3 || numBands == 4))
       
   764         {
       
   765             ComponentColorModel ccm = (ComponentColorModel) cm;
       
   766             PixelInterleavedSampleModel csm =
       
   767                 (PixelInterleavedSampleModel)raster.getSampleModel();
       
   768             ByteComponentRaster braster = (ByteComponentRaster) raster;
       
   769             int[] offs = csm.getBandOffsets();
       
   770             if (ccm.getNumComponents() != numBands) {
       
   771                 throw new RasterFormatException("Number of components in "+
       
   772                                                 "ColorModel ("+
       
   773                                                 ccm.getNumComponents()+
       
   774                                                 ") does not match # in "+
       
   775                                                 " Raster ("+numBands+")");
       
   776             }
       
   777             int[] nBits = ccm.getComponentSize();
       
   778             boolean is8bit = true;
       
   779             for (int i=0; i < numBands; i++) {
       
   780                 if (nBits[i] != 8) {
       
   781                     is8bit = false;
       
   782                     break;
       
   783                 }
       
   784             }
       
   785             if (is8bit &&
       
   786                 braster.getPixelStride() == numBands &&
       
   787                 offs[0] == numBands-1 &&
       
   788                 offs[1] == numBands-2 &&
       
   789                 offs[2] == numBands-3)
       
   790             {
       
   791                 if (numBands == 3 && !ccm.hasAlpha()) {
       
   792                     imageType = TYPE_3BYTE_BGR;
       
   793                 }
       
   794                 else if (offs[3] == 0 && ccm.hasAlpha()) {
       
   795                     imageType = (isAlphaPre
       
   796                                  ? TYPE_4BYTE_ABGR_PRE
       
   797                                  : TYPE_4BYTE_ABGR);
       
   798                 }
       
   799             }
       
   800         }   // else if ((raster instanceof ByteComponentRaster) &&
       
   801     }
       
   802 
       
   803     private static boolean isStandard(ColorModel cm, WritableRaster wr) {
       
   804         final Class<? extends ColorModel> cmClass = cm.getClass();
       
   805         final Class<? extends WritableRaster> wrClass = wr.getClass();
       
   806         final Class<? extends SampleModel> smClass = wr.getSampleModel().getClass();
       
   807 
       
   808         final PrivilegedAction<Boolean> checkClassLoadersAction =
       
   809                 new PrivilegedAction<Boolean>()
       
   810         {
       
   811 
       
   812             @Override
       
   813             public Boolean run() {
       
   814                 final ClassLoader std = System.class.getClassLoader();
       
   815 
       
   816                 return (cmClass.getClassLoader() == std) &&
       
   817                         (smClass.getClassLoader() == std) &&
       
   818                         (wrClass.getClassLoader() == std);
       
   819             }
       
   820         };
       
   821         return AccessController.doPrivileged(checkClassLoadersAction);
       
   822     }
       
   823 
       
   824     /**
       
   825      * Returns the image type.  If it is not one of the known types,
       
   826      * TYPE_CUSTOM is returned.
       
   827      * @return the image type of this {@code BufferedImage}.
       
   828      * @see #TYPE_INT_RGB
       
   829      * @see #TYPE_INT_ARGB
       
   830      * @see #TYPE_INT_ARGB_PRE
       
   831      * @see #TYPE_INT_BGR
       
   832      * @see #TYPE_3BYTE_BGR
       
   833      * @see #TYPE_4BYTE_ABGR
       
   834      * @see #TYPE_4BYTE_ABGR_PRE
       
   835      * @see #TYPE_BYTE_GRAY
       
   836      * @see #TYPE_BYTE_BINARY
       
   837      * @see #TYPE_BYTE_INDEXED
       
   838      * @see #TYPE_USHORT_GRAY
       
   839      * @see #TYPE_USHORT_565_RGB
       
   840      * @see #TYPE_USHORT_555_RGB
       
   841      * @see #TYPE_CUSTOM
       
   842      */
       
   843     public int getType() {
       
   844         return imageType;
       
   845     }
       
   846 
       
   847     /**
       
   848      * Returns the {@code ColorModel}.
       
   849      * @return the {@code ColorModel} of this
       
   850      *  {@code BufferedImage}.
       
   851      */
       
   852     public ColorModel getColorModel() {
       
   853         return colorModel;
       
   854     }
       
   855 
       
   856     /**
       
   857      * Returns the {@link WritableRaster}.
       
   858      * @return the {@code WritableRaster} of this
       
   859      *  {@code BufferedImage}.
       
   860      */
       
   861     public WritableRaster getRaster() {
       
   862         return raster;
       
   863     }
       
   864 
       
   865 
       
   866     /**
       
   867      * Returns a {@code WritableRaster} representing the alpha
       
   868      * channel for {@code BufferedImage} objects
       
   869      * with {@code ColorModel} objects that support a separate
       
   870      * spatial alpha channel, such as {@code ComponentColorModel} and
       
   871      * {@code DirectColorModel}.  Returns {@code null} if there
       
   872      * is no alpha channel associated with the {@code ColorModel} in
       
   873      * this image.  This method assumes that for all
       
   874      * {@code ColorModel} objects other than
       
   875      * {@code IndexColorModel}, if the {@code ColorModel}
       
   876      * supports alpha, there is a separate alpha channel
       
   877      * which is stored as the last band of image data.
       
   878      * If the image uses an {@code IndexColorModel} that
       
   879      * has alpha in the lookup table, this method returns
       
   880      * {@code null} since there is no spatially discrete alpha
       
   881      * channel.  This method creates a new
       
   882      * {@code WritableRaster}, but shares the data array.
       
   883      * @return a {@code WritableRaster} or {@code null} if this
       
   884      *          {@code BufferedImage} has no alpha channel associated
       
   885      *          with its {@code ColorModel}.
       
   886      */
       
   887     public WritableRaster getAlphaRaster() {
       
   888         return colorModel.getAlphaRaster(raster);
       
   889     }
       
   890 
       
   891     /**
       
   892      * Returns an integer pixel in the default RGB color model
       
   893      * (TYPE_INT_ARGB) and default sRGB colorspace.  Color
       
   894      * conversion takes place if this default model does not match
       
   895      * the image {@code ColorModel}.  There are only 8-bits of
       
   896      * precision for each color component in the returned data when using
       
   897      * this method.
       
   898      *
       
   899      * <p>
       
   900      *
       
   901      * An {@code ArrayOutOfBoundsException} may be thrown
       
   902      * if the coordinates are not in bounds.
       
   903      * However, explicit bounds checking is not guaranteed.
       
   904      *
       
   905      * @param x the X coordinate of the pixel from which to get
       
   906      *          the pixel in the default RGB color model and sRGB
       
   907      *          color space
       
   908      * @param y the Y coordinate of the pixel from which to get
       
   909      *          the pixel in the default RGB color model and sRGB
       
   910      *          color space
       
   911      * @return an integer pixel in the default RGB color model and
       
   912      *          default sRGB colorspace.
       
   913      * @see #setRGB(int, int, int)
       
   914      * @see #setRGB(int, int, int, int, int[], int, int)
       
   915      */
       
   916     public int getRGB(int x, int y) {
       
   917         return colorModel.getRGB(raster.getDataElements(x, y, null));
       
   918     }
       
   919 
       
   920     /**
       
   921      * Returns an array of integer pixels in the default RGB color model
       
   922      * (TYPE_INT_ARGB) and default sRGB color space,
       
   923      * from a portion of the image data.  Color conversion takes
       
   924      * place if the default model does not match the image
       
   925      * {@code ColorModel}.  There are only 8-bits of precision for
       
   926      * each color component in the returned data when
       
   927      * using this method.  With a specified coordinate (x,&nbsp;y) in the
       
   928      * image, the ARGB pixel can be accessed in this way:
       
   929      *
       
   930      * <pre>
       
   931      *    pixel   = rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre>
       
   932      *
       
   933      * <p>
       
   934      *
       
   935      * An {@code ArrayOutOfBoundsException} may be thrown
       
   936      * if the region is not in bounds.
       
   937      * However, explicit bounds checking is not guaranteed.
       
   938      *
       
   939      * @param startX      the starting X coordinate
       
   940      * @param startY      the starting Y coordinate
       
   941      * @param w           width of region
       
   942      * @param h           height of region
       
   943      * @param rgbArray    if not {@code null}, the rgb pixels are
       
   944      *          written here
       
   945      * @param offset      offset into the {@code rgbArray}
       
   946      * @param scansize    scanline stride for the {@code rgbArray}
       
   947      * @return            array of RGB pixels.
       
   948      * @see #setRGB(int, int, int)
       
   949      * @see #setRGB(int, int, int, int, int[], int, int)
       
   950      */
       
   951     public int[] getRGB(int startX, int startY, int w, int h,
       
   952                         int[] rgbArray, int offset, int scansize) {
       
   953         int yoff  = offset;
       
   954         int off;
       
   955         Object data;
       
   956         int nbands = raster.getNumBands();
       
   957         int dataType = raster.getDataBuffer().getDataType();
       
   958         switch (dataType) {
       
   959         case DataBuffer.TYPE_BYTE:
       
   960             data = new byte[nbands];
       
   961             break;
       
   962         case DataBuffer.TYPE_USHORT:
       
   963             data = new short[nbands];
       
   964             break;
       
   965         case DataBuffer.TYPE_INT:
       
   966             data = new int[nbands];
       
   967             break;
       
   968         case DataBuffer.TYPE_FLOAT:
       
   969             data = new float[nbands];
       
   970             break;
       
   971         case DataBuffer.TYPE_DOUBLE:
       
   972             data = new double[nbands];
       
   973             break;
       
   974         default:
       
   975             throw new IllegalArgumentException("Unknown data buffer type: "+
       
   976                                                dataType);
       
   977         }
       
   978 
       
   979         if (rgbArray == null) {
       
   980             rgbArray = new int[offset+h*scansize];
       
   981         }
       
   982 
       
   983         for (int y = startY; y < startY+h; y++, yoff+=scansize) {
       
   984             off = yoff;
       
   985             for (int x = startX; x < startX+w; x++) {
       
   986                 rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x,
       
   987                                                                         y,
       
   988                                                                         data));
       
   989             }
       
   990         }
       
   991 
       
   992         return rgbArray;
       
   993     }
       
   994 
       
   995 
       
   996     /**
       
   997      * Sets a pixel in this {@code BufferedImage} to the specified
       
   998      * RGB value. The pixel is assumed to be in the default RGB color
       
   999      * model, TYPE_INT_ARGB, and default sRGB color space.  For images
       
  1000      * with an {@code IndexColorModel}, the index with the nearest
       
  1001      * color is chosen.
       
  1002      *
       
  1003      * <p>
       
  1004      *
       
  1005      * An {@code ArrayOutOfBoundsException} may be thrown
       
  1006      * if the coordinates are not in bounds.
       
  1007      * However, explicit bounds checking is not guaranteed.
       
  1008      *
       
  1009      * @param x the X coordinate of the pixel to set
       
  1010      * @param y the Y coordinate of the pixel to set
       
  1011      * @param rgb the RGB value
       
  1012      * @see #getRGB(int, int)
       
  1013      * @see #getRGB(int, int, int, int, int[], int, int)
       
  1014      */
       
  1015     public void setRGB(int x, int y, int rgb) {
       
  1016         raster.setDataElements(x, y, colorModel.getDataElements(rgb, null));
       
  1017     }
       
  1018 
       
  1019     /**
       
  1020      * Sets an array of integer pixels in the default RGB color model
       
  1021      * (TYPE_INT_ARGB) and default sRGB color space,
       
  1022      * into a portion of the image data.  Color conversion takes place
       
  1023      * if the default model does not match the image
       
  1024      * {@code ColorModel}.  There are only 8-bits of precision for
       
  1025      * each color component in the returned data when
       
  1026      * using this method.  With a specified coordinate (x,&nbsp;y) in the
       
  1027      * this image, the ARGB pixel can be accessed in this way:
       
  1028      * <pre>
       
  1029      *    pixel   = rgbArray[offset + (y-startY)*scansize + (x-startX)];
       
  1030      * </pre>
       
  1031      * WARNING: No dithering takes place.
       
  1032      *
       
  1033      * <p>
       
  1034      *
       
  1035      * An {@code ArrayOutOfBoundsException} may be thrown
       
  1036      * if the region is not in bounds.
       
  1037      * However, explicit bounds checking is not guaranteed.
       
  1038      *
       
  1039      * @param startX      the starting X coordinate
       
  1040      * @param startY      the starting Y coordinate
       
  1041      * @param w           width of the region
       
  1042      * @param h           height of the region
       
  1043      * @param rgbArray    the rgb pixels
       
  1044      * @param offset      offset into the {@code rgbArray}
       
  1045      * @param scansize    scanline stride for the {@code rgbArray}
       
  1046      * @see #getRGB(int, int)
       
  1047      * @see #getRGB(int, int, int, int, int[], int, int)
       
  1048      */
       
  1049     public void setRGB(int startX, int startY, int w, int h,
       
  1050                         int[] rgbArray, int offset, int scansize) {
       
  1051         int yoff  = offset;
       
  1052         int off;
       
  1053         Object pixel = null;
       
  1054 
       
  1055         for (int y = startY; y < startY+h; y++, yoff+=scansize) {
       
  1056             off = yoff;
       
  1057             for (int x = startX; x < startX+w; x++) {
       
  1058                 pixel = colorModel.getDataElements(rgbArray[off++], pixel);
       
  1059                 raster.setDataElements(x, y, pixel);
       
  1060             }
       
  1061         }
       
  1062     }
       
  1063 
       
  1064 
       
  1065     /**
       
  1066      * Returns the width of the {@code BufferedImage}.
       
  1067      * @return the width of this {@code BufferedImage}
       
  1068      */
       
  1069     public int getWidth() {
       
  1070         return raster.getWidth();
       
  1071     }
       
  1072 
       
  1073     /**
       
  1074      * Returns the height of the {@code BufferedImage}.
       
  1075      * @return the height of this {@code BufferedImage}
       
  1076      */
       
  1077     public int getHeight() {
       
  1078         return raster.getHeight();
       
  1079     }
       
  1080 
       
  1081     /**
       
  1082      * Returns the width of the {@code BufferedImage}.
       
  1083      * @param observer ignored
       
  1084      * @return the width of this {@code BufferedImage}
       
  1085      */
       
  1086     public int getWidth(ImageObserver observer) {
       
  1087         return raster.getWidth();
       
  1088     }
       
  1089 
       
  1090     /**
       
  1091      * Returns the height of the {@code BufferedImage}.
       
  1092      * @param observer ignored
       
  1093      * @return the height of this {@code BufferedImage}
       
  1094      */
       
  1095     public int getHeight(ImageObserver observer) {
       
  1096         return raster.getHeight();
       
  1097     }
       
  1098 
       
  1099     /**
       
  1100      * Returns the object that produces the pixels for the image.
       
  1101      * @return the {@link ImageProducer} that is used to produce the
       
  1102      * pixels for this image.
       
  1103      * @see ImageProducer
       
  1104      */
       
  1105     public ImageProducer getSource() {
       
  1106         if (osis == null) {
       
  1107             if (properties == null) {
       
  1108                 properties = new Hashtable<>();
       
  1109             }
       
  1110             osis = new OffScreenImageSource(this, properties);
       
  1111         }
       
  1112         return osis;
       
  1113     }
       
  1114 
       
  1115 
       
  1116     /**
       
  1117      * Returns a property of the image by name.  Individual property names
       
  1118      * are defined by the various image formats.  If a property is not
       
  1119      * defined for a particular image, this method returns the
       
  1120      * {@code UndefinedProperty} field.  If the properties
       
  1121      * for this image are not yet known, then this method returns
       
  1122      * {@code null} and the {@code ImageObserver} object is
       
  1123      * notified later.  The property name "comment" should be used to
       
  1124      * store an optional comment that can be presented to the user as a
       
  1125      * description of the image, its source, or its author.
       
  1126      * @param name the property name
       
  1127      * @param observer the {@code ImageObserver} that receives
       
  1128      *  notification regarding image information
       
  1129      * @return an {@link Object} that is the property referred to by the
       
  1130      *          specified {@code name} or {@code null} if the
       
  1131      *          properties of this image are not yet known.
       
  1132      * @throws NullPointerException if the property name is null.
       
  1133      * @see ImageObserver
       
  1134      * @see java.awt.Image#UndefinedProperty
       
  1135      */
       
  1136     public Object getProperty(String name, ImageObserver observer) {
       
  1137         return getProperty(name);
       
  1138     }
       
  1139 
       
  1140     /**
       
  1141      * Returns a property of the image by name.
       
  1142      * @param name the property name
       
  1143      * @return an {@code Object} that is the property referred to by
       
  1144      *          the specified {@code name}.
       
  1145      * @throws NullPointerException if the property name is null.
       
  1146      */
       
  1147     public Object getProperty(String name) {
       
  1148         if (name == null) {
       
  1149             throw new NullPointerException("null property name is not allowed");
       
  1150         }
       
  1151         if (properties == null) {
       
  1152             return java.awt.Image.UndefinedProperty;
       
  1153         }
       
  1154         Object o = properties.get(name);
       
  1155         if (o == null) {
       
  1156             o = java.awt.Image.UndefinedProperty;
       
  1157         }
       
  1158         return o;
       
  1159     }
       
  1160 
       
  1161     /**
       
  1162      * This method returns a {@link Graphics2D}, but is here
       
  1163      * for backwards compatibility.  {@link #createGraphics() createGraphics} is more
       
  1164      * convenient, since it is declared to return a
       
  1165      * {@code Graphics2D}.
       
  1166      * @return a {@code Graphics2D}, which can be used to draw into
       
  1167      *          this image.
       
  1168      */
       
  1169     public java.awt.Graphics getGraphics() {
       
  1170         return createGraphics();
       
  1171     }
       
  1172 
       
  1173     /**
       
  1174      * Creates a {@code Graphics2D}, which can be used to draw into
       
  1175      * this {@code BufferedImage}.
       
  1176      * @return a {@code Graphics2D}, used for drawing into this
       
  1177      *          image.
       
  1178      */
       
  1179     public Graphics2D createGraphics() {
       
  1180         GraphicsEnvironment env =
       
  1181             GraphicsEnvironment.getLocalGraphicsEnvironment();
       
  1182         return env.createGraphics(this);
       
  1183     }
       
  1184 
       
  1185     /**
       
  1186      * Returns a subimage defined by a specified rectangular region.
       
  1187      * The returned {@code BufferedImage} shares the same
       
  1188      * data array as the original image.
       
  1189      * @param x the X coordinate of the upper-left corner of the
       
  1190      *          specified rectangular region
       
  1191      * @param y the Y coordinate of the upper-left corner of the
       
  1192      *          specified rectangular region
       
  1193      * @param w the width of the specified rectangular region
       
  1194      * @param h the height of the specified rectangular region
       
  1195      * @return a {@code BufferedImage} that is the subimage of this
       
  1196      *          {@code BufferedImage}.
       
  1197      * @exception RasterFormatException if the specified
       
  1198      * area is not contained within this {@code BufferedImage}.
       
  1199      */
       
  1200     public BufferedImage getSubimage (int x, int y, int w, int h) {
       
  1201         return new BufferedImage (colorModel,
       
  1202                                   raster.createWritableChild(x, y, w, h,
       
  1203                                                              0, 0, null),
       
  1204                                   colorModel.isAlphaPremultiplied(),
       
  1205                                   properties);
       
  1206     }
       
  1207 
       
  1208     /**
       
  1209      * Returns whether or not the alpha has been premultiplied.  It
       
  1210      * returns {@code false} if there is no alpha.
       
  1211      * @return {@code true} if the alpha has been premultiplied;
       
  1212      *          {@code false} otherwise.
       
  1213      */
       
  1214     public boolean isAlphaPremultiplied() {
       
  1215         return colorModel.isAlphaPremultiplied();
       
  1216     }
       
  1217 
       
  1218     /**
       
  1219      * Forces the data to match the state specified in the
       
  1220      * {@code isAlphaPremultiplied} variable.  It may multiply or
       
  1221      * divide the color raster data by alpha, or do nothing if the data is
       
  1222      * in the correct state.
       
  1223      * @param isAlphaPremultiplied {@code true} if the alpha has been
       
  1224      *          premultiplied; {@code false} otherwise.
       
  1225      */
       
  1226     public void coerceData (boolean isAlphaPremultiplied) {
       
  1227         if (colorModel.hasAlpha() &&
       
  1228             colorModel.isAlphaPremultiplied() != isAlphaPremultiplied) {
       
  1229             // Make the color model do the conversion
       
  1230             colorModel = colorModel.coerceData (raster, isAlphaPremultiplied);
       
  1231         }
       
  1232     }
       
  1233 
       
  1234     /**
       
  1235      * Returns a {@code String} representation of this
       
  1236      * {@code BufferedImage} object and its values.
       
  1237      * @return a {@code String} representing this
       
  1238      *          {@code BufferedImage}.
       
  1239      */
       
  1240     public String toString() {
       
  1241         return "BufferedImage@"+Integer.toHexString(hashCode())
       
  1242             +": type = "+imageType
       
  1243             +" "+colorModel+" "+raster;
       
  1244     }
       
  1245 
       
  1246     /**
       
  1247      * Returns a {@link Vector} of {@link RenderedImage} objects that are
       
  1248      * the immediate sources, not the sources of these immediate sources,
       
  1249      * of image data for this {@code BufferedImage}.  This
       
  1250      * method returns {@code null} if the {@code BufferedImage}
       
  1251      * has no information about its immediate sources.  It returns an
       
  1252      * empty {@code Vector} if the {@code BufferedImage} has no
       
  1253      * immediate sources.
       
  1254      * @return a {@code Vector} containing immediate sources of
       
  1255      *          this {@code BufferedImage} object's image date, or
       
  1256      *          {@code null} if this {@code BufferedImage} has
       
  1257      *          no information about its immediate sources, or an empty
       
  1258      *          {@code Vector} if this {@code BufferedImage}
       
  1259      *          has no immediate sources.
       
  1260      */
       
  1261     public Vector<RenderedImage> getSources() {
       
  1262         return null;
       
  1263     }
       
  1264 
       
  1265     /**
       
  1266      * Returns an array of names recognized by
       
  1267      * {@link #getProperty(String) getProperty(String)}
       
  1268      * or {@code null}, if no property names are recognized.
       
  1269      * @return a {@code String} array containing all of the property
       
  1270      *          names that {@code getProperty(String)} recognizes;
       
  1271      *          or {@code null} if no property names are recognized.
       
  1272      */
       
  1273     public String[] getPropertyNames() {
       
  1274         if (properties == null || properties.isEmpty()) {
       
  1275             return null;
       
  1276         }
       
  1277         final Set<String> keys = properties.keySet();
       
  1278         return keys.toArray(new String[keys.size()]);
       
  1279     }
       
  1280 
       
  1281     /**
       
  1282      * Returns the minimum x coordinate of this
       
  1283      * {@code BufferedImage}.  This is always zero.
       
  1284      * @return the minimum x coordinate of this
       
  1285      *          {@code BufferedImage}.
       
  1286      */
       
  1287     public int getMinX() {
       
  1288         return raster.getMinX();
       
  1289     }
       
  1290 
       
  1291     /**
       
  1292      * Returns the minimum y coordinate of this
       
  1293      * {@code BufferedImage}.  This is always zero.
       
  1294      * @return the minimum y coordinate of this
       
  1295      *          {@code BufferedImage}.
       
  1296      */
       
  1297     public int getMinY() {
       
  1298         return raster.getMinY();
       
  1299     }
       
  1300 
       
  1301     /**
       
  1302      * Returns the {@code SampleModel} associated with this
       
  1303      * {@code BufferedImage}.
       
  1304      * @return the {@code SampleModel} of this
       
  1305      *          {@code BufferedImage}.
       
  1306      */
       
  1307     public SampleModel getSampleModel() {
       
  1308         return raster.getSampleModel();
       
  1309     }
       
  1310 
       
  1311     /**
       
  1312      * Returns the number of tiles in the x direction.
       
  1313      * This is always one.
       
  1314      * @return the number of tiles in the x direction.
       
  1315      */
       
  1316     public int getNumXTiles() {
       
  1317         return 1;
       
  1318     }
       
  1319 
       
  1320     /**
       
  1321      * Returns the number of tiles in the y direction.
       
  1322      * This is always one.
       
  1323      * @return the number of tiles in the y direction.
       
  1324      */
       
  1325     public int getNumYTiles() {
       
  1326         return 1;
       
  1327     }
       
  1328 
       
  1329     /**
       
  1330      * Returns the minimum tile index in the x direction.
       
  1331      * This is always zero.
       
  1332      * @return the minimum tile index in the x direction.
       
  1333      */
       
  1334     public int getMinTileX() {
       
  1335         return 0;
       
  1336     }
       
  1337 
       
  1338     /**
       
  1339      * Returns the minimum tile index in the y direction.
       
  1340      * This is always zero.
       
  1341      * @return the minimum tile index in the y direction.
       
  1342      */
       
  1343     public int getMinTileY() {
       
  1344         return 0;
       
  1345     }
       
  1346 
       
  1347     /**
       
  1348      * Returns the tile width in pixels.
       
  1349      * @return the tile width in pixels.
       
  1350      */
       
  1351     public int getTileWidth() {
       
  1352        return raster.getWidth();
       
  1353     }
       
  1354 
       
  1355     /**
       
  1356      * Returns the tile height in pixels.
       
  1357      * @return the tile height in pixels.
       
  1358      */
       
  1359     public int getTileHeight() {
       
  1360        return raster.getHeight();
       
  1361     }
       
  1362 
       
  1363     /**
       
  1364      * Returns the x offset of the tile grid relative to the origin,
       
  1365      * For example, the x coordinate of the location of tile
       
  1366      * (0,&nbsp;0).  This is always zero.
       
  1367      * @return the x offset of the tile grid.
       
  1368      */
       
  1369     public int getTileGridXOffset() {
       
  1370         return raster.getSampleModelTranslateX();
       
  1371     }
       
  1372 
       
  1373     /**
       
  1374      * Returns the y offset of the tile grid relative to the origin,
       
  1375      * For example, the y coordinate of the location of tile
       
  1376      * (0,&nbsp;0).  This is always zero.
       
  1377      * @return the y offset of the tile grid.
       
  1378      */
       
  1379     public int getTileGridYOffset() {
       
  1380         return raster.getSampleModelTranslateY();
       
  1381     }
       
  1382 
       
  1383     /**
       
  1384      * Returns tile ({@code tileX},&nbsp;{@code tileY}).  Note
       
  1385      * that {@code tileX} and {@code tileY} are indices
       
  1386      * into the tile array, not pixel locations.  The {@code Raster}
       
  1387      * that is returned is live, which means that it is updated if the
       
  1388      * image is changed.
       
  1389      * @param tileX the x index of the requested tile in the tile array
       
  1390      * @param tileY the y index of the requested tile in the tile array
       
  1391      * @return a {@code Raster} that is the tile defined by the
       
  1392      *          arguments {@code tileX} and {@code tileY}.
       
  1393      * @exception ArrayIndexOutOfBoundsException if both
       
  1394      *          {@code tileX} and {@code tileY} are not
       
  1395      *          equal to 0
       
  1396      */
       
  1397     public Raster getTile(int tileX, int tileY) {
       
  1398         if (tileX == 0 && tileY == 0) {
       
  1399             return raster;
       
  1400         }
       
  1401         throw new ArrayIndexOutOfBoundsException("BufferedImages only have"+
       
  1402              " one tile with index 0,0");
       
  1403     }
       
  1404 
       
  1405     /**
       
  1406      * Returns the image as one large tile.  The {@code Raster}
       
  1407      * returned is a copy of the image data is not updated if the
       
  1408      * image is changed.
       
  1409      * @return a {@code Raster} that is a copy of the image data.
       
  1410      * @see #setData(Raster)
       
  1411      */
       
  1412     public Raster getData() {
       
  1413 
       
  1414         // REMIND : this allocates a whole new tile if raster is a
       
  1415         // subtile.  (It only copies in the requested area)
       
  1416         // We should do something smarter.
       
  1417         int width = raster.getWidth();
       
  1418         int height = raster.getHeight();
       
  1419         int startX = raster.getMinX();
       
  1420         int startY = raster.getMinY();
       
  1421         WritableRaster wr =
       
  1422            Raster.createWritableRaster(raster.getSampleModel(),
       
  1423                          new Point(raster.getSampleModelTranslateX(),
       
  1424                                    raster.getSampleModelTranslateY()));
       
  1425 
       
  1426         Object tdata = null;
       
  1427 
       
  1428         for (int i = startY; i < startY+height; i++)  {
       
  1429             tdata = raster.getDataElements(startX,i,width,1,tdata);
       
  1430             wr.setDataElements(startX,i,width,1, tdata);
       
  1431         }
       
  1432         return wr;
       
  1433     }
       
  1434 
       
  1435     /**
       
  1436      * Computes and returns an arbitrary region of the
       
  1437      * {@code BufferedImage}.  The {@code Raster} returned is a
       
  1438      * copy of the image data and is not updated if the image is
       
  1439      * changed.
       
  1440      * @param rect the region of the {@code BufferedImage} to be
       
  1441      * returned.
       
  1442      * @return a {@code Raster} that is a copy of the image data of
       
  1443      *          the specified region of the {@code BufferedImage}
       
  1444      * @see #setData(Raster)
       
  1445      */
       
  1446     public Raster getData(Rectangle rect) {
       
  1447         SampleModel sm = raster.getSampleModel();
       
  1448         SampleModel nsm = sm.createCompatibleSampleModel(rect.width,
       
  1449                                                          rect.height);
       
  1450         WritableRaster wr = Raster.createWritableRaster(nsm,
       
  1451                                                   rect.getLocation());
       
  1452         int width = rect.width;
       
  1453         int height = rect.height;
       
  1454         int startX = rect.x;
       
  1455         int startY = rect.y;
       
  1456 
       
  1457         Object tdata = null;
       
  1458 
       
  1459         for (int i = startY; i < startY+height; i++)  {
       
  1460             tdata = raster.getDataElements(startX,i,width,1,tdata);
       
  1461             wr.setDataElements(startX,i,width,1, tdata);
       
  1462         }
       
  1463         return wr;
       
  1464     }
       
  1465 
       
  1466     /**
       
  1467      * Computes an arbitrary rectangular region of the
       
  1468      * {@code BufferedImage} and copies it into a specified
       
  1469      * {@code WritableRaster}.  The region to be computed is
       
  1470      * determined from the bounds of the specified
       
  1471      * {@code WritableRaster}.  The specified
       
  1472      * {@code WritableRaster} must have a
       
  1473      * {@code SampleModel} that is compatible with this image.  If
       
  1474      * {@code outRaster} is {@code null},
       
  1475      * an appropriate {@code WritableRaster} is created.
       
  1476      * @param outRaster a {@code WritableRaster} to hold the returned
       
  1477      *          part of the image, or {@code null}
       
  1478      * @return a reference to the supplied or created
       
  1479      *          {@code WritableRaster}.
       
  1480      */
       
  1481     public WritableRaster copyData(WritableRaster outRaster) {
       
  1482         if (outRaster == null) {
       
  1483             return (WritableRaster) getData();
       
  1484         }
       
  1485         int width = outRaster.getWidth();
       
  1486         int height = outRaster.getHeight();
       
  1487         int startX = outRaster.getMinX();
       
  1488         int startY = outRaster.getMinY();
       
  1489 
       
  1490         Object tdata = null;
       
  1491 
       
  1492         for (int i = startY; i < startY+height; i++)  {
       
  1493             tdata = raster.getDataElements(startX,i,width,1,tdata);
       
  1494             outRaster.setDataElements(startX,i,width,1, tdata);
       
  1495         }
       
  1496 
       
  1497         return outRaster;
       
  1498     }
       
  1499 
       
  1500   /**
       
  1501      * Sets a rectangular region of the image to the contents of the
       
  1502      * specified {@code Raster r}, which is
       
  1503      * assumed to be in the same coordinate space as the
       
  1504      * {@code BufferedImage}. The operation is clipped to the bounds
       
  1505      * of the {@code BufferedImage}.
       
  1506      * @param r the specified {@code Raster}
       
  1507      * @see #getData
       
  1508      * @see #getData(Rectangle)
       
  1509     */
       
  1510     public void setData(Raster r) {
       
  1511         int width = r.getWidth();
       
  1512         int height = r.getHeight();
       
  1513         int startX = r.getMinX();
       
  1514         int startY = r.getMinY();
       
  1515 
       
  1516         int[] tdata = null;
       
  1517 
       
  1518         // Clip to the current Raster
       
  1519         Rectangle rclip = new Rectangle(startX, startY, width, height);
       
  1520         Rectangle bclip = new Rectangle(0, 0, raster.width, raster.height);
       
  1521         Rectangle intersect = rclip.intersection(bclip);
       
  1522         if (intersect.isEmpty()) {
       
  1523             return;
       
  1524         }
       
  1525         width = intersect.width;
       
  1526         height = intersect.height;
       
  1527         startX = intersect.x;
       
  1528         startY = intersect.y;
       
  1529 
       
  1530         // remind use get/setDataElements for speed if Rasters are
       
  1531         // compatible
       
  1532         for (int i = startY; i < startY+height; i++)  {
       
  1533             tdata = r.getPixels(startX,i,width,1,tdata);
       
  1534             raster.setPixels(startX,i,width,1, tdata);
       
  1535         }
       
  1536     }
       
  1537 
       
  1538 
       
  1539   /**
       
  1540    * Adds a tile observer.  If the observer is already present,
       
  1541    * it receives multiple notifications.
       
  1542    * @param to the specified {@link TileObserver}
       
  1543    */
       
  1544     public void addTileObserver (TileObserver to) {
       
  1545     }
       
  1546 
       
  1547   /**
       
  1548    * Removes a tile observer.  If the observer was not registered,
       
  1549    * nothing happens.  If the observer was registered for multiple
       
  1550    * notifications, it is now registered for one fewer notification.
       
  1551    * @param to the specified {@code TileObserver}.
       
  1552    */
       
  1553     public void removeTileObserver (TileObserver to) {
       
  1554     }
       
  1555 
       
  1556     /**
       
  1557      * Returns whether or not a tile is currently checked out for writing.
       
  1558      * @param tileX the x index of the tile.
       
  1559      * @param tileY the y index of the tile.
       
  1560      * @return {@code true} if the tile specified by the specified
       
  1561      *          indices is checked out for writing; {@code false}
       
  1562      *          otherwise.
       
  1563      * @exception ArrayIndexOutOfBoundsException if both
       
  1564      *          {@code tileX} and {@code tileY} are not equal
       
  1565      *          to 0
       
  1566      */
       
  1567     public boolean isTileWritable (int tileX, int tileY) {
       
  1568         if (tileX == 0 && tileY == 0) {
       
  1569             return true;
       
  1570         }
       
  1571         throw new IllegalArgumentException("Only 1 tile in image");
       
  1572     }
       
  1573 
       
  1574     /**
       
  1575      * Returns an array of {@link Point} objects indicating which tiles
       
  1576      * are checked out for writing.  Returns {@code null} if none are
       
  1577      * checked out.
       
  1578      * @return a {@code Point} array that indicates the tiles that
       
  1579      *          are checked out for writing, or {@code null} if no
       
  1580      *          tiles are checked out for writing.
       
  1581      */
       
  1582     public Point[] getWritableTileIndices() {
       
  1583         Point[] p = new Point[1];
       
  1584         p[0] = new Point(0, 0);
       
  1585 
       
  1586         return p;
       
  1587     }
       
  1588 
       
  1589     /**
       
  1590      * Returns whether or not any tile is checked out for writing.
       
  1591      * Semantically equivalent to
       
  1592      * <pre>
       
  1593      * (getWritableTileIndices() != null).
       
  1594      * </pre>
       
  1595      * @return {@code true} if any tile is checked out for writing;
       
  1596      *          {@code false} otherwise.
       
  1597      */
       
  1598     public boolean hasTileWriters () {
       
  1599         return true;
       
  1600     }
       
  1601 
       
  1602   /**
       
  1603    * Checks out a tile for writing.  All registered
       
  1604    * {@code TileObservers} are notified when a tile goes from having
       
  1605    * no writers to having one writer.
       
  1606    * @param tileX the x index of the tile
       
  1607    * @param tileY the y index of the tile
       
  1608    * @return a {@code WritableRaster} that is the tile, indicated by
       
  1609    *            the specified indices, to be checked out for writing.
       
  1610    */
       
  1611     public WritableRaster getWritableTile (int tileX, int tileY) {
       
  1612         return raster;
       
  1613     }
       
  1614 
       
  1615   /**
       
  1616    * Relinquishes permission to write to a tile.  If the caller
       
  1617    * continues to write to the tile, the results are undefined.
       
  1618    * Calls to this method should only appear in matching pairs
       
  1619    * with calls to {@link #getWritableTile(int, int) getWritableTile(int, int)}.  Any other leads
       
  1620    * to undefined results.  All registered {@code TileObservers}
       
  1621    * are notified when a tile goes from having one writer to having no
       
  1622    * writers.
       
  1623    * @param tileX the x index of the tile
       
  1624    * @param tileY the y index of the tile
       
  1625    */
       
  1626     public void releaseWritableTile (int tileX, int tileY) {
       
  1627     }
       
  1628 
       
  1629     /**
       
  1630      * Returns the transparency.  Returns either OPAQUE, BITMASK,
       
  1631      * or TRANSLUCENT.
       
  1632      * @return the transparency of this {@code BufferedImage}.
       
  1633      * @see Transparency#OPAQUE
       
  1634      * @see Transparency#BITMASK
       
  1635      * @see Transparency#TRANSLUCENT
       
  1636      * @since 1.5
       
  1637      */
       
  1638     public int getTransparency() {
       
  1639         return colorModel.getTransparency();
       
  1640     }
       
  1641 }