1/*
2 * Copyright (c) 2003, 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 com.sun.imageio.plugins.bmp;
27
28import java.awt.Point;
29import java.awt.Rectangle;
30import java.awt.Transparency;
31import java.awt.color.ColorSpace;
32import java.awt.color.ICC_ColorSpace;
33import java.awt.color.ICC_Profile;
34import java.awt.image.BufferedImage;
35import java.awt.image.ColorModel;
36import java.awt.image.ComponentColorModel;
37import java.awt.image.ComponentSampleModel;
38import java.awt.image.DataBuffer;
39import java.awt.image.DataBufferByte;
40import java.awt.image.DataBufferInt;
41import java.awt.image.DataBufferUShort;
42import java.awt.image.DirectColorModel;
43import java.awt.image.IndexColorModel;
44import java.awt.image.MultiPixelPackedSampleModel;
45import java.awt.image.PixelInterleavedSampleModel;
46import java.awt.image.Raster;
47import java.awt.image.SampleModel;
48import java.awt.image.SinglePixelPackedSampleModel;
49import java.awt.image.WritableRaster;
50
51import javax.imageio.IIOException;
52import javax.imageio.ImageIO;
53import javax.imageio.ImageReader;
54import javax.imageio.ImageReadParam;
55import javax.imageio.ImageTypeSpecifier;
56import javax.imageio.metadata.IIOMetadata;
57import javax.imageio.spi.ImageReaderSpi;
58import javax.imageio.stream.ImageInputStream;
59import javax.imageio.event.IIOReadProgressListener;
60import javax.imageio.event.IIOReadUpdateListener;
61import javax.imageio.event.IIOReadWarningListener;
62
63import java.io.*;
64import java.nio.*;
65import java.security.AccessController;
66import java.security.PrivilegedAction;
67import java.util.ArrayList;
68import java.util.Iterator;
69import java.util.StringTokenizer;
70
71import com.sun.imageio.plugins.common.ImageUtil;
72import com.sun.imageio.plugins.common.I18N;
73
74/** This class is the Java Image IO plugin reader for BMP images.
75 *  It may subsample the image, clip the image, select sub-bands,
76 *  and shift the decoded image origin if the proper decoding parameter
77 *  are set in the provided {@code ImageReadParam}.
78 *
79 *  This class supports Microsoft Windows Bitmap Version 3-5,
80 *  as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
81 */
82public class BMPImageReader extends ImageReader implements BMPConstants {
83    // BMP Image types
84    private static final int VERSION_2_1_BIT = 0;
85    private static final int VERSION_2_4_BIT = 1;
86    private static final int VERSION_2_8_BIT = 2;
87    private static final int VERSION_2_24_BIT = 3;
88
89    private static final int VERSION_3_1_BIT = 4;
90    private static final int VERSION_3_4_BIT = 5;
91    private static final int VERSION_3_8_BIT = 6;
92    private static final int VERSION_3_24_BIT = 7;
93
94    private static final int VERSION_3_NT_16_BIT = 8;
95    private static final int VERSION_3_NT_32_BIT = 9;
96
97    private static final int VERSION_4_1_BIT = 10;
98    private static final int VERSION_4_4_BIT = 11;
99    private static final int VERSION_4_8_BIT = 12;
100    private static final int VERSION_4_16_BIT = 13;
101    private static final int VERSION_4_24_BIT = 14;
102    private static final int VERSION_4_32_BIT = 15;
103
104    private static final int VERSION_3_XP_EMBEDDED = 16;
105    private static final int VERSION_4_XP_EMBEDDED = 17;
106    private static final int VERSION_5_XP_EMBEDDED = 18;
107
108    // BMP variables
109    private long bitmapFileSize;
110    private long bitmapOffset;
111    private long bitmapStart;
112    private long compression;
113    private long imageSize;
114    private byte palette[];
115    private int imageType;
116    private int numBands;
117    private boolean isBottomUp;
118    private int bitsPerPixel;
119    private int redMask, greenMask, blueMask, alphaMask;
120
121    private SampleModel sampleModel, originalSampleModel;
122    private ColorModel colorModel, originalColorModel;
123
124    /** The input stream where reads from */
125    private ImageInputStream iis = null;
126
127    /** Indicates whether the header is read. */
128    private boolean gotHeader = false;
129
130    /** The original image width. */
131    private int width;
132
133    /** The original image height. */
134    private int height;
135
136    /** The destination region. */
137    private Rectangle destinationRegion;
138
139    /** The source region. */
140    private Rectangle sourceRegion;
141
142    /** The metadata from the stream. */
143    private BMPMetadata metadata;
144
145    /** The destination image. */
146    private BufferedImage bi;
147
148    /** Indicates whether subsampled, subregion is required, and offset is
149     *  defined
150     */
151    private boolean noTransform = true;
152
153    /** Indicates whether subband is selected. */
154    private boolean seleBand = false;
155
156    /** The scaling factors. */
157    private int scaleX, scaleY;
158
159    /** source and destination bands. */
160    private int[] sourceBands, destBands;
161
162    /** Constructs {@code BMPImageReader} from the provided
163     *  {@code ImageReaderSpi}.
164     */
165    public BMPImageReader(ImageReaderSpi originator) {
166        super(originator);
167    }
168
169    /** Overrides the method defined in the superclass. */
170    public void setInput(Object input,
171                         boolean seekForwardOnly,
172                         boolean ignoreMetadata) {
173        super.setInput(input, seekForwardOnly, ignoreMetadata);
174        iis = (ImageInputStream) input; // Always works
175        if(iis != null)
176            iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
177        resetHeaderInfo();
178    }
179
180    /** Overrides the method defined in the superclass. */
181    public int getNumImages(boolean allowSearch) throws IOException {
182        if (iis == null) {
183            throw new IllegalStateException(I18N.getString("GetNumImages0"));
184        }
185        if (seekForwardOnly && allowSearch) {
186            throw new IllegalStateException(I18N.getString("GetNumImages1"));
187        }
188        return 1;
189    }
190
191    @Override
192    public int getWidth(int imageIndex) throws IOException {
193        checkIndex(imageIndex);
194        try {
195            readHeader();
196        } catch (IllegalArgumentException e) {
197            throw new IIOException(I18N.getString("BMPImageReader6"), e);
198        }
199        return width;
200    }
201
202    public int getHeight(int imageIndex) throws IOException {
203        checkIndex(imageIndex);
204        try {
205            readHeader();
206        } catch (IllegalArgumentException e) {
207            throw new IIOException(I18N.getString("BMPImageReader6"), e);
208        }
209        return height;
210    }
211
212    private void checkIndex(int imageIndex) {
213        if (imageIndex != 0) {
214            throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
215        }
216    }
217
218    /**
219     * Process the image header.
220     *
221     * @exception IllegalStateException if source stream is not set.
222     *
223     * @exception IOException if image stream is corrupted.
224     *
225     * @exception IllegalArgumentException if the image stream does not contain
226     *             a BMP image, or if a sample model instance to describe the
227     *             image can not be created.
228     */
229    protected void readHeader() throws IOException, IllegalArgumentException {
230        if (gotHeader)
231            return;
232
233        if (iis == null) {
234            throw new IllegalStateException("Input source not set!");
235        }
236        int profileData = 0, profileSize = 0;
237
238        this.metadata = new BMPMetadata();
239        iis.mark();
240
241        // read and check the magic marker
242        byte[] marker = new byte[2];
243        iis.read(marker);
244        if (marker[0] != 0x42 || marker[1] != 0x4d)
245            throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
246
247        // Read file size
248        bitmapFileSize = iis.readUnsignedInt();
249        // skip the two reserved fields
250        iis.skipBytes(4);
251
252        // Offset to the bitmap from the beginning
253        bitmapOffset = iis.readUnsignedInt();
254        // End File Header
255
256        // Start BitmapCoreHeader
257        long size = iis.readUnsignedInt();
258
259        if (size == 12) {
260            width = iis.readShort();
261            height = iis.readShort();
262        } else {
263            width = iis.readInt();
264            height = iis.readInt();
265        }
266
267        metadata.width = width;
268        metadata.height = height;
269
270        int planes = iis.readUnsignedShort();
271        bitsPerPixel = iis.readUnsignedShort();
272
273        //metadata.colorPlane = planes;
274        metadata.bitsPerPixel = (short)bitsPerPixel;
275
276        // As BMP always has 3 rgb bands, except for Version 5,
277        // which is bgra
278        numBands = 3;
279
280        if (size == 12) {
281            // Windows 2.x and OS/2 1.x
282            metadata.bmpVersion = VERSION_2;
283
284            // Classify the image type
285            if (bitsPerPixel == 1) {
286                imageType = VERSION_2_1_BIT;
287            } else if (bitsPerPixel == 4) {
288                imageType = VERSION_2_4_BIT;
289            } else if (bitsPerPixel == 8) {
290                imageType = VERSION_2_8_BIT;
291            } else if (bitsPerPixel == 24) {
292                imageType = VERSION_2_24_BIT;
293            } else {
294                throw new IIOException(I18N.getString("BMPImageReader8"));
295            }
296
297            // Read in the palette
298            int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
299            int sizeOfPalette = numberOfEntries*3;
300            palette = new byte[sizeOfPalette];
301            iis.readFully(palette, 0, sizeOfPalette);
302            metadata.palette = palette;
303            metadata.paletteSize = numberOfEntries;
304        } else {
305            compression = iis.readUnsignedInt();
306            imageSize = iis.readUnsignedInt();
307            long xPelsPerMeter = iis.readInt();
308            long yPelsPerMeter = iis.readInt();
309            long colorsUsed = iis.readUnsignedInt();
310            long colorsImportant = iis.readUnsignedInt();
311
312            metadata.compression = (int)compression;
313            metadata.xPixelsPerMeter = (int)xPelsPerMeter;
314            metadata.yPixelsPerMeter = (int)yPelsPerMeter;
315            metadata.colorsUsed = (int)colorsUsed;
316            metadata.colorsImportant = (int)colorsImportant;
317
318            if (size == 40) {
319                // Windows 3.x and Windows NT
320                switch((int)compression) {
321
322                case BI_JPEG:
323                case BI_PNG:
324                    metadata.bmpVersion = VERSION_3;
325                    imageType = VERSION_3_XP_EMBEDDED;
326                    break;
327
328                case BI_RGB:  // No compression
329                case BI_RLE8:  // 8-bit RLE compression
330                case BI_RLE4:  // 4-bit RLE compression
331
332                    // Read in the palette
333                    if (bitmapOffset < (size + 14)) {
334                        throw new IIOException(I18N.getString("BMPImageReader7"));
335                    }
336                    int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
337                    int sizeOfPalette = numberOfEntries * 4;
338                    palette = new byte[sizeOfPalette];
339                    iis.readFully(palette, 0, sizeOfPalette);
340
341                    metadata.palette = palette;
342                    metadata.paletteSize = numberOfEntries;
343
344                    if (bitsPerPixel == 1) {
345                        imageType = VERSION_3_1_BIT;
346                    } else if (bitsPerPixel == 4) {
347                        imageType = VERSION_3_4_BIT;
348                    } else if (bitsPerPixel == 8) {
349                        imageType = VERSION_3_8_BIT;
350                    } else if (bitsPerPixel == 24) {
351                        imageType = VERSION_3_24_BIT;
352                    } else if (bitsPerPixel == 16) {
353                        imageType = VERSION_3_NT_16_BIT;
354
355                        redMask = 0x7C00;
356                        greenMask = 0x3E0;
357                        blueMask =  (1 << 5) - 1;// 0x1F;
358                        metadata.redMask = redMask;
359                        metadata.greenMask = greenMask;
360                        metadata.blueMask = blueMask;
361                    } else if (bitsPerPixel == 32) {
362                        imageType = VERSION_3_NT_32_BIT;
363                        redMask   = 0x00FF0000;
364                        greenMask = 0x0000FF00;
365                        blueMask  = 0x000000FF;
366                        metadata.redMask = redMask;
367                        metadata.greenMask = greenMask;
368                        metadata.blueMask = blueMask;
369                    } else {
370                        throw new
371                            IIOException(I18N.getString("BMPImageReader8"));
372                    }
373
374                    metadata.bmpVersion = VERSION_3;
375                    break;
376
377                case BI_BITFIELDS:
378
379                    if (bitsPerPixel == 16) {
380                        imageType = VERSION_3_NT_16_BIT;
381                    } else if (bitsPerPixel == 32) {
382                        imageType = VERSION_3_NT_32_BIT;
383                    } else {
384                        throw new
385                            IIOException(I18N.getString("BMPImageReader8"));
386                    }
387
388                    // BitsField encoding
389                    redMask = (int)iis.readUnsignedInt();
390                    greenMask = (int)iis.readUnsignedInt();
391                    blueMask = (int)iis.readUnsignedInt();
392                    metadata.redMask = redMask;
393                    metadata.greenMask = greenMask;
394                    metadata.blueMask = blueMask;
395
396                    if (colorsUsed != 0) {
397                        // there is a palette
398                        sizeOfPalette = (int)colorsUsed*4;
399                        palette = new byte[sizeOfPalette];
400                        iis.readFully(palette, 0, sizeOfPalette);
401
402                        metadata.palette = palette;
403                        metadata.paletteSize = (int)colorsUsed;
404                    }
405                    metadata.bmpVersion = VERSION_3_NT;
406
407                    break;
408                default:
409                    throw new
410                        IIOException(I18N.getString("BMPImageReader2"));
411                }
412            } else if (size == 108 || size == 124) {
413                // Windows 4.x BMP
414                if (size == 108)
415                    metadata.bmpVersion = VERSION_4;
416                else if (size == 124)
417                    metadata.bmpVersion = VERSION_5;
418
419                // rgb masks, valid only if comp is BI_BITFIELDS
420                redMask = (int)iis.readUnsignedInt();
421                greenMask = (int)iis.readUnsignedInt();
422                blueMask = (int)iis.readUnsignedInt();
423                // Only supported for 32bpp BI_RGB argb
424                alphaMask = (int)iis.readUnsignedInt();
425                long csType = iis.readUnsignedInt();
426                int redX = iis.readInt();
427                int redY = iis.readInt();
428                int redZ = iis.readInt();
429                int greenX = iis.readInt();
430                int greenY = iis.readInt();
431                int greenZ = iis.readInt();
432                int blueX = iis.readInt();
433                int blueY = iis.readInt();
434                int blueZ = iis.readInt();
435                long gammaRed = iis.readUnsignedInt();
436                long gammaGreen = iis.readUnsignedInt();
437                long gammaBlue = iis.readUnsignedInt();
438
439                if (size == 124) {
440                    metadata.intent = iis.readInt();
441                    profileData = iis.readInt();
442                    profileSize = iis.readInt();
443                    iis.skipBytes(4);
444                }
445
446                metadata.colorSpace = (int)csType;
447
448                if (csType == LCS_CALIBRATED_RGB) {
449                    // All the new fields are valid only for this case
450                    metadata.redX = redX;
451                    metadata.redY = redY;
452                    metadata.redZ = redZ;
453                    metadata.greenX = greenX;
454                    metadata.greenY = greenY;
455                    metadata.greenZ = greenZ;
456                    metadata.blueX = blueX;
457                    metadata.blueY = blueY;
458                    metadata.blueZ = blueZ;
459                    metadata.gammaRed = (int)gammaRed;
460                    metadata.gammaGreen = (int)gammaGreen;
461                    metadata.gammaBlue = (int)gammaBlue;
462                }
463
464                // Read in the palette
465                int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
466                int sizeOfPalette = numberOfEntries*4;
467                palette = new byte[sizeOfPalette];
468                iis.readFully(palette, 0, sizeOfPalette);
469                metadata.palette = palette;
470                metadata.paletteSize = numberOfEntries;
471
472                switch ((int)compression) {
473                case BI_JPEG:
474                case BI_PNG:
475                    if (size == 108) {
476                        imageType = VERSION_4_XP_EMBEDDED;
477                    } else if (size == 124) {
478                        imageType = VERSION_5_XP_EMBEDDED;
479                    }
480                    break;
481                default:
482                    if (bitsPerPixel == 1) {
483                        imageType = VERSION_4_1_BIT;
484                    } else if (bitsPerPixel == 4) {
485                        imageType = VERSION_4_4_BIT;
486                    } else if (bitsPerPixel == 8) {
487                        imageType = VERSION_4_8_BIT;
488                    } else if (bitsPerPixel == 16) {
489                        imageType = VERSION_4_16_BIT;
490                        if ((int)compression == BI_RGB) {
491                            redMask = 0x7C00;
492                            greenMask = 0x3E0;
493                            blueMask = 0x1F;
494                        }
495                    } else if (bitsPerPixel == 24) {
496                        imageType = VERSION_4_24_BIT;
497                    } else if (bitsPerPixel == 32) {
498                        imageType = VERSION_4_32_BIT;
499                        if ((int)compression == BI_RGB) {
500                            redMask   = 0x00FF0000;
501                            greenMask = 0x0000FF00;
502                            blueMask  = 0x000000FF;
503                        }
504                    } else {
505                        throw new
506                            IIOException(I18N.getString("BMPImageReader8"));
507                    }
508
509                    metadata.redMask = redMask;
510                    metadata.greenMask = greenMask;
511                    metadata.blueMask = blueMask;
512                    metadata.alphaMask = alphaMask;
513                }
514            } else {
515                throw new
516                    IIOException(I18N.getString("BMPImageReader3"));
517            }
518        }
519
520        if (height > 0) {
521            // bottom up image
522            isBottomUp = true;
523        } else {
524            // top down image
525            isBottomUp = false;
526            height = Math.abs(height);
527        }
528
529        // Reset Image Layout so there's only one tile.
530        //Define the color space
531        ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
532        if (metadata.colorSpace == PROFILE_LINKED ||
533            metadata.colorSpace == PROFILE_EMBEDDED) {
534
535            iis.mark();
536            iis.skipBytes(profileData - size);
537            byte[] profile = new byte[profileSize];
538            iis.readFully(profile, 0, profileSize);
539            iis.reset();
540
541            try {
542                if (metadata.colorSpace == PROFILE_LINKED &&
543                    isLinkedProfileAllowed() &&
544                    !isUncOrDevicePath(profile))
545                {
546                    String path = new String(profile, "windows-1252");
547
548                    colorSpace =
549                        new ICC_ColorSpace(ICC_Profile.getInstance(path));
550                } else {
551                    colorSpace =
552                        new ICC_ColorSpace(ICC_Profile.getInstance(profile));
553                }
554            } catch (Exception e) {
555                colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
556            }
557        }
558
559        if (bitsPerPixel == 0 ||
560            compression == BI_JPEG || compression == BI_PNG )
561        {
562            // the colorModel and sampleModel will be initialzed
563            // by the  reader of embedded image
564            colorModel = null;
565            sampleModel = null;
566        } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
567            // When number of bitsPerPixel is <= 8, we use IndexColorModel.
568            numBands = 1;
569
570            if (bitsPerPixel == 8) {
571                int[] bandOffsets = new int[numBands];
572                for (int i = 0; i < numBands; i++) {
573                    bandOffsets[i] = numBands -1 -i;
574                }
575                sampleModel =
576                    new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
577                                                    width, height,
578                                                    numBands,
579                                                    numBands * width,
580                                                    bandOffsets);
581            } else {
582                // 1 and 4 bit pixels can be stored in a packed format.
583                sampleModel =
584                    new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
585                                                    width, height,
586                                                    bitsPerPixel);
587            }
588
589            // Create IndexColorModel from the palette.
590            byte r[], g[], b[];
591            if (imageType == VERSION_2_1_BIT ||
592                imageType == VERSION_2_4_BIT ||
593                imageType == VERSION_2_8_BIT) {
594
595
596                size = palette.length/3;
597
598                if (size > 256) {
599                    size = 256;
600                }
601
602                int off;
603                r = new byte[(int)size];
604                g = new byte[(int)size];
605                b = new byte[(int)size];
606                for (int i=0; i<(int)size; i++) {
607                    off = 3 * i;
608                    b[i] = palette[off];
609                    g[i] = palette[off+1];
610                    r[i] = palette[off+2];
611                }
612            } else {
613                size = palette.length/4;
614
615                if (size > 256) {
616                    size = 256;
617                }
618
619                int off;
620                r = new byte[(int)size];
621                g = new byte[(int)size];
622                b = new byte[(int)size];
623                for (int i=0; i<size; i++) {
624                    off = 4 * i;
625                    b[i] = palette[off];
626                    g[i] = palette[off+1];
627                    r[i] = palette[off+2];
628                }
629            }
630
631            if (ImageUtil.isIndicesForGrayscale(r, g, b))
632                colorModel =
633                    ImageUtil.createColorModel(null, sampleModel);
634            else
635                colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
636        } else if (bitsPerPixel == 16) {
637            numBands = 3;
638            sampleModel =
639                new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
640                                                 width, height,
641                                                 new int[] {redMask, greenMask, blueMask});
642
643            colorModel =
644                new DirectColorModel(colorSpace,
645                                     16, redMask, greenMask, blueMask, 0,
646                                     false, DataBuffer.TYPE_USHORT);
647
648        } else if (bitsPerPixel == 32) {
649            numBands = alphaMask == 0 ? 3 : 4;
650
651            // The number of bands in the SampleModel is determined by
652            // the length of the mask array passed in.
653            int[] bitMasks = numBands == 3 ?
654                new int[] {redMask, greenMask, blueMask} :
655                new int[] {redMask, greenMask, blueMask, alphaMask};
656
657                sampleModel =
658                    new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
659                                                     width, height,
660                                                     bitMasks);
661
662                colorModel =
663                    new DirectColorModel(colorSpace,
664                                         32, redMask, greenMask, blueMask, alphaMask,
665                                         false, DataBuffer.TYPE_INT);
666        } else {
667            numBands = 3;
668            // Create SampleModel
669            int[] bandOffsets = new int[numBands];
670            for (int i = 0; i < numBands; i++) {
671                bandOffsets[i] = numBands -1 -i;
672            }
673
674            sampleModel =
675                new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
676                                                width, height,
677                                                numBands,
678                                                numBands * width,
679                                                bandOffsets);
680
681            colorModel =
682                ImageUtil.createColorModel(colorSpace, sampleModel);
683        }
684
685        originalSampleModel = sampleModel;
686        originalColorModel = colorModel;
687
688        // Reset to the start of bitmap; then jump to the
689        //start of image data
690        iis.reset();
691        iis.skipBytes(bitmapOffset);
692        bitmapStart = iis.getStreamPosition();
693
694        gotHeader = true;
695    }
696
697    public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
698      throws IOException {
699        checkIndex(imageIndex);
700        try {
701            readHeader();
702        } catch (IllegalArgumentException e) {
703            throw new IIOException(I18N.getString("BMPImageReader6"), e);
704        }
705        ArrayList<ImageTypeSpecifier> list = new ArrayList<>(1);
706        list.add(new ImageTypeSpecifier(originalColorModel,
707                                        originalSampleModel));
708        return list.iterator();
709    }
710
711    public ImageReadParam getDefaultReadParam() {
712        return new ImageReadParam();
713    }
714
715    public IIOMetadata getImageMetadata(int imageIndex)
716      throws IOException {
717        checkIndex(imageIndex);
718        if (metadata == null) {
719            try {
720                readHeader();
721            } catch (IllegalArgumentException e) {
722                throw new IIOException(I18N.getString("BMPImageReader6"), e);
723            }
724        }
725        return metadata;
726    }
727
728    public IIOMetadata getStreamMetadata() throws IOException {
729        return null;
730    }
731
732    public boolean isRandomAccessEasy(int imageIndex) throws IOException {
733        checkIndex(imageIndex);
734        try {
735            readHeader();
736        } catch (IllegalArgumentException e) {
737            throw new IIOException(I18N.getString("BMPImageReader6"), e);
738        }
739        return metadata.compression == BI_RGB;
740    }
741
742    public BufferedImage read(int imageIndex, ImageReadParam param)
743        throws IOException {
744
745        if (iis == null) {
746            throw new IllegalStateException(I18N.getString("BMPImageReader5"));
747        }
748
749        checkIndex(imageIndex);
750        clearAbortRequest();
751        processImageStarted(imageIndex);
752        if (abortRequested()) {
753            processReadAborted();
754            return bi;
755        }
756
757        if (param == null)
758            param = getDefaultReadParam();
759
760        //read header
761        try {
762            readHeader();
763        } catch (IllegalArgumentException e) {
764            throw new IIOException(I18N.getString("BMPImageReader6"), e);
765        }
766
767        sourceRegion = new Rectangle(0, 0, 0, 0);
768        destinationRegion = new Rectangle(0, 0, 0, 0);
769
770        computeRegions(param, this.width, this.height,
771                       param.getDestination(),
772                       sourceRegion,
773                       destinationRegion);
774
775        scaleX = param.getSourceXSubsampling();
776        scaleY = param.getSourceYSubsampling();
777
778        // If the destination band is set used it
779        sourceBands = param.getSourceBands();
780        destBands = param.getDestinationBands();
781
782        seleBand = (sourceBands != null) && (destBands != null);
783        noTransform =
784            destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
785            seleBand;
786
787        if (!seleBand) {
788            sourceBands = new int[numBands];
789            destBands = new int[numBands];
790            for (int i = 0; i < numBands; i++)
791                destBands[i] = sourceBands[i] = i;
792        }
793
794        // If the destination is provided, then use it.  Otherwise, create new one
795        bi = param.getDestination();
796
797        // Get the image data.
798        WritableRaster raster = null;
799
800        if (bi == null) {
801            if (sampleModel != null && colorModel != null) {
802                sampleModel =
803                    sampleModel.createCompatibleSampleModel(destinationRegion.x +
804                                                            destinationRegion.width,
805                                                            destinationRegion.y +
806                                                            destinationRegion.height);
807                if (seleBand)
808                    sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
809                raster = Raster.createWritableRaster(sampleModel, new Point());
810                bi = new BufferedImage(colorModel, raster, false, null);
811            }
812        } else {
813            raster = bi.getWritableTile(0, 0);
814            sampleModel = bi.getSampleModel();
815            colorModel = bi.getColorModel();
816
817            noTransform &=  destinationRegion.equals(raster.getBounds());
818        }
819
820        byte bdata[] = null; // buffer for byte data
821        short sdata[] = null; // buffer for short data
822        int idata[] = null; // buffer for int data
823
824        // the sampleModel can be null in case of embedded image
825        if (sampleModel != null) {
826            if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
827                bdata = ((DataBufferByte)raster.getDataBuffer()).getData();
828            else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
829                sdata = ((DataBufferUShort)raster.getDataBuffer()).getData();
830            else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
831                idata = ((DataBufferInt)raster.getDataBuffer()).getData();
832        }
833
834        iis.seek(bitmapStart);
835
836        // There should only be one tile.
837        switch(imageType) {
838
839        case VERSION_2_1_BIT:
840            // no compression
841            read1Bit(bdata);
842            break;
843
844        case VERSION_2_4_BIT:
845            // no compression
846            read4Bit(bdata);
847            break;
848
849        case VERSION_2_8_BIT:
850            // no compression
851            read8Bit(bdata);
852            break;
853
854        case VERSION_2_24_BIT:
855            // no compression
856            read24Bit(bdata);
857            break;
858
859        case VERSION_3_1_BIT:
860            // 1-bit images cannot be compressed.
861            read1Bit(bdata);
862            break;
863
864        case VERSION_3_4_BIT:
865            switch((int)compression) {
866            case BI_RGB:
867                read4Bit(bdata);
868                break;
869
870            case BI_RLE4:
871                readRLE4(bdata);
872                break;
873
874            default:
875                throw new
876                    IIOException(I18N.getString("BMPImageReader1"));
877            }
878            break;
879
880        case VERSION_3_8_BIT:
881            switch((int)compression) {
882            case BI_RGB:
883                read8Bit(bdata);
884                break;
885
886            case BI_RLE8:
887                readRLE8(bdata);
888                break;
889
890            default:
891                throw new
892                    IIOException(I18N.getString("BMPImageReader1"));
893            }
894
895            break;
896
897        case VERSION_3_24_BIT:
898            // 24-bit images are not compressed
899            read24Bit(bdata);
900            break;
901
902        case VERSION_3_NT_16_BIT:
903            read16Bit(sdata);
904            break;
905
906        case VERSION_3_NT_32_BIT:
907            read32Bit(idata);
908            break;
909
910        case VERSION_3_XP_EMBEDDED:
911        case VERSION_4_XP_EMBEDDED:
912        case VERSION_5_XP_EMBEDDED:
913            bi = readEmbedded((int)compression, bi, param);
914            break;
915
916        case VERSION_4_1_BIT:
917            read1Bit(bdata);
918            break;
919
920        case VERSION_4_4_BIT:
921            switch((int)compression) {
922
923            case BI_RGB:
924                read4Bit(bdata);
925                break;
926
927            case BI_RLE4:
928                readRLE4(bdata);
929                break;
930
931            default:
932                throw new
933                    IIOException(I18N.getString("BMPImageReader1"));
934            }
935            break;
936
937        case VERSION_4_8_BIT:
938            switch((int)compression) {
939
940            case BI_RGB:
941                read8Bit(bdata);
942                break;
943
944            case BI_RLE8:
945                readRLE8(bdata);
946                break;
947
948            default:
949                throw new
950                    IIOException(I18N.getString("BMPImageReader1"));
951            }
952            break;
953
954        case VERSION_4_16_BIT:
955            read16Bit(sdata);
956            break;
957
958        case VERSION_4_24_BIT:
959            read24Bit(bdata);
960            break;
961
962        case VERSION_4_32_BIT:
963            read32Bit(idata);
964            break;
965        }
966
967        if (abortRequested())
968            processReadAborted();
969        else
970            processImageComplete();
971
972        return bi;
973    }
974
975    public boolean canReadRaster() {
976        return true;
977    }
978
979    public Raster readRaster(int imageIndex,
980                             ImageReadParam param) throws IOException {
981        BufferedImage bi = read(imageIndex, param);
982        return bi.getData();
983    }
984
985    private void resetHeaderInfo() {
986        gotHeader = false;
987        bi = null;
988        sampleModel = originalSampleModel = null;
989        colorModel = originalColorModel = null;
990    }
991
992    public void reset() {
993        super.reset();
994        iis = null;
995        resetHeaderInfo();
996    }
997
998    // Deal with 1 Bit images using IndexColorModels
999    private void read1Bit(byte[] bdata) throws IOException {
1000        int bytesPerScanline = (width + 7) / 8;
1001        int padding = bytesPerScanline % 4;
1002        if (padding != 0) {
1003            padding = 4 - padding;
1004        }
1005
1006        int lineLength = bytesPerScanline + padding;
1007
1008        if (noTransform) {
1009            int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
1010
1011            for (int i=0; i<height; i++) {
1012                iis.readFully(bdata, j, bytesPerScanline);
1013                iis.skipBytes(padding);
1014                j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1015                processImageUpdate(bi, 0, i,
1016                                   destinationRegion.width, 1, 1, 1,
1017                                   new int[]{0});
1018                processImageProgress(100.0F * i/destinationRegion.height);
1019                if (abortRequested()) {
1020                    break;
1021                }
1022            }
1023        } else {
1024            byte[] buf = new byte[lineLength];
1025            int lineStride =
1026                ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1027
1028            if (isBottomUp) {
1029                int lastLine =
1030                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1031                iis.skipBytes(lineLength * (height - 1 - lastLine));
1032            } else
1033                iis.skipBytes(lineLength * sourceRegion.y);
1034
1035            int skipLength = lineLength * (scaleY - 1);
1036
1037            // cache the values to avoid duplicated computation
1038            int[] srcOff = new int[destinationRegion.width];
1039            int[] destOff = new int[destinationRegion.width];
1040            int[] srcPos = new int[destinationRegion.width];
1041            int[] destPos = new int[destinationRegion.width];
1042
1043            for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1044                 i < destinationRegion.x + destinationRegion.width;
1045                 i++, j++, x += scaleX) {
1046                srcPos[j] = x >> 3;
1047                srcOff[j] = 7 - (x & 7);
1048                destPos[j] = i >> 3;
1049                destOff[j] = 7 - (i & 7);
1050            }
1051
1052            int k = destinationRegion.y * lineStride;
1053            if (isBottomUp)
1054                k += (destinationRegion.height - 1) * lineStride;
1055
1056            for (int j = 0, y = sourceRegion.y;
1057                 j < destinationRegion.height; j++, y+=scaleY) {
1058                iis.read(buf, 0, lineLength);
1059                for (int i = 0; i < destinationRegion.width; i++) {
1060                    //get the bit and assign to the data buffer of the raster
1061                    int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
1062                    bdata[k + destPos[i]] |= v << destOff[i];
1063                }
1064
1065                k += isBottomUp ? -lineStride : lineStride;
1066                iis.skipBytes(skipLength);
1067                processImageUpdate(bi, 0, j,
1068                                   destinationRegion.width, 1, 1, 1,
1069                                   new int[]{0});
1070                processImageProgress(100.0F*j/destinationRegion.height);
1071                if (abortRequested()) {
1072                    break;
1073                }
1074            }
1075        }
1076    }
1077
1078    // Method to read a 4 bit BMP image data
1079    private void read4Bit(byte[] bdata) throws IOException {
1080
1081        int bytesPerScanline = (width + 1) / 2;
1082
1083        // Padding bytes at the end of each scanline
1084        int padding = bytesPerScanline % 4;
1085        if (padding != 0)
1086            padding = 4 - padding;
1087
1088        int lineLength = bytesPerScanline + padding;
1089
1090        if (noTransform) {
1091            int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
1092
1093            for (int i=0; i<height; i++) {
1094                iis.readFully(bdata, j, bytesPerScanline);
1095                iis.skipBytes(padding);
1096                j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1097                processImageUpdate(bi, 0, i,
1098                                   destinationRegion.width, 1, 1, 1,
1099                                   new int[]{0});
1100                processImageProgress(100.0F * i/destinationRegion.height);
1101                if (abortRequested()) {
1102                    break;
1103                }
1104            }
1105        } else {
1106            byte[] buf = new byte[lineLength];
1107            int lineStride =
1108                ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1109
1110            if (isBottomUp) {
1111                int lastLine =
1112                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1113                iis.skipBytes(lineLength * (height - 1 - lastLine));
1114            } else
1115                iis.skipBytes(lineLength * sourceRegion.y);
1116
1117            int skipLength = lineLength * (scaleY - 1);
1118
1119            // cache the values to avoid duplicated computation
1120            int[] srcOff = new int[destinationRegion.width];
1121            int[] destOff = new int[destinationRegion.width];
1122            int[] srcPos = new int[destinationRegion.width];
1123            int[] destPos = new int[destinationRegion.width];
1124
1125            for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1126                 i < destinationRegion.x + destinationRegion.width;
1127                 i++, j++, x += scaleX) {
1128                srcPos[j] = x >> 1;
1129                srcOff[j] = (1 - (x & 1)) << 2;
1130                destPos[j] = i >> 1;
1131                destOff[j] = (1 - (i & 1)) << 2;
1132            }
1133
1134            int k = destinationRegion.y * lineStride;
1135            if (isBottomUp)
1136                k += (destinationRegion.height - 1) * lineStride;
1137
1138            for (int j = 0, y = sourceRegion.y;
1139                 j < destinationRegion.height; j++, y+=scaleY) {
1140                iis.read(buf, 0, lineLength);
1141                for (int i = 0; i < destinationRegion.width; i++) {
1142                    //get the bit and assign to the data buffer of the raster
1143                    int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
1144                    bdata[k + destPos[i]] |= v << destOff[i];
1145                }
1146
1147                k += isBottomUp ? -lineStride : lineStride;
1148                iis.skipBytes(skipLength);
1149                processImageUpdate(bi, 0, j,
1150                                   destinationRegion.width, 1, 1, 1,
1151                                   new int[]{0});
1152                processImageProgress(100.0F*j/destinationRegion.height);
1153                if (abortRequested()) {
1154                    break;
1155                }
1156            }
1157        }
1158    }
1159
1160    // Method to read 8 bit BMP image data
1161    private void read8Bit(byte[] bdata) throws IOException {
1162
1163        // Padding bytes at the end of each scanline
1164        int padding = width % 4;
1165        if (padding != 0) {
1166            padding = 4 - padding;
1167        }
1168
1169        int lineLength = width + padding;
1170
1171        if (noTransform) {
1172            int j = isBottomUp ? (height -1) * width : 0;
1173
1174            for (int i=0; i<height; i++) {
1175                iis.readFully(bdata, j, width);
1176                iis.skipBytes(padding);
1177                j += isBottomUp ? -width : width;
1178                processImageUpdate(bi, 0, i,
1179                                   destinationRegion.width, 1, 1, 1,
1180                                   new int[]{0});
1181                processImageProgress(100.0F * i/destinationRegion.height);
1182                if (abortRequested()) {
1183                    break;
1184                }
1185            }
1186        } else {
1187            byte[] buf = new byte[lineLength];
1188            int lineStride =
1189                ((ComponentSampleModel)sampleModel).getScanlineStride();
1190
1191            if (isBottomUp) {
1192                int lastLine =
1193                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1194                iis.skipBytes(lineLength * (height - 1 - lastLine));
1195            } else
1196                iis.skipBytes(lineLength * sourceRegion.y);
1197
1198            int skipLength = lineLength * (scaleY - 1);
1199
1200            int k = destinationRegion.y * lineStride;
1201            if (isBottomUp)
1202                k += (destinationRegion.height - 1) * lineStride;
1203            k += destinationRegion.x;
1204
1205            for (int j = 0, y = sourceRegion.y;
1206                 j < destinationRegion.height; j++, y+=scaleY) {
1207                iis.read(buf, 0, lineLength);
1208                for (int i = 0, m = sourceRegion.x;
1209                     i < destinationRegion.width; i++, m += scaleX) {
1210                    //get the bit and assign to the data buffer of the raster
1211                    bdata[k + i] = buf[m];
1212                }
1213
1214                k += isBottomUp ? -lineStride : lineStride;
1215                iis.skipBytes(skipLength);
1216                processImageUpdate(bi, 0, j,
1217                                   destinationRegion.width, 1, 1, 1,
1218                                   new int[]{0});
1219                processImageProgress(100.0F*j/destinationRegion.height);
1220                if (abortRequested()) {
1221                    break;
1222                }
1223            }
1224        }
1225    }
1226
1227    // Method to read 24 bit BMP image data
1228    private void read24Bit(byte[] bdata) throws IOException {
1229        // Padding bytes at the end of each scanline
1230        // width * bitsPerPixel should be divisible by 32
1231        int padding = width * 3 % 4;
1232        if ( padding != 0)
1233            padding = 4 - padding;
1234
1235        int lineStride = width * 3;
1236        int lineLength = lineStride + padding;
1237
1238        if (noTransform) {
1239            int j = isBottomUp ? (height -1) * width * 3 : 0;
1240
1241            for (int i=0; i<height; i++) {
1242                iis.readFully(bdata, j, lineStride);
1243                iis.skipBytes(padding);
1244                j += isBottomUp ? -lineStride : lineStride;
1245                processImageUpdate(bi, 0, i,
1246                                   destinationRegion.width, 1, 1, 1,
1247                                   new int[]{0});
1248                processImageProgress(100.0F * i/destinationRegion.height);
1249                if (abortRequested()) {
1250                    break;
1251                }
1252            }
1253        } else {
1254            byte[] buf = new byte[lineLength];
1255            lineStride =
1256                ((ComponentSampleModel)sampleModel).getScanlineStride();
1257
1258            if (isBottomUp) {
1259                int lastLine =
1260                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1261                iis.skipBytes(lineLength * (height - 1 - lastLine));
1262            } else
1263                iis.skipBytes(lineLength * sourceRegion.y);
1264
1265            int skipLength = lineLength * (scaleY - 1);
1266
1267            int k = destinationRegion.y * lineStride;
1268            if (isBottomUp)
1269                k += (destinationRegion.height - 1) * lineStride;
1270            k += destinationRegion.x * 3;
1271
1272            for (int j = 0, y = sourceRegion.y;
1273                 j < destinationRegion.height; j++, y+=scaleY) {
1274                iis.read(buf, 0, lineLength);
1275                for (int i = 0, m = 3 * sourceRegion.x;
1276                     i < destinationRegion.width; i++, m += 3 * scaleX) {
1277                    //get the bit and assign to the data buffer of the raster
1278                    int n = 3 * i + k;
1279                    for (int b = 0; b < destBands.length; b++)
1280                        bdata[n + destBands[b]] = buf[m + sourceBands[b]];
1281                }
1282
1283                k += isBottomUp ? -lineStride : lineStride;
1284                iis.skipBytes(skipLength);
1285                processImageUpdate(bi, 0, j,
1286                                   destinationRegion.width, 1, 1, 1,
1287                                   new int[]{0});
1288                processImageProgress(100.0F*j/destinationRegion.height);
1289                if (abortRequested()) {
1290                    break;
1291                }
1292            }
1293        }
1294    }
1295
1296    private void read16Bit(short sdata[]) throws IOException {
1297        // Padding bytes at the end of each scanline
1298        // width * bitsPerPixel should be divisible by 32
1299        int padding = width * 2 % 4;
1300
1301        if ( padding != 0)
1302            padding = 4 - padding;
1303
1304        int lineLength = width + padding / 2;
1305
1306        if (noTransform) {
1307            int j = isBottomUp ? (height -1) * width : 0;
1308            for (int i=0; i<height; i++) {
1309                iis.readFully(sdata, j, width);
1310                iis.skipBytes(padding);
1311
1312                j += isBottomUp ? -width : width;
1313                processImageUpdate(bi, 0, i,
1314                                   destinationRegion.width, 1, 1, 1,
1315                                   new int[]{0});
1316                processImageProgress(100.0F * i/destinationRegion.height);
1317                if (abortRequested()) {
1318                    break;
1319                }
1320            }
1321        } else {
1322            short[] buf = new short[lineLength];
1323            int lineStride =
1324                ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1325
1326            if (isBottomUp) {
1327                int lastLine =
1328                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1329                iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
1330            } else
1331                iis.skipBytes(lineLength * sourceRegion.y << 1);
1332
1333            int skipLength = lineLength * (scaleY - 1) << 1;
1334
1335            int k = destinationRegion.y * lineStride;
1336            if (isBottomUp)
1337                k += (destinationRegion.height - 1) * lineStride;
1338            k += destinationRegion.x;
1339
1340            for (int j = 0, y = sourceRegion.y;
1341                 j < destinationRegion.height; j++, y+=scaleY) {
1342                iis.readFully(buf, 0, lineLength);
1343                for (int i = 0, m = sourceRegion.x;
1344                     i < destinationRegion.width; i++, m += scaleX) {
1345                    //get the bit and assign to the data buffer of the raster
1346                    sdata[k + i] = buf[m];
1347                }
1348
1349                k += isBottomUp ? -lineStride : lineStride;
1350                iis.skipBytes(skipLength);
1351                processImageUpdate(bi, 0, j,
1352                                   destinationRegion.width, 1, 1, 1,
1353                                   new int[]{0});
1354                processImageProgress(100.0F*j/destinationRegion.height);
1355                if (abortRequested()) {
1356                    break;
1357                }
1358            }
1359        }
1360    }
1361
1362    private void read32Bit(int idata[]) throws IOException {
1363        if (noTransform) {
1364            int j = isBottomUp ? (height -1) * width : 0;
1365
1366            for (int i=0; i<height; i++) {
1367                iis.readFully(idata, j, width);
1368                j += isBottomUp ? -width : width;
1369                processImageUpdate(bi, 0, i,
1370                                   destinationRegion.width, 1, 1, 1,
1371                                   new int[]{0});
1372                processImageProgress(100.0F * i/destinationRegion.height);
1373                if (abortRequested()) {
1374                    break;
1375                }
1376            }
1377        } else {
1378            int[] buf = new int[width];
1379            int lineStride =
1380                ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1381
1382            if (isBottomUp) {
1383                int lastLine =
1384                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1385                iis.skipBytes(width * (height - 1 - lastLine) << 2);
1386            } else
1387                iis.skipBytes(width * sourceRegion.y << 2);
1388
1389            int skipLength = width * (scaleY - 1) << 2;
1390
1391            int k = destinationRegion.y * lineStride;
1392            if (isBottomUp)
1393                k += (destinationRegion.height - 1) * lineStride;
1394            k += destinationRegion.x;
1395
1396            for (int j = 0, y = sourceRegion.y;
1397                 j < destinationRegion.height; j++, y+=scaleY) {
1398                iis.readFully(buf, 0, width);
1399                for (int i = 0, m = sourceRegion.x;
1400                     i < destinationRegion.width; i++, m += scaleX) {
1401                    //get the bit and assign to the data buffer of the raster
1402                    idata[k + i] = buf[m];
1403                }
1404
1405                k += isBottomUp ? -lineStride : lineStride;
1406                iis.skipBytes(skipLength);
1407                processImageUpdate(bi, 0, j,
1408                                   destinationRegion.width, 1, 1, 1,
1409                                   new int[]{0});
1410                processImageProgress(100.0F*j/destinationRegion.height);
1411                if (abortRequested()) {
1412                    break;
1413                }
1414            }
1415        }
1416    }
1417
1418    private void readRLE8(byte bdata[]) throws IOException {
1419        // If imageSize field is not provided, calculate it.
1420        int imSize = (int)imageSize;
1421        if (imSize == 0) {
1422            imSize = (int)(bitmapFileSize - bitmapOffset);
1423        }
1424
1425        int padding = 0;
1426        // If width is not 32 bit aligned, then while uncompressing each
1427        // scanline will have padding bytes, calculate the amount of padding
1428        int remainder = width % 4;
1429        if (remainder != 0) {
1430            padding = 4 - remainder;
1431        }
1432
1433        // Read till we have the whole image
1434        byte values[] = new byte[imSize];
1435        int bytesRead = 0;
1436        iis.readFully(values, 0, imSize);
1437
1438        // Since data is compressed, decompress it
1439        decodeRLE8(imSize, padding, values, bdata);
1440    }
1441
1442    private boolean copyRLE8ScanlineToDst(int lineNo,
1443                                          byte[] val,
1444                                          byte[] bdata) {
1445        // Return value
1446        boolean isSuccess = false;
1447
1448        // Reusing the code to copy 1 row of pixels or scanline to required
1449        // destination buffer.
1450        if (lineNo >= sourceRegion.y &&
1451            lineNo < sourceRegion.y + sourceRegion.height) {
1452            if (noTransform) {
1453                int pos = lineNo * width;
1454                for(int i = 0; i < width; i++)
1455                    bdata[pos++] = val[i];
1456                processImageUpdate(bi, 0, lineNo,
1457                                   destinationRegion.width, 1, 1, 1,
1458                                   new int[]{0});
1459                isSuccess = true;
1460            } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1461                int lineStride =
1462                    ((ComponentSampleModel)sampleModel).getScanlineStride();
1463                int currentLine = (lineNo - sourceRegion.y) / scaleY +
1464                    destinationRegion.y;
1465                int pos = currentLine * lineStride;
1466                pos += destinationRegion.x;
1467                for (int i = sourceRegion.x;
1468                     i < sourceRegion.x + sourceRegion.width;
1469                     i += scaleX)
1470                    bdata[pos++] = val[i];
1471                processImageUpdate(bi, 0, currentLine,
1472                                   destinationRegion.width, 1, 1, 1,
1473                                   new int[]{0});
1474                isSuccess = true;
1475            }
1476            // Ensure to reset the scanline buffer once the copy is complete.
1477            for(int scIndex = 0; scIndex < width; scIndex++) {
1478                val[scIndex] = 0;
1479            }
1480        }
1481
1482        return isSuccess;
1483    }
1484
1485    private void decodeRLE8(int imSize,
1486                            int padding,
1487                            byte[] values,
1488                            byte[] bdata) throws IOException {
1489
1490        byte val[] = new byte[width];
1491        int count = 0, l = 0;
1492        int value;
1493        boolean flag = false;
1494        int lineNo = isBottomUp ? height - 1 : 0;
1495        int finished = 0;
1496
1497        // Ensure image source has sufficient data to decode
1498        while ((count + 1) < imSize) {
1499            value = values[count++] & 0xff;
1500            if (value == 0) {
1501                switch(values[count++] & 0xff) {
1502
1503                case 0:
1504                    // End-of-scanline marker
1505                    // Copy the decoded scanline to destination
1506                    if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
1507                        finished++;
1508                    }
1509                    processImageProgress(100.0F * finished / destinationRegion.height);
1510                    lineNo += isBottomUp ? -1 : 1;
1511                    l = 0;
1512
1513                    if (abortRequested()) {
1514                        flag = true;
1515                    }
1516                    break;
1517
1518                case 1:
1519                    // End-of-RLE marker
1520                    flag = true;
1521
1522                    // Check if the last decoded scanline was copied to
1523                    // destination bitmap
1524                    if (l != 0) {
1525                        // Copy the decoded scanline to destination
1526                        if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
1527                            finished++;
1528                        }
1529                        processImageProgress(100.0F * finished / destinationRegion.height);
1530                        lineNo += isBottomUp ? -1 : 1;
1531                        l = 0;
1532                    }
1533                    break;
1534
1535                case 2:
1536                    // delta or vector marker
1537                    if ((count+1) < imSize) {
1538                        int xoff = values[count++] & 0xff;
1539                        int yoff = values[count++] & 0xff;
1540
1541                        // Check if the yOffset shifts the decoding to another
1542                        // row. In such cases, the decoded pixels in scanline
1543                        // buffer-val must be copied to the destination image.
1544                        if (yoff != 0) {
1545                            // Copy the decoded scanline to destination
1546                            if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
1547                                finished++;
1548                            }
1549                            processImageProgress(100.0F * finished
1550                                                 / destinationRegion.height);
1551                            lineNo += isBottomUp ? -yoff : yoff;
1552                        }
1553
1554                        // Move to the position xoff, yoff down
1555                        l += xoff + yoff*width;
1556                        l %= width;
1557                    }
1558                    break;
1559
1560                default:
1561                    int end = values[count-1] & 0xff;
1562                    byte readByte = 0;
1563                    // Ensure to check if the source index-count, does not
1564                    // exceed the source image size
1565                    for (int i=0; (i < end) && (count < imSize); i++) {
1566                        readByte = (byte)(values[count++] & 0xff);
1567                        // Ensure to check if scanline index-l, does not
1568                        // exceed the scanline buffer size (width of image)
1569                        if (l < width) {
1570                            val[l++] = readByte;
1571                        }
1572                    }
1573
1574                    // Whenever end pixels can fit into odd number of bytes,
1575                    // an extra padding byte will be present, so skip that.
1576                    if ((end & 1) == 1) {
1577                        count++;
1578                    }
1579                    break;
1580                }
1581            } else {
1582                // Encoded mode
1583                // Ensure to check if the source index-count, does not
1584                // exceed the source image size
1585                if (count < imSize) {
1586                    for (int i=0; (i < value) && (l < width); i++) {
1587                        val[l++] = (byte)(values[count] & 0xff);
1588                    }
1589                }
1590
1591                count++;
1592            }
1593
1594            // If End-of-RLE data, then exit the while loop
1595            if (flag) {
1596                break;
1597            }
1598        }
1599    }
1600
1601    private void readRLE4(byte[] bdata) throws IOException {
1602
1603        // If imageSize field is not specified, calculate it.
1604        int imSize = (int)imageSize;
1605        if (imSize == 0) {
1606            imSize = (int)(bitmapFileSize - bitmapOffset);
1607        }
1608
1609        int padding = 0;
1610        // If width is not 32 byte aligned, then while uncompressing each
1611        // scanline will have padding bytes, calculate the amount of padding
1612        int remainder = width % 4;
1613        if (remainder != 0) {
1614            padding = 4 - remainder;
1615        }
1616
1617        // Read till we have the whole image
1618        byte[] values = new byte[imSize];
1619        iis.readFully(values, 0, imSize);
1620
1621        // Decompress the RLE4 compressed data.
1622        decodeRLE4(imSize, padding, values, bdata);
1623    }
1624
1625    private boolean copyRLE4ScanlineToDst(int lineNo,
1626                                          byte[] val,
1627                                          byte[] bdata) throws IOException {
1628        // Return value
1629        boolean isSuccess = false;
1630
1631        // Reusing the code to copy 1 row of pixels or scanline to required
1632        // destination buffer.
1633        if (lineNo >= sourceRegion.y &&
1634            lineNo < sourceRegion.y + sourceRegion.height) {
1635            if (noTransform) {
1636                int pos = lineNo * (width + 1 >> 1);
1637                for(int i = 0, j = 0; i < width >> 1; i++)
1638                    bdata[pos++] =
1639                        (byte)((val[j++] << 4) | val[j++]);
1640                if ((width & 1) == 1)
1641                    bdata[pos] |= val[width - 1] << 4;
1642
1643                processImageUpdate(bi, 0, lineNo,
1644                                   destinationRegion.width, 1, 1, 1,
1645                                   new int[]{0});
1646                isSuccess = true;
1647            } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1648                int lineStride =
1649                    ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1650                int currentLine = (lineNo - sourceRegion.y) / scaleY +
1651                    destinationRegion.y;
1652                int pos = currentLine * lineStride;
1653                pos += destinationRegion.x >> 1;
1654                int shift = (1 - (destinationRegion.x & 1)) << 2;
1655                for (int i = sourceRegion.x;
1656                     i < sourceRegion.x + sourceRegion.width;
1657                     i += scaleX) {
1658                    bdata[pos] |= val[i] << shift;
1659                    shift += 4;
1660                    if (shift == 4) {
1661                        pos++;
1662                    }
1663                    shift &= 7;
1664                }
1665                processImageUpdate(bi, 0, currentLine,
1666                                   destinationRegion.width, 1, 1, 1,
1667                                   new int[]{0});
1668                isSuccess = true;
1669            }
1670            // Ensure to reset the scanline buffer once the copy is complete.
1671            for(int scIndex = 0; scIndex < width; scIndex++) {
1672                val[scIndex] = 0;
1673            }
1674        }
1675        return isSuccess;
1676    }
1677
1678    private void decodeRLE4(int imSize,
1679                            int padding,
1680                            byte[] values,
1681                            byte[] bdata) throws IOException {
1682        byte[] val = new byte[width];
1683        int count = 0, l = 0;
1684        int value;
1685        boolean flag = false;
1686        int lineNo = isBottomUp ? height - 1 : 0;
1687        int finished = 0;
1688
1689        // Ensure the image has sufficient data before proceeding to decode
1690        while ((count + 1) < imSize) {
1691
1692            value = values[count++] & 0xFF;
1693            if (value == 0) {
1694
1695                // Absolute mode
1696                switch(values[count++] & 0xFF) {
1697
1698                case 0:
1699                    // End-of-scanline marker
1700                    // Copy the decoded scanline to destination
1701                    if (copyRLE4ScanlineToDst(lineNo, val, bdata)) {
1702                        finished++;
1703                    }
1704                    processImageProgress(100.0F * finished / destinationRegion.height);
1705                    lineNo += isBottomUp ? -1 : 1;
1706                    l = 0;
1707
1708                    if (abortRequested()) {
1709                        flag = true;
1710                    }
1711
1712                    break;
1713
1714                case 1:
1715                    // End-of-RLE marker
1716                    flag = true;
1717
1718                    // Check if the last decoded scanline was copied to
1719                    // destination bitmap
1720                    if (l != 0) {
1721                        // Copy the decoded scanline to destination
1722                        if (copyRLE4ScanlineToDst(lineNo, val, bdata)) {
1723                            finished++;
1724                        }
1725                        processImageProgress(100.0F * finished / destinationRegion.height);
1726                        lineNo += isBottomUp ? -1 : 1;
1727                        l = 0;
1728                    }
1729                    break;
1730
1731                case 2:
1732                    // delta or vector marker
1733                    if ((count + 1) < imSize) {
1734                        int xoff = values[count++] & 0xFF;
1735                        int yoff = values[count++] & 0xFF;
1736
1737                        // Check if the yOffset shifts the decoding to another
1738                        // row. In such cases, the decoded pixels in scanline
1739                        // buffer-val must be copied to the destination image.
1740                        if (yoff != 0) {
1741                            // Copy the decoded scanline to destination
1742                            if (copyRLE4ScanlineToDst(lineNo, val, bdata)) {
1743                                finished++;
1744                            }
1745                            processImageProgress(100.0F * finished
1746                                                 / destinationRegion.height);
1747                            lineNo += isBottomUp ? -yoff : yoff;
1748                        }
1749
1750                        // Move to the position (xoff, yoff). Since l-is used
1751                        // to index into the scanline buffer, the accumulated
1752                        // offset is limited to the width of the scanline
1753                        l += xoff + yoff*width;
1754                        l %= width;
1755                    }
1756                    break;
1757
1758                default:
1759                    int end = values[count-1] & 0xFF;
1760                    byte readByte = 0;
1761                    // Ensure to check if the source index-count, does not
1762                    // exceed the source image size
1763                    for (int i = 0; (i < end) && (count < imSize); i++) {
1764                        readByte = (byte)(((i & 1) == 0) ?
1765                                        (values[count] & 0xf0) >> 4 :
1766                                        (values[count++] & 0x0f));
1767                        // Ensure to check if scanline index-l, does not
1768                        // exceed the scanline buffer size (width of image)
1769                        if (l < width) {
1770                            val[l++] = readByte;
1771                        }
1772                    }
1773
1774                    // When end is odd, the above for loop does not
1775                    // increment count, so do it now.
1776                    if ((end & 1) == 1) {
1777                        count++;
1778                    }
1779
1780                    // Whenever end pixels can fit into odd number of bytes,
1781                    // an extra padding byte will be present, so skip that.
1782                    if ((((end + 1) / 2) & 1) == 1) {
1783                        count++;
1784                    }
1785                    break;
1786                }
1787            } else {
1788                // Encoded mode
1789                // Ensure to check if the source index-count, does not
1790                // exceed the source image size
1791                if (count < imSize) {
1792                    int alternate[] = { (values[count] & 0xf0) >> 4,
1793                                        values[count] & 0x0f };
1794                    for (int i=0; (i < value) && (l < width); i++) {
1795                        val[l++] = (byte)alternate[i & 1];
1796                    }
1797                }
1798
1799                count++;
1800            }
1801
1802            // If End-of-RLE data, then exit the while loop
1803            if (flag) {
1804                break;
1805            }
1806        }
1807    }
1808
1809    /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1810     *  ImageIO-style plugin.
1811     *
1812     * @param bi The destination {@code BufferedImage}.
1813     * @param bmpParam The {@code ImageReadParam} for decoding this
1814     *          BMP image.  The parameters for subregion, band selection and
1815     *          subsampling are used in decoding the jpeg image.
1816     */
1817
1818    private BufferedImage readEmbedded(int type,
1819                              BufferedImage bi, ImageReadParam bmpParam)
1820      throws IOException {
1821        String format;
1822        switch(type) {
1823          case BI_JPEG:
1824              format = "JPEG";
1825              break;
1826          case BI_PNG:
1827              format = "PNG";
1828              break;
1829          default:
1830              throw new
1831                  IOException("Unexpected compression type: " + type);
1832        }
1833        ImageReader reader =
1834            ImageIO.getImageReadersByFormatName(format).next();
1835        if (reader == null) {
1836            throw new RuntimeException(I18N.getString("BMPImageReader4") +
1837                                       " " + format);
1838        }
1839        // prepare input
1840        byte[] buff = new byte[(int)imageSize];
1841        iis.read(buff);
1842        reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
1843        if (bi == null) {
1844            ImageTypeSpecifier embType = reader.getImageTypes(0).next();
1845            bi = embType.createBufferedImage(destinationRegion.x +
1846                                             destinationRegion.width,
1847                                             destinationRegion.y +
1848                                             destinationRegion.height);
1849        }
1850
1851        reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1852                public void imageProgress(ImageReader source,
1853                                          float percentageDone)
1854                {
1855                    processImageProgress(percentageDone);
1856                }
1857            });
1858
1859        reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1860                public void imageUpdate(ImageReader source,
1861                                        BufferedImage theImage,
1862                                        int minX, int minY,
1863                                        int width, int height,
1864                                        int periodX, int periodY,
1865                                        int[] bands)
1866                {
1867                    processImageUpdate(theImage, minX, minY,
1868                                       width, height,
1869                                       periodX, periodY, bands);
1870                }
1871                public void passComplete(ImageReader source,
1872                                         BufferedImage theImage)
1873                {
1874                    processPassComplete(theImage);
1875                }
1876                public void passStarted(ImageReader source,
1877                                        BufferedImage theImage,
1878                                        int pass,
1879                                        int minPass, int maxPass,
1880                                        int minX, int minY,
1881                                        int periodX, int periodY,
1882                                        int[] bands)
1883                {
1884                    processPassStarted(theImage, pass, minPass, maxPass,
1885                                       minX, minY, periodX, periodY,
1886                                       bands);
1887                }
1888                public void thumbnailPassComplete(ImageReader source,
1889                                                  BufferedImage thumb) {}
1890                public void thumbnailPassStarted(ImageReader source,
1891                                                 BufferedImage thumb,
1892                                                 int pass,
1893                                                 int minPass, int maxPass,
1894                                                 int minX, int minY,
1895                                                 int periodX, int periodY,
1896                                                 int[] bands) {}
1897                public void thumbnailUpdate(ImageReader source,
1898                                            BufferedImage theThumbnail,
1899                                            int minX, int minY,
1900                                            int width, int height,
1901                                            int periodX, int periodY,
1902                                            int[] bands) {}
1903            });
1904
1905        reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1906                public void warningOccurred(ImageReader source, String warning)
1907                {
1908                    processWarningOccurred(warning);
1909                }
1910            });
1911
1912        ImageReadParam param = reader.getDefaultReadParam();
1913        param.setDestination(bi);
1914        param.setDestinationBands(bmpParam.getDestinationBands());
1915        param.setDestinationOffset(bmpParam.getDestinationOffset());
1916        param.setSourceBands(bmpParam.getSourceBands());
1917        param.setSourceRegion(bmpParam.getSourceRegion());
1918        param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1919                                   bmpParam.getSourceYSubsampling(),
1920                                   bmpParam.getSubsamplingXOffset(),
1921                                   bmpParam.getSubsamplingYOffset());
1922        reader.read(0, param);
1923        return bi;
1924    }
1925
1926    private class EmbeddedProgressAdapter implements IIOReadProgressListener {
1927        public void imageComplete(ImageReader src) {}
1928        public void imageProgress(ImageReader src, float percentageDone) {}
1929        public void imageStarted(ImageReader src, int imageIndex) {}
1930        public void thumbnailComplete(ImageReader src) {}
1931        public void thumbnailProgress(ImageReader src, float percentageDone) {}
1932        public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
1933        public void sequenceComplete(ImageReader src) {}
1934        public void sequenceStarted(ImageReader src, int minIndex) {}
1935        public void readAborted(ImageReader src) {}
1936    }
1937
1938    private static Boolean isLinkedProfileDisabled = null;
1939
1940    private static boolean isLinkedProfileAllowed() {
1941        if (isLinkedProfileDisabled == null) {
1942            PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1943                public Boolean run() {
1944                    return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles");
1945                }
1946            };
1947            isLinkedProfileDisabled = AccessController.doPrivileged(a);
1948        }
1949        return !isLinkedProfileDisabled;
1950    }
1951
1952    private static Boolean isWindowsPlatform = null;
1953
1954    /**
1955     * Verifies whether the byte array contans a unc path.
1956     * Non-UNC path examples:
1957     *  c:\path\to\file  - simple notation
1958     *  \\?\c:\path\to\file - long notation
1959     *
1960     * UNC path examples:
1961     *  \\server\share - a UNC path in simple notation
1962     *  \\?\UNC\server\share - a UNC path in long notation
1963     *  \\.\some\device - a path to device.
1964     */
1965    private static boolean isUncOrDevicePath(byte[] p) {
1966        if (isWindowsPlatform == null) {
1967            PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1968                public Boolean run() {
1969                    String osname = System.getProperty("os.name");
1970                    return (osname != null &&
1971                            osname.toLowerCase().startsWith("win"));
1972                }
1973            };
1974            isWindowsPlatform = AccessController.doPrivileged(a);
1975        }
1976
1977        if (!isWindowsPlatform) {
1978            /* no need for the check on platforms except windows */
1979            return false;
1980        }
1981
1982        /* normalize prefix of the path */
1983        if (p[0] == '/') p[0] = '\\';
1984        if (p[1] == '/') p[1] = '\\';
1985        if (p[3] == '/') p[3] = '\\';
1986
1987
1988        if ((p[0] == '\\') && (p[1] == '\\')) {
1989            if ((p[2] == '?') && (p[3] == '\\')) {
1990                // long path: whether unc or local
1991                return ((p[4] == 'U' || p[4] == 'u') &&
1992                        (p[5] == 'N' || p[5] == 'n') &&
1993                        (p[6] == 'C' || p[6] == 'c'));
1994            } else {
1995                // device path or short unc notation
1996                return true;
1997            }
1998        } else {
1999            return false;
2000        }
2001    }
2002}
2003