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
26package java.awt.image;
27
28import java.awt.Graphics2D;
29import java.awt.GraphicsEnvironment;
30import java.awt.Point;
31import java.awt.Rectangle;
32import java.awt.Transparency;
33import java.awt.color.ColorSpace;
34import java.security.AccessController;
35import java.security.PrivilegedAction;
36import java.util.Hashtable;
37import java.util.Set;
38import java.util.Vector;
39
40import sun.awt.image.ByteComponentRaster;
41import sun.awt.image.BytePackedRaster;
42import sun.awt.image.IntegerComponentRaster;
43import sun.awt.image.OffScreenImageSource;
44import 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 */
68public 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}
1642