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