001/*
002 * $RCSfile: BMPImageReader.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.2 $
042 * $Date: 2006/04/14 21:29:14 $
043 * $State: Exp $
044 */
045
046package com.github.jaiimageio.impl.plugins.bmp;
047
048import java.awt.Point;
049import java.awt.Rectangle;
050import java.awt.color.ColorSpace;
051import java.awt.color.ICC_ColorSpace;
052import java.awt.color.ICC_Profile;
053import java.awt.image.BufferedImage;
054import java.awt.image.ColorModel;
055import java.awt.image.ComponentSampleModel;
056import java.awt.image.DataBuffer;
057import java.awt.image.DataBufferByte;
058import java.awt.image.DataBufferInt;
059import java.awt.image.DataBufferUShort;
060import java.awt.image.DirectColorModel;
061import java.awt.image.IndexColorModel;
062import java.awt.image.MultiPixelPackedSampleModel;
063import java.awt.image.PixelInterleavedSampleModel;
064import java.awt.image.Raster;
065import java.awt.image.SampleModel;
066import java.awt.image.SinglePixelPackedSampleModel;
067import java.awt.image.WritableRaster;
068import java.io.ByteArrayInputStream;
069import java.io.IOException;
070import java.nio.ByteOrder;
071import java.util.ArrayList;
072import java.util.Iterator;
073
074import javax.imageio.ImageIO;
075import javax.imageio.ImageReadParam;
076import javax.imageio.ImageReader;
077import javax.imageio.ImageTypeSpecifier;
078import javax.imageio.event.IIOReadProgressListener;
079import javax.imageio.event.IIOReadUpdateListener;
080import javax.imageio.event.IIOReadWarningListener;
081import javax.imageio.metadata.IIOMetadata;
082import javax.imageio.spi.ImageReaderSpi;
083import javax.imageio.stream.ImageInputStream;
084
085import com.github.jaiimageio.impl.common.ImageUtil;
086
087/** This class is the Java Image IO plugin reader for BMP images.
088 *  It may subsample the image, clip the image, select sub-bands,
089 *  and shift the decoded image origin if the proper decoding parameter
090 *  are set in the provided <code>ImageReadParam</code>.
091 *
092 *  This class supports Microsoft Windows Bitmap Version 3-5,
093 *  as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
094 */
095public class BMPImageReader extends ImageReader implements BMPConstants {
096    // BMP Image types
097    private static final int VERSION_2_1_BIT = 0;
098    private static final int VERSION_2_4_BIT = 1;
099    private static final int VERSION_2_8_BIT = 2;
100    private static final int VERSION_2_24_BIT = 3;
101
102    private static final int VERSION_3_1_BIT = 4;
103    private static final int VERSION_3_4_BIT = 5;
104    private static final int VERSION_3_8_BIT = 6;
105    private static final int VERSION_3_24_BIT = 7;
106
107    private static final int VERSION_3_NT_16_BIT = 8;
108    private static final int VERSION_3_NT_32_BIT = 9;
109
110    private static final int VERSION_4_1_BIT = 10;
111    private static final int VERSION_4_4_BIT = 11;
112    private static final int VERSION_4_8_BIT = 12;
113    private static final int VERSION_4_16_BIT = 13;
114    private static final int VERSION_4_24_BIT = 14;
115    private static final int VERSION_4_32_BIT = 15;
116
117    private static final int VERSION_3_XP_EMBEDDED = 16;
118    private static final int VERSION_4_XP_EMBEDDED = 17;
119    private static final int VERSION_5_XP_EMBEDDED = 18;
120
121    // BMP variables
122    private long bitmapFileSize;
123    private long bitmapOffset;
124    private long compression;
125    private long imageSize;
126    private byte palette[];
127    private int imageType;
128    private int numBands;
129    private boolean isBottomUp;
130    private int bitsPerPixel;
131    private int redMask, greenMask, blueMask, alphaMask;
132
133    private SampleModel sampleModel, originalSampleModel;
134    private ColorModel colorModel, originalColorModel;
135
136    /** The input stream where reads from */
137    private ImageInputStream iis = null;
138
139    /** Indicates whether the header is read. */
140    private boolean gotHeader = false;
141
142    /** The stream position where the image data starts. */
143    private long imageDataOffset;
144
145    /** The original image width. */
146    private int width;
147
148    /** The original image height. */
149    private int height;
150
151    /** The destination region. */
152    private Rectangle destinationRegion;
153
154    /** The source region. */
155    private Rectangle sourceRegion;
156
157    /** The metadata from the stream. */
158    private BMPMetadata metadata;
159
160    /** The destination image. */
161    private BufferedImage bi;
162
163    /** Indicates whether subsampled, subregion is required, and offset is
164     *  defined
165     */
166    private boolean noTransform = true;
167
168    /** Indicates whether subband is selected. */
169    private boolean seleBand = false;
170
171    /** The scaling factors. */
172    private int scaleX, scaleY;
173
174    /** source and destination bands. */
175    private int[] sourceBands, destBands;
176
177    /** Constructs <code>BMPImageReader</code> from the provided
178     *  <code>ImageReaderSpi</code>.
179     */
180    public BMPImageReader(ImageReaderSpi originator) {
181        super(originator);
182    }
183
184    /** Overrides the method defined in the superclass. */
185    public void setInput(Object input,
186                         boolean seekForwardOnly,
187                         boolean ignoreMetadata) {
188        super.setInput(input, seekForwardOnly, ignoreMetadata);
189        iis = (ImageInputStream) input; // Always works
190        if(iis != null)
191            iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
192        resetHeaderInfo();
193    }
194
195    /** Overrides the method defined in the superclass. */
196    public int getNumImages(boolean allowSearch) throws IOException {
197        if (iis == null) {
198            throw new IllegalStateException(I18N.getString("GetNumImages0"));
199        }
200        if (seekForwardOnly && allowSearch) {
201            throw new IllegalStateException(I18N.getString("GetNumImages1"));
202        }
203        return 1;
204    }
205
206    public int getWidth(int imageIndex) throws IOException {
207        checkIndex(imageIndex);
208        readHeader();
209        return width;
210    }
211
212    public int getHeight(int imageIndex) throws IOException {
213        checkIndex(imageIndex);
214        readHeader();
215        return height;
216    }
217
218    private void checkIndex(int imageIndex) {
219        if (imageIndex != 0) {
220            throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
221        }
222    }
223
224    public void readHeader() throws IOException {
225        if (gotHeader) {
226            // Seek to where the image data starts, since that is where
227            // the stream pointer should be after header is read
228            iis.seek(imageDataOffset);
229            return;
230        }
231
232        if (iis == null) {
233            throw new IllegalStateException(I18N.getString("BMPImageReader5"));
234        }
235        int profileData = 0, profileSize = 0;
236
237        this.metadata = new BMPMetadata();
238        iis.mark();
239
240        // read and check the magic marker
241        byte[] marker = new byte[2];
242        iis.read(marker);
243        if (marker[0] != 0x42 || marker[1] != 0x4d)
244            throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
245
246        // Read file size
247        bitmapFileSize = iis.readUnsignedInt();
248        // skip the two reserved fields
249        iis.skipBytes(4);
250
251        // Offset to the bitmap from the beginning
252        bitmapOffset = iis.readUnsignedInt();
253        // End File Header
254
255        // Start BitmapCoreHeader
256        long size = iis.readUnsignedInt();
257
258        if (size == 12) {
259            width = iis.readShort();
260            height = iis.readShort();
261        } else {
262            width = iis.readInt();
263            height = iis.readInt();
264        }
265
266        metadata.width = width;
267        metadata.height = height;
268
269        int planes = iis.readUnsignedShort();
270        bitsPerPixel = iis.readUnsignedShort();
271
272        //metadata.colorPlane = planes;
273        metadata.bitsPerPixel = (short)bitsPerPixel;
274
275        // As BMP always has 3 rgb bands, except for Version 5,
276        // which is bgra
277        numBands = 3;
278
279        if (size == 12) {
280            // Windows 2.x and OS/2 1.x
281            metadata.bmpVersion = VERSION_2;
282
283            // Classify the image type
284            if (bitsPerPixel == 1) {
285                imageType = VERSION_2_1_BIT;
286            } else if (bitsPerPixel == 4) {
287                imageType = VERSION_2_4_BIT;
288            } else if (bitsPerPixel == 8) {
289                imageType = VERSION_2_8_BIT;
290            } else if (bitsPerPixel == 24) {
291                imageType = VERSION_2_24_BIT;
292            }
293
294            // Read in the palette
295            int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
296            int sizeOfPalette = numberOfEntries*3;
297            palette = new byte[sizeOfPalette];
298            iis.readFully(palette, 0, sizeOfPalette);
299            metadata.palette = palette;
300            metadata.paletteSize = numberOfEntries;
301        } else {
302            compression = iis.readUnsignedInt();
303            imageSize = iis.readUnsignedInt();
304            long xPelsPerMeter = iis.readInt();
305            long yPelsPerMeter = iis.readInt();
306            long colorsUsed = iis.readUnsignedInt();
307            long colorsImportant = iis.readUnsignedInt();
308
309            metadata.compression = (int)compression;
310            metadata.imageSize = (int)imageSize;
311            metadata.xPixelsPerMeter = (int)xPelsPerMeter;
312            metadata.yPixelsPerMeter = (int)yPelsPerMeter;
313            metadata.colorsUsed = (int)colorsUsed;
314            metadata.colorsImportant = (int)colorsImportant;
315
316            if (size == 40) {
317                // Windows 3.x and Windows NT
318                switch((int)compression) {
319
320                case BI_JPEG:
321                case BI_PNG:
322                    metadata.bmpVersion = VERSION_3;
323                    imageType = VERSION_3_XP_EMBEDDED;
324                    break;
325
326                case BI_RGB:  // No compression
327                case BI_RLE8:  // 8-bit RLE compression
328                case BI_RLE4:  // 4-bit RLE compression
329
330                    // Read in the palette
331                    int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
332                    int sizeOfPalette = numberOfEntries * 4;
333                    palette = new byte[sizeOfPalette];
334                    iis.readFully(palette, 0, sizeOfPalette);
335
336                    metadata.palette = palette;
337                    metadata.paletteSize = numberOfEntries;
338
339                    if (bitsPerPixel == 1) {
340                        imageType = VERSION_3_1_BIT;
341                    } else if (bitsPerPixel == 4) {
342                        imageType = VERSION_3_4_BIT;
343                    } else if (bitsPerPixel == 8) {
344                        imageType = VERSION_3_8_BIT;
345                    } else if (bitsPerPixel == 24) {
346                        imageType = VERSION_3_24_BIT;
347                    } else if (bitsPerPixel == 16) {
348                        imageType = VERSION_3_NT_16_BIT;
349                            
350                        redMask = 0x7C00;
351                        greenMask = 0x3E0;
352                        blueMask =  (1 << 5) - 1;// 0x1F;
353                        metadata.redMask = redMask;
354                        metadata.greenMask = greenMask;
355                        metadata.blueMask = blueMask;
356                    } else if (bitsPerPixel == 32) {
357                        imageType = VERSION_3_NT_32_BIT;
358                        redMask   = 0x00FF0000;
359                        greenMask = 0x0000FF00;
360                        blueMask  = 0x000000FF;
361                        metadata.redMask = redMask;
362                        metadata.greenMask = greenMask;
363                        metadata.blueMask = blueMask;
364                    }
365
366                    metadata.bmpVersion = VERSION_3;
367                    break;
368
369                case BI_BITFIELDS:
370
371                    if (bitsPerPixel == 16) {
372                        imageType = VERSION_3_NT_16_BIT;
373                    } else if (bitsPerPixel == 32) {
374                        imageType = VERSION_3_NT_32_BIT;
375                    }
376
377                    // BitsField encoding
378                    redMask = (int)iis.readUnsignedInt();
379                    greenMask = (int)iis.readUnsignedInt();
380                    blueMask = (int)iis.readUnsignedInt();
381                    metadata.redMask = redMask;
382                    metadata.greenMask = greenMask;
383                    metadata.blueMask = blueMask;
384
385                    if (colorsUsed != 0) {
386                        // there is a palette
387                        sizeOfPalette = (int)colorsUsed*4;
388                        palette = new byte[sizeOfPalette];
389                        iis.readFully(palette, 0, sizeOfPalette);
390                        metadata.palette = palette;
391                        metadata.paletteSize = (int)colorsUsed;
392                    }
393                    metadata.bmpVersion = VERSION_3_NT;
394
395                    break;
396                default:
397                    throw new
398                        RuntimeException(I18N.getString("BMPImageReader2"));
399                }
400            } else if (size == 108 || size == 124) {
401                // Windows 4.x BMP
402                if (size == 108)
403                    metadata.bmpVersion = VERSION_4;
404                else if (size == 124)
405                    metadata.bmpVersion = VERSION_5;
406
407                // rgb masks, valid only if comp is BI_BITFIELDS
408                redMask = (int)iis.readUnsignedInt();
409                greenMask = (int)iis.readUnsignedInt();
410                blueMask = (int)iis.readUnsignedInt();
411                // Only supported for 32bpp BI_RGB argb
412                alphaMask = (int)iis.readUnsignedInt();
413                long csType = iis.readUnsignedInt();
414                int redX = iis.readInt();
415                int redY = iis.readInt();
416                int redZ = iis.readInt();
417                int greenX = iis.readInt();
418                int greenY = iis.readInt();
419                int greenZ = iis.readInt();
420                int blueX = iis.readInt();
421                int blueY = iis.readInt();
422                int blueZ = iis.readInt();
423                long gammaRed = iis.readUnsignedInt();
424                long gammaGreen = iis.readUnsignedInt();
425                long gammaBlue = iis.readUnsignedInt();
426
427                if (size == 124) {
428                    metadata.intent = iis.readInt();
429                    profileData = iis.readInt();
430                    profileSize = iis.readInt();
431                    iis.skipBytes(4);
432                }
433
434                metadata.colorSpace = (int)csType;
435
436                if (csType == LCS_CALIBRATED_RGB) {
437                    // All the new fields are valid only for this case
438                    metadata.redX = redX;
439                    metadata.redY = redY;
440                    metadata.redZ = redZ;
441                    metadata.greenX = greenX;
442                    metadata.greenY = greenY;
443                    metadata.greenZ = greenZ;
444                    metadata.blueX = blueX;
445                    metadata.blueY = blueY;
446                    metadata.blueZ = blueZ;
447                    metadata.gammaRed = (int)gammaRed;
448                    metadata.gammaGreen = (int)gammaGreen;
449                    metadata.gammaBlue = (int)gammaBlue;
450                }
451
452                // Read in the palette
453                int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
454                int sizeOfPalette = numberOfEntries*4;
455                palette = new byte[sizeOfPalette];
456                iis.readFully(palette, 0, sizeOfPalette);
457                metadata.palette = palette;
458                metadata.paletteSize = numberOfEntries;
459
460                switch ((int)compression) {
461                case BI_JPEG:
462                case BI_PNG:
463                    if (size == 108) {
464                        imageType = VERSION_4_XP_EMBEDDED;
465                    } else if (size == 124) {
466                        imageType = VERSION_5_XP_EMBEDDED;
467                    }
468                    break;
469                default:
470                    if (bitsPerPixel == 1) {
471                        imageType = VERSION_4_1_BIT;
472                    } else if (bitsPerPixel == 4) {
473                        imageType = VERSION_4_4_BIT;
474                    } else if (bitsPerPixel == 8) {
475                        imageType = VERSION_4_8_BIT;
476                    } else if (bitsPerPixel == 16) {
477                        imageType = VERSION_4_16_BIT;
478                        if ((int)compression == BI_RGB) {
479                            redMask = 0x7C00;
480                            greenMask = 0x3E0;
481                            blueMask = 0x1F;
482                        }
483                    } else if (bitsPerPixel == 24) {
484                        imageType = VERSION_4_24_BIT;
485                    } else if (bitsPerPixel == 32) {
486                        imageType = VERSION_4_32_BIT;
487                        if ((int)compression == BI_RGB) {
488                            redMask   = 0x00FF0000;
489                            greenMask = 0x0000FF00;
490                            blueMask  = 0x000000FF;
491                        }
492                    }
493                    
494                    metadata.redMask = redMask;
495                    metadata.greenMask = greenMask;
496                    metadata.blueMask = blueMask;
497                    metadata.alphaMask = alphaMask;
498                }
499            } else {
500                throw new
501                    RuntimeException(I18N.getString("BMPImageReader3"));
502            }
503        }
504
505        if (height > 0) {
506            // bottom up image
507            isBottomUp = true;
508        } else {
509            // top down image
510            isBottomUp = false;
511            height = Math.abs(height);
512        }
513
514        // Reset Image Layout so there's only one tile.
515        //Define the color space
516        ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
517        if (metadata.colorSpace == PROFILE_LINKED ||
518            metadata.colorSpace == PROFILE_EMBEDDED) {
519
520            iis.mark();
521            iis.skipBytes(profileData - size);
522            byte[] profile = new byte[profileSize];
523            iis.readFully(profile, 0, profileSize);
524            iis.reset();
525
526            try {
527                if (metadata.colorSpace == PROFILE_LINKED)
528                    colorSpace =
529                        new ICC_ColorSpace(ICC_Profile.getInstance(new String(profile)));
530                else
531                    colorSpace =
532                        new ICC_ColorSpace(ICC_Profile.getInstance(profile));
533            } catch (Exception e) {
534                colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
535            }
536        }
537
538        if (bitsPerPixel == 0 ||
539            compression == BI_JPEG || compression == BI_PNG )
540        {
541            // the colorModel and sampleModel will be initialzed
542            // by the  reader of embedded image
543            colorModel = null;
544            sampleModel = null;
545        } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
546            // When number of bitsPerPixel is <= 8, we use IndexColorModel.
547            numBands = 1;
548
549            if (bitsPerPixel == 8) {
550                int[] bandOffsets = new int[numBands];
551                for (int i = 0; i < numBands; i++) {
552                    bandOffsets[i] = numBands -1 -i;
553                }
554                sampleModel =
555                    new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
556                                                    width, height,
557                                                    numBands,
558                                                    numBands * width,
559                                                    bandOffsets);
560            } else {
561                // 1 and 4 bit pixels can be stored in a packed format.
562                sampleModel =
563                    new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
564                                                    width, height,
565                                                    bitsPerPixel);
566            }
567
568            // Create IndexColorModel from the palette.
569            byte r[], g[], b[];
570            if (imageType == VERSION_2_1_BIT ||
571                imageType == VERSION_2_4_BIT ||
572                imageType == VERSION_2_8_BIT) {
573
574
575                size = palette.length/3;
576
577                if (size > 256) {
578                    size = 256;
579                }
580
581                int off;
582                r = new byte[(int)size];
583                g = new byte[(int)size];
584                b = new byte[(int)size];
585                for (int i=0; i<(int)size; i++) {
586                    off = 3 * i;
587                    b[i] = palette[off];
588                    g[i] = palette[off+1];
589                    r[i] = palette[off+2];
590                }
591            } else {
592                size = palette.length/4;
593
594                if (size > 256) {
595                    size = 256;
596                }
597
598                int off;
599                r = new byte[(int)size];
600                g = new byte[(int)size];
601                b = new byte[(int)size];
602                for (int i=0; i<size; i++) {
603                    off = 4 * i;
604                    b[i] = palette[off];
605                    g[i] = palette[off+1];
606                    r[i] = palette[off+2];
607                }
608            }
609
610            if (ImageUtil.isIndicesForGrayscale(r, g, b))
611                colorModel =
612                    ImageUtil.createColorModel(null, sampleModel);
613            else
614                colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
615        } else if (bitsPerPixel == 16) {
616            numBands = 3;
617            sampleModel =
618                new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
619                                                 width, height,
620                                                 new int[] {redMask, greenMask, blueMask});
621
622            colorModel =
623                new DirectColorModel(colorSpace,
624                                     16, redMask, greenMask, blueMask, 0,
625                                     false, DataBuffer.TYPE_USHORT);
626                   
627        } else if (bitsPerPixel == 32) {
628            numBands = alphaMask == 0 ? 3 : 4;
629
630            if (redMask == 0 || greenMask == 0 || blueMask ==0) {
631                redMask = 0xFF0000;
632                greenMask = 0xFF00;
633                blueMask = 0xFF;
634                alphaMask= 0xFF000000;
635            }
636
637            // The number of bands in the SampleModel is determined by
638            // the length of the mask array passed in.
639            int[] bitMasks = numBands == 3 ?
640                new int[] {redMask, greenMask, blueMask} :
641                new int[] {redMask, greenMask, blueMask, alphaMask};
642
643                sampleModel =
644                    new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
645                                                     width, height,
646                                                     bitMasks);
647
648                colorModel =
649                    new DirectColorModel(colorSpace,
650                                         32, redMask, greenMask, blueMask, alphaMask,
651                                         false, DataBuffer.TYPE_INT);
652        } else {
653            numBands = 3;
654            // Create SampleModel
655            int[] bandOffsets = new int[numBands];
656            for (int i = 0; i < numBands; i++) {
657                bandOffsets[i] = numBands -1 -i;
658            }
659
660            sampleModel =
661                new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
662                                                width, height,
663                                                numBands,
664                                                numBands * width,
665                                                bandOffsets);
666
667            colorModel =
668                ImageUtil.createColorModel(colorSpace, sampleModel);
669        }
670
671        originalSampleModel = sampleModel;
672        originalColorModel = colorModel;
673
674        // Reset to the start of bitmap; then jump to the
675        //start of image data
676        iis.reset();
677        iis.skipBytes(bitmapOffset);
678        gotHeader = true;
679        
680        // Store the stream position where the image data starts
681        imageDataOffset = iis.getStreamPosition();       
682    }
683
684    public Iterator getImageTypes(int imageIndex)
685      throws IOException {
686        checkIndex(imageIndex);
687        readHeader();
688        ArrayList list = new ArrayList(1);
689        list.add(new ImageTypeSpecifier(originalColorModel,
690                                        originalSampleModel));
691        return list.iterator();
692    }
693
694    public ImageReadParam getDefaultReadParam() {
695        return new ImageReadParam();
696    }
697
698    public IIOMetadata getImageMetadata(int imageIndex)
699      throws IOException {
700        checkIndex(imageIndex);
701        if (metadata == null) {
702            readHeader();
703        }
704        return metadata;
705    }
706
707    public IIOMetadata getStreamMetadata() throws IOException {
708        return null;
709    }
710
711    public boolean isRandomAccessEasy(int imageIndex) throws IOException {
712        checkIndex(imageIndex);
713        readHeader();
714        return metadata.compression == BI_RGB;
715    }
716
717    public BufferedImage read(int imageIndex, ImageReadParam param)
718        throws IOException {
719
720        if (iis == null) {
721            throw new IllegalStateException(I18N.getString("BMPImageReader5"));
722        }
723
724        checkIndex(imageIndex);
725        clearAbortRequest();
726        processImageStarted(imageIndex);
727
728        if (param == null)
729            param = getDefaultReadParam();
730
731        //read header
732        readHeader();
733
734        sourceRegion = new Rectangle(0, 0, 0, 0);
735        destinationRegion = new Rectangle(0, 0, 0, 0);
736
737        computeRegions(param, this.width, this.height,
738                       param.getDestination(),
739                       sourceRegion,
740                       destinationRegion);
741
742        scaleX = param.getSourceXSubsampling();
743        scaleY = param.getSourceYSubsampling();
744
745        // If the destination band is set used it
746        sourceBands = param.getSourceBands();
747        destBands = param.getDestinationBands();
748
749        seleBand = (sourceBands != null) && (destBands != null);
750        noTransform =
751            destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
752            seleBand;
753
754        if (!seleBand) {
755            sourceBands = new int[numBands];
756            destBands = new int[numBands];
757            for (int i = 0; i < numBands; i++)
758                destBands[i] = sourceBands[i] = i;
759        }
760
761        // If the destination is provided, then use it.  Otherwise, create new one
762        bi = param.getDestination();
763
764        // Get the image data.
765        WritableRaster raster = null;
766
767        if (bi == null) {
768            if (sampleModel != null && colorModel != null) {
769                sampleModel =
770                    sampleModel.createCompatibleSampleModel(destinationRegion.x +
771                                                            destinationRegion.width,
772                                                            destinationRegion.y +
773                                                            destinationRegion.height);
774                if (seleBand)
775                    sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
776                raster = Raster.createWritableRaster(sampleModel, new Point());
777                bi = new BufferedImage(colorModel, raster, false, null);
778            }
779        } else {
780            raster = bi.getWritableTile(0, 0);
781            sampleModel = bi.getSampleModel();
782            colorModel = bi.getColorModel();
783
784            noTransform &=  destinationRegion.equals(raster.getBounds());
785        }
786
787        byte bdata[] = null; // buffer for byte data
788        short sdata[] = null; // buffer for short data
789        int idata[] = null; // buffer for int data
790
791        // the sampleModel can be null in case of embedded image
792        if (sampleModel != null) {
793            if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
794                bdata = (byte[])
795                    ((DataBufferByte)raster.getDataBuffer()).getData();
796            else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
797                sdata = (short[])
798                    ((DataBufferUShort)raster.getDataBuffer()).getData();
799            else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
800                idata = (int[])
801                    ((DataBufferInt)raster.getDataBuffer()).getData();
802        }
803
804        // There should only be one tile.
805        switch(imageType) {
806
807        case VERSION_2_1_BIT:
808            // no compression
809            read1Bit(bdata);
810            break;
811
812        case VERSION_2_4_BIT:
813            // no compression
814            read4Bit(bdata);
815            break;
816
817        case VERSION_2_8_BIT:
818            // no compression
819            read8Bit(bdata);
820            break;
821
822        case VERSION_2_24_BIT:
823            // no compression
824            read24Bit(bdata);
825            break;
826
827        case VERSION_3_1_BIT:
828            // 1-bit images cannot be compressed.
829            read1Bit(bdata);
830            break;
831
832        case VERSION_3_4_BIT:
833            switch((int)compression) {
834            case BI_RGB:
835                read4Bit(bdata);
836                break;
837
838            case BI_RLE4:
839                readRLE4(bdata);
840                break;
841
842            default:
843                throw new
844                    RuntimeException(I18N.getString("BMPImageReader1"));
845            }
846            break;
847
848        case VERSION_3_8_BIT:
849            switch((int)compression) {
850            case BI_RGB:
851                read8Bit(bdata);
852                break;
853
854            case BI_RLE8:
855                readRLE8(bdata);
856                break;
857
858            default:
859                throw new
860                    RuntimeException(I18N.getString("BMPImageReader1"));
861            }
862
863            break;
864
865        case VERSION_3_24_BIT:
866            // 24-bit images are not compressed
867            read24Bit(bdata);
868            break;
869
870        case VERSION_3_NT_16_BIT:
871            read16Bit(sdata);
872            break;
873
874        case VERSION_3_NT_32_BIT:
875            read32Bit(idata);
876            break;
877
878        case VERSION_3_XP_EMBEDDED:
879        case VERSION_4_XP_EMBEDDED:
880        case VERSION_5_XP_EMBEDDED:
881            bi = readEmbedded((int)compression, bi, param);
882            break;
883
884        case VERSION_4_1_BIT:
885            read1Bit(bdata);
886            break;
887
888        case VERSION_4_4_BIT:
889            switch((int)compression) {
890
891            case BI_RGB:
892                read4Bit(bdata);
893                break;
894
895            case BI_RLE4:
896                readRLE4(bdata);
897                break;
898
899            default:
900                throw new
901                    RuntimeException(I18N.getString("BMPImageReader1"));
902            }
903
904        case VERSION_4_8_BIT:
905            switch((int)compression) {
906
907            case BI_RGB:
908                read8Bit(bdata);
909                break;
910
911            case BI_RLE8:
912                readRLE8(bdata);
913                break;
914
915            default:
916                throw new
917                    RuntimeException(I18N.getString("BMPImageReader1"));
918            }
919            break;
920
921        case VERSION_4_16_BIT:
922            read16Bit(sdata);
923            break;
924
925        case VERSION_4_24_BIT:
926            read24Bit(bdata);
927            break;
928
929        case VERSION_4_32_BIT:
930            read32Bit(idata);
931            break;
932        }
933
934        if (abortRequested())
935            processReadAborted();
936        else
937            processImageComplete();
938
939        return bi;
940    }
941
942    public boolean canReadRaster() {
943        return true;
944    }
945
946    public Raster readRaster(int imageIndex,
947                             ImageReadParam param) throws IOException {
948        BufferedImage bi = read(imageIndex, param);
949        return bi.getData();
950    }
951
952    private void resetHeaderInfo() {
953        gotHeader = false;
954        bi = null;
955        sampleModel = originalSampleModel = null;
956        colorModel = originalColorModel = null;
957    }
958
959    public void reset() {
960        super.reset();
961        iis = null;
962        resetHeaderInfo();
963    }
964
965    // Deal with 1 Bit images using IndexColorModels
966    private void read1Bit(byte[] bdata) throws IOException {
967        int bytesPerScanline = (width + 7) / 8;
968        int padding = bytesPerScanline % 4;
969        if (padding != 0) {
970            padding = 4 - padding;
971        }
972
973        int lineLength = bytesPerScanline + padding;
974
975        if (noTransform) {
976            int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
977
978            for (int i=0; i<height; i++) {
979                if (abortRequested()) {
980                    break;
981                }
982                iis.readFully(bdata, j, bytesPerScanline);
983                iis.skipBytes(padding);
984                j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
985                processImageUpdate(bi, 0, i,
986                                   destinationRegion.width, 1, 1, 1,
987                                   new int[]{0});
988                processImageProgress(100.0F * i/destinationRegion.height);
989            }
990        } else {
991            byte[] buf = new byte[lineLength];
992            int lineStride =
993                ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
994
995            if (isBottomUp) {
996                int lastLine =
997                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
998                iis.skipBytes(lineLength * (height - 1 - lastLine));
999            } else
1000                iis.skipBytes(lineLength * sourceRegion.y);
1001
1002            int skipLength = lineLength * (scaleY - 1);
1003
1004            // cache the values to avoid duplicated computation
1005            int[] srcOff = new int[destinationRegion.width];
1006            int[] destOff = new int[destinationRegion.width];
1007            int[] srcPos = new int[destinationRegion.width];
1008            int[] destPos = new int[destinationRegion.width];
1009
1010            for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1011                 i < destinationRegion.x + destinationRegion.width;
1012                 i++, j++, x += scaleX) {
1013                srcPos[j] = x >> 3;
1014                srcOff[j] = 7 - (x & 7);
1015                destPos[j] = i >> 3;
1016                destOff[j] = 7 - (i & 7);
1017            }
1018
1019            int k = destinationRegion.y * lineStride;
1020            if (isBottomUp)
1021                k += (destinationRegion.height - 1) * lineStride;
1022
1023            for (int j = 0, y = sourceRegion.y;
1024                 j < destinationRegion.height; j++, y+=scaleY) {
1025
1026                if (abortRequested())
1027                    break;
1028                iis.read(buf, 0, lineLength);
1029                for (int i = 0; i < destinationRegion.width; i++) {
1030                    //get the bit and assign to the data buffer of the raster
1031                    int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
1032                    bdata[k + destPos[i]] |= v << destOff[i];
1033                }
1034
1035                k += isBottomUp ? -lineStride : lineStride;
1036                iis.skipBytes(skipLength);
1037                processImageUpdate(bi, 0, j,
1038                                   destinationRegion.width, 1, 1, 1,
1039                                   new int[]{0});
1040                processImageProgress(100.0F*j/destinationRegion.height);
1041            }
1042        }
1043    }
1044
1045    // Method to read a 4 bit BMP image data
1046    private void read4Bit(byte[] bdata) throws IOException {
1047
1048        int bytesPerScanline = (width + 1) / 2;
1049
1050        // Padding bytes at the end of each scanline
1051        int padding = bytesPerScanline % 4;
1052        if (padding != 0)
1053            padding = 4 - padding;
1054
1055        int lineLength = bytesPerScanline + padding;
1056
1057        if (noTransform) {
1058            int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
1059
1060            for (int i=0; i<height; i++) {
1061                if (abortRequested()) {
1062                    break;
1063                }
1064                iis.readFully(bdata, j, bytesPerScanline);
1065                iis.skipBytes(padding);
1066                j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1067                processImageUpdate(bi, 0, i,
1068                                   destinationRegion.width, 1, 1, 1,
1069                                   new int[]{0});
1070                processImageProgress(100.0F * i/destinationRegion.height);
1071            }
1072        } else {
1073            byte[] buf = new byte[lineLength];
1074            int lineStride =
1075                ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1076
1077            if (isBottomUp) {
1078                int lastLine =
1079                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1080                iis.skipBytes(lineLength * (height - 1 - lastLine));
1081            } else
1082                iis.skipBytes(lineLength * sourceRegion.y);
1083
1084            int skipLength = lineLength * (scaleY - 1);
1085
1086            // cache the values to avoid duplicated computation
1087            int[] srcOff = new int[destinationRegion.width];
1088            int[] destOff = new int[destinationRegion.width];
1089            int[] srcPos = new int[destinationRegion.width];
1090            int[] destPos = new int[destinationRegion.width];
1091
1092            for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1093                 i < destinationRegion.x + destinationRegion.width;
1094                 i++, j++, x += scaleX) {
1095                srcPos[j] = x >> 1;
1096                srcOff[j] = (1 - (x & 1)) << 2;
1097                destPos[j] = i >> 1;
1098                destOff[j] = (1 - (i & 1)) << 2;
1099            }
1100
1101            int k = destinationRegion.y * lineStride;
1102            if (isBottomUp)
1103                k += (destinationRegion.height - 1) * lineStride;
1104
1105            for (int j = 0, y = sourceRegion.y;
1106                 j < destinationRegion.height; j++, y+=scaleY) {
1107
1108                if (abortRequested())
1109                    break;
1110                iis.read(buf, 0, lineLength);
1111                for (int i = 0; i < destinationRegion.width; i++) {
1112                    //get the bit and assign to the data buffer of the raster
1113                    int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
1114                    bdata[k + destPos[i]] |= v << destOff[i];
1115                }
1116
1117                k += isBottomUp ? -lineStride : lineStride;
1118                iis.skipBytes(skipLength);
1119                processImageUpdate(bi, 0, j,
1120                                   destinationRegion.width, 1, 1, 1,
1121                                   new int[]{0});
1122                processImageProgress(100.0F*j/destinationRegion.height);
1123            }
1124        }
1125    }
1126
1127    // Method to read 8 bit BMP image data
1128    private void read8Bit(byte[] bdata) throws IOException {
1129
1130        // Padding bytes at the end of each scanline
1131        int padding = width % 4;
1132        if (padding != 0) {
1133            padding = 4 - padding;
1134        }
1135
1136        int lineLength = width + padding;
1137
1138        if (noTransform) {
1139            int j = isBottomUp ? (height -1) * width : 0;
1140
1141            for (int i=0; i<height; i++) {
1142                if (abortRequested()) {
1143                    break;
1144                }
1145                iis.readFully(bdata, j, width);
1146                iis.skipBytes(padding);
1147                j += isBottomUp ? -width : width;
1148                processImageUpdate(bi, 0, i,
1149                                   destinationRegion.width, 1, 1, 1,
1150                                   new int[]{0});
1151                processImageProgress(100.0F * i/destinationRegion.height);
1152            }
1153        } else {
1154            byte[] buf = new byte[lineLength];
1155            int lineStride =
1156                ((ComponentSampleModel)sampleModel).getScanlineStride();
1157
1158            if (isBottomUp) {
1159                int lastLine =
1160                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1161                iis.skipBytes(lineLength * (height - 1 - lastLine));
1162            } else
1163                iis.skipBytes(lineLength * sourceRegion.y);
1164
1165            int skipLength = lineLength * (scaleY - 1);
1166
1167            int k = destinationRegion.y * lineStride;
1168            if (isBottomUp)
1169                k += (destinationRegion.height - 1) * lineStride;
1170            k += destinationRegion.x;
1171
1172            for (int j = 0, y = sourceRegion.y;
1173                 j < destinationRegion.height; j++, y+=scaleY) {
1174
1175                if (abortRequested())
1176                    break;
1177                iis.read(buf, 0, lineLength);
1178                for (int i = 0, m = sourceRegion.x;
1179                     i < destinationRegion.width; i++, m += scaleX) {
1180                    //get the bit and assign to the data buffer of the raster
1181                    bdata[k + i] = buf[m];
1182                }
1183
1184                k += isBottomUp ? -lineStride : lineStride;
1185                iis.skipBytes(skipLength);
1186                processImageUpdate(bi, 0, j,
1187                                   destinationRegion.width, 1, 1, 1,
1188                                   new int[]{0});
1189                processImageProgress(100.0F*j/destinationRegion.height);
1190            }
1191        }
1192    }
1193
1194    // Method to read 24 bit BMP image data
1195    private void read24Bit(byte[] bdata) throws IOException {
1196        // Padding bytes at the end of each scanline
1197        // width * bitsPerPixel should be divisible by 32
1198        int padding = width * 3 % 4;
1199        if ( padding != 0)
1200            padding = 4 - padding;
1201
1202        int lineStride = width * 3;
1203        int lineLength = lineStride + padding;
1204
1205        if (noTransform) {
1206            int j = isBottomUp ? (height -1) * width * 3 : 0;
1207
1208            for (int i=0; i<height; i++) {
1209                if (abortRequested()) {
1210                    break;
1211                }
1212                iis.readFully(bdata, j, lineStride);
1213                iis.skipBytes(padding);
1214                j += isBottomUp ? -lineStride : lineStride;
1215                processImageUpdate(bi, 0, i,
1216                                   destinationRegion.width, 1, 1, 1,
1217                                   new int[]{0});
1218                processImageProgress(100.0F * i/destinationRegion.height);
1219            }
1220        } else {
1221            byte[] buf = new byte[lineLength];
1222            lineStride =
1223                ((ComponentSampleModel)sampleModel).getScanlineStride();
1224
1225            if (isBottomUp) {
1226                int lastLine =
1227                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1228                iis.skipBytes(lineLength * (height - 1 - lastLine));
1229            } else
1230                iis.skipBytes(lineLength * sourceRegion.y);
1231
1232            int skipLength = lineLength * (scaleY - 1);
1233
1234            int k = destinationRegion.y * lineStride;
1235            if (isBottomUp)
1236                k += (destinationRegion.height - 1) * lineStride;
1237            k += destinationRegion.x * 3;
1238
1239            for (int j = 0, y = sourceRegion.y;
1240                 j < destinationRegion.height; j++, y+=scaleY) {
1241
1242                if (abortRequested())
1243                    break;
1244                iis.read(buf, 0, lineLength);
1245                for (int i = 0, m = 3 * sourceRegion.x;
1246                     i < destinationRegion.width; i++, m += 3 * scaleX) {
1247                    //get the bit and assign to the data buffer of the raster
1248                    int n = 3 * i + k;
1249                    for (int b = 0; b < destBands.length; b++)
1250                        bdata[n + destBands[b]] = buf[m + sourceBands[b]];
1251                }
1252
1253                k += isBottomUp ? -lineStride : lineStride;
1254                iis.skipBytes(skipLength);
1255                processImageUpdate(bi, 0, j,
1256                                   destinationRegion.width, 1, 1, 1,
1257                                   new int[]{0});
1258                processImageProgress(100.0F*j/destinationRegion.height);
1259            }
1260        }
1261    }
1262
1263    private void read16Bit(short sdata[]) throws IOException {
1264        // Padding bytes at the end of each scanline
1265        // width * bitsPerPixel should be divisible by 32
1266        int padding = width * 2 % 4;
1267        
1268        if ( padding != 0)
1269            padding = 4 - padding;
1270        
1271        int lineLength = width + padding / 2;
1272        
1273        if (noTransform) {
1274            int j = isBottomUp ? (height -1) * width : 0;
1275            for (int i=0; i<height; i++) {
1276                if (abortRequested()) {
1277                    break;
1278                }
1279
1280                iis.readFully(sdata, j, width);
1281                iis.skipBytes(padding);
1282                j += isBottomUp ? -width : width;
1283                processImageUpdate(bi, 0, i,
1284                                   destinationRegion.width, 1, 1, 1,
1285                                   new int[]{0});
1286                processImageProgress(100.0F * i/destinationRegion.height);
1287            }
1288        } else {
1289            short[] buf = new short[lineLength];
1290            int lineStride =
1291                ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1292            
1293            if (isBottomUp) {
1294                int lastLine =
1295                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1296                iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
1297            } else
1298                iis.skipBytes(lineLength * sourceRegion.y << 1);
1299            
1300            int skipLength = lineLength * (scaleY - 1) << 1;
1301            
1302            int k = destinationRegion.y * lineStride;
1303            if (isBottomUp)
1304                k += (destinationRegion.height - 1) * lineStride;
1305            k += destinationRegion.x;
1306            
1307            for (int j = 0, y = sourceRegion.y;
1308                 j < destinationRegion.height; j++, y+=scaleY) {
1309                
1310                if (abortRequested())
1311                    break;
1312                iis.readFully(buf, 0, lineLength);
1313                for (int i = 0, m = sourceRegion.x;
1314                     i < destinationRegion.width; i++, m += scaleX) {
1315                    //get the bit and assign to the data buffer of the raster
1316                    sdata[k + i] = buf[m];
1317                }
1318
1319                k += isBottomUp ? -lineStride : lineStride;
1320                iis.skipBytes(skipLength);
1321                processImageUpdate(bi, 0, j,
1322                                   destinationRegion.width, 1, 1, 1,
1323                                   new int[]{0});
1324                processImageProgress(100.0F*j/destinationRegion.height);
1325            }
1326        }
1327    }
1328
1329    private void read32Bit(int idata[]) throws IOException {
1330        if (noTransform) {
1331            int j = isBottomUp ? (height -1) * width : 0;
1332
1333            for (int i=0; i<height; i++) {
1334                if (abortRequested()) {
1335                    break;
1336                }
1337                iis.readFully(idata, j, width);
1338                j += isBottomUp ? -width : width;
1339                processImageUpdate(bi, 0, i,
1340                                   destinationRegion.width, 1, 1, 1,
1341                                   new int[]{0});
1342                processImageProgress(100.0F * i/destinationRegion.height);
1343            }
1344        } else {
1345            int[] buf = new int[width];
1346            int lineStride =
1347                ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1348
1349            if (isBottomUp) {
1350                int lastLine =
1351                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1352                iis.skipBytes(width * (height - 1 - lastLine) << 2);
1353            } else
1354                iis.skipBytes(width * sourceRegion.y << 2);
1355
1356            int skipLength = width * (scaleY - 1) << 2;
1357
1358            int k = destinationRegion.y * lineStride;
1359            if (isBottomUp)
1360                k += (destinationRegion.height - 1) * lineStride;
1361            k += destinationRegion.x;
1362
1363            for (int j = 0, y = sourceRegion.y;
1364                 j < destinationRegion.height; j++, y+=scaleY) {
1365
1366                if (abortRequested())
1367                    break;
1368                iis.readFully(buf, 0, width);
1369                for (int i = 0, m = sourceRegion.x;
1370                     i < destinationRegion.width; i++, m += scaleX) {
1371                    //get the bit and assign to the data buffer of the raster
1372                    idata[k + i] = buf[m];
1373                }
1374
1375                k += isBottomUp ? -lineStride : lineStride;
1376                iis.skipBytes(skipLength);
1377                processImageUpdate(bi, 0, j,
1378                                   destinationRegion.width, 1, 1, 1,
1379                                   new int[]{0});
1380                processImageProgress(100.0F*j/destinationRegion.height);
1381            }
1382        }
1383    }
1384
1385    private void readRLE8(byte bdata[]) throws IOException {
1386        // If imageSize field is not provided, calculate it.
1387        int imSize = (int)imageSize;
1388        if (imSize == 0) {
1389            imSize = (int)(bitmapFileSize - bitmapOffset);
1390        }
1391
1392        int padding = 0;
1393        // If width is not 32 bit aligned, then while uncompressing each
1394        // scanline will have padding bytes, calculate the amount of padding
1395        int remainder = width % 4;
1396        if (remainder != 0) {
1397            padding = 4 - remainder;
1398        }
1399
1400        // Read till we have the whole image
1401        byte values[] = new byte[imSize];
1402        int bytesRead = 0;
1403        iis.readFully(values, 0, imSize);
1404
1405        // Since data is compressed, decompress it
1406        decodeRLE8(imSize, padding, values, bdata);
1407    }
1408
1409    private void decodeRLE8(int imSize,
1410                            int padding,
1411                            byte[] values,
1412                            byte[] bdata) throws IOException {
1413
1414        byte val[] = new byte[width * height];
1415        int count = 0, l = 0;
1416        int value;
1417        boolean flag = false;
1418        int lineNo = isBottomUp ? height - 1 : 0;
1419        int lineStride =
1420            ((ComponentSampleModel)sampleModel).getScanlineStride();
1421        int finished = 0;
1422
1423        while (count != imSize) {
1424            value = values[count++] & 0xff;
1425            if (value == 0) {
1426                switch(values[count++] & 0xff) {
1427
1428                case 0:
1429                case 1:
1430                    // 0 is End-of-scanline marker, 1 is End-of-RLE marker
1431                    // In either case, we want to copy the just decoded 
1432                    // scanline from val array to bdata array
1433                    if (lineNo >= sourceRegion.y &&
1434                        lineNo < sourceRegion.y + sourceRegion.height) {
1435                        if (noTransform) {
1436                            int pos = lineNo * width;
1437                            for(int i = 0; i < width; i++)
1438                                bdata[pos++] = val[i];
1439                            processImageUpdate(bi, 0, lineNo,
1440                                               destinationRegion.width, 1, 1, 1,
1441                                               new int[]{0});
1442                            finished++;
1443                        } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1444                            int currentLine = (lineNo - sourceRegion.y) / scaleY +
1445                                destinationRegion.y;
1446                            int pos = currentLine * lineStride;
1447                            pos += destinationRegion.x;
1448                            for (int i = sourceRegion.x;
1449                                 i < sourceRegion.x + sourceRegion.width;
1450                                 i += scaleX)
1451                                bdata[pos++] = val[i];
1452                            processImageUpdate(bi, 0, currentLine,
1453                                               destinationRegion.width, 1, 1, 1,
1454                                               new int[]{0});
1455                            finished++;
1456                        }
1457                    }
1458                    processImageProgress(100.0F * finished / destinationRegion.height);
1459                    lineNo += isBottomUp ? -1 : 1;
1460                    l = 0;
1461
1462                    if (abortRequested()) {
1463                        break;
1464                    }
1465
1466                    // End-of-RLE marker
1467                    if ((values[count-1] & 0xff) == 1)
1468                        flag = true;
1469
1470                    break;
1471
1472                case 2:
1473                    // delta or vector marker
1474                    int xoff = values[count++] & 0xff;
1475                    int yoff = values[count] & 0xff;
1476                    // Move to the position xoff, yoff down
1477                    l += xoff + yoff*width;
1478                    break;
1479
1480                default:
1481                    int end = values[count-1] & 0xff;
1482                    for (int i=0; i<end; i++) {
1483                        val[l++] = (byte)(values[count++] & 0xff);
1484                    }
1485
1486                    // Whenever end pixels can fit into odd number of bytes,
1487                    // an extra padding byte will be present, so skip that.
1488                    if ((end & 1) == 1) {
1489                        count++;
1490                    }
1491                }
1492            } else {
1493                for (int i=0; i<value; i++) {
1494                    val[l++] = (byte)(values[count] & 0xff);
1495                }
1496
1497                count++;
1498            }
1499
1500            // If End-of-RLE data, then exit the while loop
1501            if (flag) {
1502                break;
1503            }
1504        }
1505    }
1506
1507    private void readRLE4(byte[] bdata) throws IOException {
1508
1509        // If imageSize field is not specified, calculate it.
1510        int imSize = (int)imageSize;
1511        if (imSize == 0) {
1512            imSize = (int)(bitmapFileSize - bitmapOffset);
1513        }
1514        
1515        int padding = 0;
1516        // If width is not 32 byte aligned, then while uncompressing each
1517        // scanline will have padding bytes, calculate the amount of padding
1518        int remainder = width % 4;
1519        if (remainder != 0) {
1520            padding = 4 - remainder;
1521        }
1522        
1523        // Read till we have the whole image
1524        byte[] values = new byte[imSize];
1525        iis.readFully(values, 0, imSize);
1526
1527        // Decompress the RLE4 compressed data.
1528        decodeRLE4(imSize, padding, values, bdata);
1529    }
1530
1531    private void decodeRLE4(int imSize,
1532                            int padding,
1533                            byte[] values,
1534                            byte[] bdata) throws IOException {
1535        byte[] val = new byte[width];
1536        int count = 0, l = 0;
1537        int value;
1538        boolean flag = false;
1539        int lineNo = isBottomUp ? height - 1 : 0;
1540        int lineStride =
1541            ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1542        int finished = 0;
1543        
1544        while (count != imSize) {
1545            
1546            value = values[count++] & 0xFF;
1547            if (value == 0) {
1548                
1549                
1550                // Absolute mode
1551                switch(values[count++] & 0xFF) {
1552                    
1553                case 0:
1554                case 1:
1555                    // 0 is End-of-scanline marker, 1 is End-of-RLE marker
1556                    // In either case, we want to copy the just decoded 
1557                    // scanline from val array to bdata array
1558                    if (lineNo >= sourceRegion.y &&
1559                        lineNo < sourceRegion.y + sourceRegion.height) {
1560                        if (noTransform) {
1561                            int pos = lineNo * (width + 1 >> 1);
1562                            for(int i = 0, j = 0; i < width >> 1; i++)
1563                                bdata[pos++] =
1564                                    (byte)((val[j++] << 4) | val[j++]);
1565                            if ((width & 1) == 1)
1566                                bdata[pos] |= val[width - 1] << 4;
1567
1568                            processImageUpdate(bi, 0, lineNo,
1569                                               destinationRegion.width, 1, 1, 1,
1570                                               new int[]{0});
1571                            finished++;
1572                        } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1573                            int currentLine = (lineNo - sourceRegion.y) / scaleY +
1574                                destinationRegion.y;
1575                            int pos = currentLine * lineStride;
1576                            pos += destinationRegion.x >> 1;
1577                            int shift = (1 - (destinationRegion.x & 1)) << 2;
1578                            for (int i = sourceRegion.x;
1579                                 i < sourceRegion.x + sourceRegion.width;
1580                                 i += scaleX) {
1581                                bdata[pos] |= val[i] << shift;
1582                                shift += 4;
1583                                if (shift == 4) {
1584                                    pos++;
1585                                }
1586                                shift &= 7;
1587                            }
1588                            processImageUpdate(bi, 0, currentLine,
1589                                               destinationRegion.width, 1, 1, 1,
1590                                               new int[]{0});
1591                            finished++;
1592                        }
1593                    }
1594                    processImageProgress(100.0F * finished / destinationRegion.height);
1595                    lineNo += isBottomUp ? -1 : 1;
1596                    l = 0;
1597
1598                    if (abortRequested()) {
1599                        break;
1600                    }
1601
1602                    // End-of-RLE marker
1603                    if ((values[count-1] & 0xff) == 1)
1604                        flag = true;
1605                    break;
1606
1607                case 2:
1608                    // delta or vector marker
1609                    int xoff = values[count++] & 0xFF;
1610                    int yoff = values[count] & 0xFF;
1611                    // Move to the position xoff, yoff down
1612                    l += xoff + yoff*width;
1613                    break;
1614
1615                default:
1616                    int end = values[count-1] & 0xFF;
1617                    for (int i=0; i<end; i++) {
1618                        val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
1619                                          : (values[count++] & 0x0f));
1620                    }
1621
1622                    // When end is odd, the above for loop does not
1623                    // increment count, so do it now.
1624                    if ((end & 1) == 1) {
1625                        count++;
1626                    }
1627
1628                    // Whenever end pixels can fit into odd number of bytes,
1629                    // an extra padding byte will be present, so skip that.
1630                    if ((((int)Math.ceil(end/2)) & 1) ==1 ) {
1631                        count++;
1632                    }
1633                    break;
1634                }
1635            } else {
1636                // Encoded mode
1637                int alternate[] = { (values[count] & 0xf0) >> 4,
1638                                    values[count] & 0x0f };
1639                for (int i=0; (i < value) && (l < width); i++) {
1640                    val[l++] = (byte)alternate[i & 1];
1641                }
1642
1643                count++;
1644            }
1645
1646            // If End-of-RLE data, then exit the while loop
1647            if (flag) {
1648                break;
1649            }
1650        }
1651    }
1652
1653    /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1654     *  ImageIO-style plugin.
1655     *
1656     * @param bi The destination <code>BufferedImage</code>.
1657     * @param bmpParam The <code>ImageReadParam</code> for decoding this
1658     *          BMP image.  The parameters for subregion, band selection and
1659     *          subsampling are used in decoding the jpeg image.
1660     */
1661
1662    private BufferedImage readEmbedded(int type,
1663                              BufferedImage bi, ImageReadParam bmpParam)
1664      throws IOException {
1665        String format;
1666        switch(type) {
1667          case BI_JPEG:
1668              format = "JPEG";
1669              break;
1670          case BI_PNG:
1671              format = "PNG";
1672              break;
1673          default:
1674              throw new
1675                  IOException("Unexpected compression type: " + type);
1676        }
1677        ImageReader reader =
1678            (ImageReader)ImageIO.getImageReadersByFormatName(format).next();
1679        if (reader == null) {
1680            throw new RuntimeException(I18N.getString("BMPImageReader4") +
1681                                       " " + format);
1682        }
1683        // prepare input
1684        byte[] buff = new byte[(int)imageSize];
1685        iis.read(buff);
1686        reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
1687        if (bi == null) {
1688            ImageTypeSpecifier embType = (ImageTypeSpecifier)reader.getImageTypes(0).next();
1689            bi = embType.createBufferedImage(destinationRegion.x +
1690                                             destinationRegion.width,
1691                                             destinationRegion.y +
1692                                             destinationRegion.height);
1693        }
1694
1695        reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1696                public void imageProgress(ImageReader source,
1697                                          float percentageDone) 
1698                {            
1699                    processImageProgress(percentageDone);
1700                }
1701            });
1702
1703        reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1704                public void imageUpdate(ImageReader source, 
1705                                        BufferedImage theImage, 
1706                                        int minX, int minY, 
1707                                        int width, int height, 
1708                                        int periodX, int periodY, 
1709                                        int[] bands) 
1710                {
1711                    processImageUpdate(theImage, minX, minY, 
1712                                       width, height, 
1713                                       periodX, periodY, bands);
1714                }
1715                public void passComplete(ImageReader source, 
1716                                         BufferedImage theImage) 
1717                {
1718                    processPassComplete(theImage);
1719                }
1720                public void passStarted(ImageReader source, 
1721                                        BufferedImage theImage, 
1722                                        int pass, 
1723                                        int minPass, int maxPass, 
1724                                        int minX, int minY, 
1725                                        int periodX, int periodY, 
1726                                        int[] bands) 
1727                {
1728                    processPassStarted(theImage, pass, minPass, maxPass, 
1729                                       minX, minY, periodX, periodY, 
1730                                       bands);
1731                }
1732                public void thumbnailPassComplete(ImageReader source, 
1733                                                  BufferedImage thumb) {}
1734                public void thumbnailPassStarted(ImageReader source, 
1735                                                 BufferedImage thumb, 
1736                                                 int pass, 
1737                                                 int minPass, int maxPass,
1738                                                 int minX, int minY,
1739                                                 int periodX, int periodY,
1740                                                 int[] bands) {}
1741                public void thumbnailUpdate(ImageReader source, 
1742                                            BufferedImage theThumbnail,
1743                                            int minX, int minY, 
1744                                            int width, int height,
1745                                            int periodX, int periodY,
1746                                            int[] bands) {}
1747            });
1748
1749        reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1750                public void warningOccurred(ImageReader source, String warning)
1751                {
1752                    processWarningOccurred(warning);
1753                }
1754            });
1755 
1756        ImageReadParam param = reader.getDefaultReadParam();
1757        param.setDestination(bi);
1758        param.setDestinationBands(bmpParam.getDestinationBands());
1759        param.setDestinationOffset(bmpParam.getDestinationOffset());
1760        param.setSourceBands(bmpParam.getSourceBands());
1761        param.setSourceRegion(bmpParam.getSourceRegion());
1762        param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1763                                   bmpParam.getSourceYSubsampling(),
1764                                   bmpParam.getSubsamplingXOffset(),
1765                                   bmpParam.getSubsamplingYOffset());
1766        reader.read(0, param);
1767        return bi;
1768    }
1769
1770    private class EmbeddedProgressAdapter implements IIOReadProgressListener {
1771        public void imageComplete(ImageReader src) {}
1772        public void imageProgress(ImageReader src, float percentageDone) {}
1773        public void imageStarted(ImageReader src, int imageIndex) {}
1774        public void thumbnailComplete(ImageReader src) {}
1775        public void thumbnailProgress(ImageReader src, float percentageDone) {}
1776        public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
1777        public void sequenceComplete(ImageReader src) {}
1778        public void sequenceStarted(ImageReader src, int minIndex) {}
1779        public void readAborted(ImageReader src) {}
1780    }
1781}