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 
initIDs()284     private static native void initIDs();
285     static {
ColorModel.loadLibraries()286         ColorModel.loadLibraries();
initIDs()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      */
BufferedImage(int width, int height, int imageType)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      */
BufferedImage(int width, int height, int imageType, IndexColorModel cm)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  */
BufferedImage(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable<?,?> properties)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 
isStandard(ColorModel cm, WritableRaster wr)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      */
getType()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      */
getColorModel()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      */
getRaster()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      */
getAlphaRaster()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      */
getRGB(int x, int y)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      */
getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)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      */
setRGB(int x, int y, int rgb)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      */
setRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)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      */
getWidth()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      */
getHeight()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      */
getWidth(ImageObserver observer)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      */
getHeight(ImageObserver observer)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      */
getSource()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      */
getProperty(String name, ImageObserver observer)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      */
getProperty(String name)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      */
getGraphics()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      */
createGraphics()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      */
getSubimage(int x, int y, int w, int h)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      */
isAlphaPremultiplied()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      */
coerceData(boolean isAlphaPremultiplied)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      */
toString()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      */
getSources()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      */
getPropertyNames()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      */
getMinX()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      */
getMinY()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      */
getSampleModel()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      */
getNumXTiles()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      */
getNumYTiles()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      */
getMinTileX()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      */
getMinTileY()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      */
getTileWidth()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      */
getTileHeight()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      */
getTileGridXOffset()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      */
getTileGridYOffset()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      */
getTile(int tileX, int tileY)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      */
getData()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      */
getData(Rectangle rect)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      */
copyData(WritableRaster outRaster)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     */
setData(Raster r)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    */
addTileObserver(TileObserver to)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    */
removeTileObserver(TileObserver to)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      */
isTileWritable(int tileX, int tileY)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      */
getWritableTileIndices()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      */
hasTileWriters()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    */
getWritableTile(int tileX, int tileY)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    */
releaseWritableTile(int tileX, int tileY)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      */
getTransparency()1638     public int getTransparency() {
1639         return colorModel.getTransparency();
1640     }
1641 }
1642