FileDocCategorySizeDatePackage
BMPImageReader.javaAPI DocJava SE 5 API63489Fri Aug 26 14:54:42 BST 2005com.sun.imageio.plugins.bmp

BMPImageReader

public class BMPImageReader extends ImageReader implements BMPConstants
This class is the Java Image IO plugin reader for BMP images. It may subsample the image, clip the image, select sub-bands, and shift the decoded image origin if the proper decoding parameter are set in the provided ImageReadParam. This class supports Microsoft Windows Bitmap Version 3-5, as well as OS/2 Bitmap Version 2.x (for single-image BMP file).

Fields Summary
private static final int
VERSION_2_1_BIT
private static final int
VERSION_2_4_BIT
private static final int
VERSION_2_8_BIT
private static final int
VERSION_2_24_BIT
private static final int
VERSION_3_1_BIT
private static final int
VERSION_3_4_BIT
private static final int
VERSION_3_8_BIT
private static final int
VERSION_3_24_BIT
private static final int
VERSION_3_NT_16_BIT
private static final int
VERSION_3_NT_32_BIT
private static final int
VERSION_4_1_BIT
private static final int
VERSION_4_4_BIT
private static final int
VERSION_4_8_BIT
private static final int
VERSION_4_16_BIT
private static final int
VERSION_4_24_BIT
private static final int
VERSION_4_32_BIT
private long
bitmapFileSize
private long
bitmapOffset
private long
compression
private long
imageSize
private byte[]
palette
private int
imageType
private int
numBands
private boolean
isBottomUp
private int
bitsPerPixel
private int
redMask
private int
greenMask
private int
blueMask
private int
alphaMask
private SampleModel
sampleModel
private SampleModel
originalSampleModel
private ColorModel
colorModel
private ColorModel
originalColorModel
private ImageInputStream
iis
The input stream where reads from
private boolean
gotHeader
Indicates whether the header is read.
private int
width
The original image width.
private int
height
The original image height.
private Rectangle
destinationRegion
The destination region.
private Rectangle
sourceRegion
The source region.
private BMPMetadata
metadata
The metadata from the stream.
private BufferedImage
bi
The destination image.
private boolean
noTransform
Indicates whether subsampled, subregion is required, and offset is defined
private boolean
seleBand
Indicates whether subband is selected.
private int
scaleX
The scaling factors.
private int
scaleY
private int[]
sourceBands
source and destination bands.
private int[]
destBands
Constructors Summary
public BMPImageReader(ImageReaderSpi originator)
Constructs BMPImageReader from the provided ImageReaderSpi.


                
       
        super(originator);
    
Methods Summary
public booleancanReadRaster()

        return true;
    
private voidcheckIndex(int imageIndex)

        if (imageIndex != 0) {
            throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
        }
    
private voiddecodeRLE4(int imSize, int padding, byte[] values, byte[] bdata)

        byte[] val = new byte[width];
        int count = 0, l = 0;
        int value;
        boolean flag = false;
        int lineNo = isBottomUp ? height - 1 : 0;
        int lineStride =
            ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
        int finished = 0;
        
        while (count != imSize) {
            
            value = values[count++] & 0xFF;
            if (value == 0) {
                
                
                // Absolute mode
                switch(values[count++] & 0xFF) {
                    
                case 0:
                    // End-of-scanline marker
                    // End-of-scanline marker
                    if (lineNo >= sourceRegion.y &&
                        lineNo < sourceRegion.y + sourceRegion.width) {
                        if (noTransform) {
                            int pos = lineNo * (width + 1 >> 1);
                            for(int i = 0, j = 0; i < width >> 1; i++)
                                bdata[pos++] =
                                    (byte)((val[j++] << 4) | val[j++]);
                            if ((width & 1) == 1)
                                bdata[pos] |= val[width - 1] << 4;

                            processImageUpdate(bi, 0, lineNo,
                                               destinationRegion.width, 1, 1, 1,
                                               new int[]{0});
                            finished++;
                        } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
                            int currentLine = (lineNo - sourceRegion.y) / scaleY +
                                destinationRegion.y;
                            int pos = currentLine * lineStride;
                            pos += destinationRegion.x >> 1;
                            int shift = (1 - (destinationRegion.x & 1)) << 2;
                            for (int i = sourceRegion.x;
                                 i < sourceRegion.x + sourceRegion.width;
                                 i += scaleX) {
                                bdata[pos] |= val[i] << shift;
                                shift += 4;
                                if (shift == 4) {
                                    pos++;
                                }
                                shift &= 7;
                            }
                            processImageUpdate(bi, 0, currentLine,
                                               destinationRegion.width, 1, 1, 1,
                                               new int[]{0});
                            finished++;
                        }
                    }
                    processImageProgress(100.0F * finished / destinationRegion.height);
                    lineNo += isBottomUp ? -1 : 1;
                    l = 0;

                    if (abortRequested()) {
                        flag = true;
                    }

                    break;

                case 1:
                    // End-of-RLE marker
                    flag = true;
                    break;

                case 2:
                    // delta or vector marker
                    int xoff = values[count++] & 0xFF;
                    int yoff = values[count] & 0xFF;
                    // Move to the position xoff, yoff down
                    l += xoff + yoff*width;
                    break;

                default:
                    int end = values[count-1] & 0xFF;
                    for (int i=0; i<end; i++) {
                        val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
                                          : (values[count++] & 0x0f));
                    }

                    // When end is odd, the above for loop does not
                    // increment count, so do it now.
                    if ((end & 1) == 1) {
                        count++;
                    }

                    // Whenever end pixels can fit into odd number of bytes,
                    // an extra padding byte will be present, so skip that.
                    if ((((int)Math.ceil(end/2)) & 1) ==1 ) {
                        count++;
                    }
                    break;
                }
            } else {
                // Encoded mode
                int alternate[] = { (values[count] & 0xf0) >> 4,
                                    values[count] & 0x0f };
                for (int i=0; (i < value) && (l < width); i++) {
                    val[l++] = (byte)alternate[i & 1];
                }

                count++;
            }

            // If End-of-RLE data, then exit the while loop
            if (flag) {
                break;
            }
        }
    
private voiddecodeRLE8(int imSize, int padding, byte[] values, byte[] bdata)


        byte val[] = new byte[width * height];
        int count = 0, l = 0;
        int value;
        boolean flag = false;
        int lineNo = isBottomUp ? height - 1 : 0;
        int lineStride =
            ((ComponentSampleModel)sampleModel).getScanlineStride();
        int finished = 0;

        while (count != imSize) {
            value = values[count++] & 0xff;
            if (value == 0) {
                switch(values[count++] & 0xff) {

                case 0:
                    // End-of-scanline marker
                    if (lineNo >= sourceRegion.y &&
                        lineNo < sourceRegion.y + sourceRegion.width) {
                        if (noTransform) {
                            int pos = lineNo * width;
                            for(int i = 0; i < width; i++)
                                bdata[pos++] = val[i];
                            processImageUpdate(bi, 0, lineNo,
                                               destinationRegion.width, 1, 1, 1,
                                               new int[]{0});
                            finished++;
                        } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
                            int currentLine = (lineNo - sourceRegion.y) / scaleY +
                                destinationRegion.y;
                            int pos = currentLine * lineStride;
                            pos += destinationRegion.x;
                            for (int i = sourceRegion.x;
                                 i < sourceRegion.x + sourceRegion.width;
                                 i += scaleX)
                                bdata[pos++] = val[i];
                            processImageUpdate(bi, 0, currentLine,
                                               destinationRegion.width, 1, 1, 1,
                                               new int[]{0});
                            finished++;
                        }
                    }
                    processImageProgress(100.0F * finished / destinationRegion.height);
                    lineNo += isBottomUp ? -1 : 1;
                    l = 0;

                    if (abortRequested()) {
                        flag = true;
                    }

                    break;

                case 1:
                    // End-of-RLE marker
                    flag = true;
                    break;

                case 2:
                    // delta or vector marker
                    int xoff = values[count++] & 0xff;
                    int yoff = values[count] & 0xff;
                    // Move to the position xoff, yoff down
                    l += xoff + yoff*width;
                    break;

                default:
                    int end = values[count-1] & 0xff;
                    for (int i=0; i<end; i++) {
                        val[l++] = (byte)(values[count++] & 0xff);
                    }

                    // Whenever end pixels can fit into odd number of bytes,
                    // an extra padding byte will be present, so skip that.
                    if ((end & 1) == 1) {
                        count++;
                    }
                }
            } else {
                for (int i=0; i<value; i++) {
                    val[l++] = (byte)(values[count] & 0xff);
                }

                count++;
            }

            // If End-of-RLE data, then exit the while loop
            if (flag) {
                break;
            }
        }
    
public javax.imageio.ImageReadParamgetDefaultReadParam()

        return new ImageReadParam();
    
public intgetHeight(int imageIndex)

        checkIndex(imageIndex);
        readHeader();
        return height;
    
public javax.imageio.metadata.IIOMetadatagetImageMetadata(int imageIndex)

        checkIndex(imageIndex);
        if (metadata == null) {
            readHeader();
        }
        return metadata;
    
public java.util.IteratorgetImageTypes(int imageIndex)

        checkIndex(imageIndex);
        readHeader();
        ArrayList list = new ArrayList(1);
        list.add(new ImageTypeSpecifier(originalColorModel,
                                        originalSampleModel));
        return list.iterator();
    
public intgetNumImages(boolean allowSearch)
Overrides the method defined in the superclass.

        if (iis == null) {
            throw new IllegalStateException(I18N.getString("GetNumImages0"));
        }
        if (seekForwardOnly && allowSearch) {
            throw new IllegalStateException(I18N.getString("GetNumImages1"));
        }
        return 1;
    
public javax.imageio.metadata.IIOMetadatagetStreamMetadata()

        return null;
    
public intgetWidth(int imageIndex)

        checkIndex(imageIndex);
        readHeader();
        return width;
    
public booleanisRandomAccessEasy(int imageIndex)

        checkIndex(imageIndex);
        readHeader();
        return metadata.compression == BI_RGB;
    
public java.awt.image.BufferedImageread(int imageIndex, javax.imageio.ImageReadParam param)


        if (iis == null) {
            throw new IllegalStateException(I18N.getString("BMPImageReader5"));
        }

        checkIndex(imageIndex);
        clearAbortRequest();
        processImageStarted(imageIndex);

        if (param == null)
            param = getDefaultReadParam();

        //read header
        readHeader();

        sourceRegion = new Rectangle(0, 0, 0, 0);
        destinationRegion = new Rectangle(0, 0, 0, 0);

        computeRegions(param, this.width, this.height,
                       param.getDestination(),
                       sourceRegion,
                       destinationRegion);

        scaleX = param.getSourceXSubsampling();
        scaleY = param.getSourceYSubsampling();

        // If the destination band is set used it
        sourceBands = param.getSourceBands();
        destBands = param.getDestinationBands();

        seleBand = (sourceBands != null) && (destBands != null);
        noTransform =
            destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
            seleBand;

        if (!seleBand) {
            sourceBands = new int[numBands];
            destBands = new int[numBands];
            for (int i = 0; i < numBands; i++)
                destBands[i] = sourceBands[i] = i;
        }

        // If the destination is provided, then use it.  Otherwise, create new one
        bi = param.getDestination();

        // Get the image data.
        WritableRaster raster = null;

        if (bi == null) {
            sampleModel =
                sampleModel.createCompatibleSampleModel(destinationRegion.x +
                                                        destinationRegion.width,
                                                        destinationRegion.y +
                                                        destinationRegion.height);
            if (seleBand)
                sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
            raster = Raster.createWritableRaster(sampleModel, new Point());
            bi = new BufferedImage(colorModel, raster, false, null);
        } else {
            raster = bi.getWritableTile(0, 0);
            sampleModel = bi.getSampleModel();
            colorModel = bi.getColorModel();

            noTransform &=  destinationRegion.equals(raster.getBounds());
        }

        byte bdata[] = null; // buffer for byte data
        short sdata[] = null; // buffer for short data
        int idata[] = null; // buffer for int data

        if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
            bdata = (byte[])((DataBufferByte)raster.getDataBuffer()).getData();
        else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
            sdata = (short[])((DataBufferUShort)raster.getDataBuffer()).getData();
        else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
            idata = (int[])((DataBufferInt)raster.getDataBuffer()).getData();

        // There should only be one tile.
        switch(imageType) {

        case VERSION_2_1_BIT:
            // no compression
            read1Bit(bdata);
            break;

        case VERSION_2_4_BIT:
            // no compression
            read4Bit(bdata);
            break;

        case VERSION_2_8_BIT:
            // no compression
            read8Bit(bdata);
            break;

        case VERSION_2_24_BIT:
            // no compression
            read24Bit(bdata);
            break;

        case VERSION_3_1_BIT:
            // 1-bit images cannot be compressed.
            read1Bit(bdata);
            break;

        case VERSION_3_4_BIT:
            switch((int)compression) {
            case BI_RGB:
                read4Bit(bdata);
                break;

            case BI_RLE4:
                readRLE4(bdata);
                break;

            case BI_JPEG:
                readEmbedded("JPEG", bi, param);
                break;

            case BI_PNG:
                readEmbedded("PNG", bi, param);
                break;
		
            default:
                throw new
                    RuntimeException(I18N.getString("BMPImageReader1"));
            }
            break;

        case VERSION_3_8_BIT:
            switch((int)compression) {
            case BI_RGB:
                read8Bit(bdata);
                break;

            case BI_RLE8:
                readRLE8(bdata);
                break;

            case BI_JPEG:
                readEmbedded("JPEG", bi, param);
                break;

            case BI_PNG:
                readEmbedded("PNG", bi, param);
                break;

            default:
                throw new
                    RuntimeException(I18N.getString("BMPImageReader1"));
            }

            break;

        case VERSION_3_24_BIT:
            // 24-bit images are not compressed
            read24Bit(bdata);
            break;

        case VERSION_3_NT_16_BIT:
            read16Bit(sdata);
            break;

        case VERSION_3_NT_32_BIT:
            switch((int)compression) {
            case BI_JPEG:
                iis.seek(54);
                readEmbedded("JPEG", bi, param);
                break;
		
            case BI_PNG:
                readEmbedded("PNG", bi, param);
                break;
            default:
                read32Bit(idata);
            }
            break;

        case VERSION_4_1_BIT:
            read1Bit(bdata);
            break;

        case VERSION_4_4_BIT:
            switch((int)compression) {

            case BI_RGB:
                read4Bit(bdata);
                break;

            case BI_RLE4:
                readRLE4(bdata);
                break;

            case BI_JPEG:
                readEmbedded("JPEG", bi, param);
                break;

            case BI_PNG:
                readEmbedded("PNG", bi, param);
                break;

            default:
                throw new
                    RuntimeException(I18N.getString("BMPImageReader1"));
            }

        case VERSION_4_8_BIT:
            switch((int)compression) {

            case BI_RGB:
                read8Bit(bdata);
                break;

            case BI_RLE8:
                readRLE8(bdata);
                break;

            case BI_JPEG:
                readEmbedded("JPEG", bi, param);
                break;

            case BI_PNG:
                readEmbedded("PNG", bi, param);
                break;

            default:
                throw new
                    RuntimeException(I18N.getString("BMPImageReader1"));
            }
            break;

        case VERSION_4_16_BIT:
            read16Bit(sdata);
            break;

        case VERSION_4_24_BIT:
            read24Bit(bdata);
            break;

        case VERSION_4_32_BIT:
            read32Bit(idata);
            break;
        }

        if (abortRequested())
            processReadAborted();
        else
            processImageComplete();

        return bi;
    
private voidread16Bit(short[] sdata)

        // Padding bytes at the end of each scanline
        // width * bitsPerPixel should be divisible by 32
        int padding = width * 2 % 4;
        
        if ( padding != 0)
            padding = 4 - padding;
        
        int lineLength = width + padding / 2;
        
        if (noTransform) {
            int j = isBottomUp ? (height -1) * width : 0;
            for (int i=0; i<height; i++) {
                if (abortRequested()) {
                    break;
                }

                iis.readFully(sdata, j, width);
                iis.skipBytes(padding);
                
                j += isBottomUp ? -width : width;
                processImageUpdate(bi, 0, i,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F * i/destinationRegion.height);
            }
        } else {
            short[] buf = new short[lineLength];
            int lineStride =
                ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
            
            if (isBottomUp) {
                int lastLine =
                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
                iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
            } else
                iis.skipBytes(lineLength * sourceRegion.y << 1);
            
            int skipLength = lineLength * (scaleY - 1) << 1;
            
            int k = destinationRegion.y * lineStride;
            if (isBottomUp)
                k += (destinationRegion.height - 1) * lineStride;
            k += destinationRegion.x;
            
            for (int j = 0, y = sourceRegion.y;
                 j < destinationRegion.height; j++, y+=scaleY) {
                
                if (abortRequested())
                    break;
                iis.readFully(buf, 0, lineLength);
                for (int i = 0, m = sourceRegion.x;
                     i < destinationRegion.width; i++, m += scaleX) {
                    //get the bit and assign to the data buffer of the raster
                    sdata[k + i] = buf[m];
                }

                k += isBottomUp ? -lineStride : lineStride;
                iis.skipBytes(skipLength);
                processImageUpdate(bi, 0, j,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F*j/destinationRegion.height);
            }
        }
    
private voidread1Bit(byte[] bdata)

        int bytesPerScanline = (width + 7) / 8;
        int padding = bytesPerScanline % 4;
        if (padding != 0) {
            padding = 4 - padding;
        }

        int lineLength = bytesPerScanline + padding;

        if (noTransform) {
            int j = isBottomUp ? (height -1)*bytesPerScanline : 0;

            for (int i=0; i<height; i++) {
                if (abortRequested()) {
                    break;
                }
                iis.readFully(bdata, j, bytesPerScanline);
                iis.skipBytes(padding);
                j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
                processImageUpdate(bi, 0, i,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F * i/destinationRegion.height);
            }
        } else {
            byte[] buf = new byte[lineLength];
            int lineStride =
                ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();

            if (isBottomUp) {
                int lastLine =
                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
                iis.skipBytes(lineLength * (height - 1 - lastLine));
            } else
                iis.skipBytes(lineLength * sourceRegion.y);

            int skipLength = lineLength * (scaleY - 1);

            // cache the values to avoid duplicated computation
            int[] srcOff = new int[destinationRegion.width];
            int[] destOff = new int[destinationRegion.width];
            int[] srcPos = new int[destinationRegion.width];
            int[] destPos = new int[destinationRegion.width];

            for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
                 i < destinationRegion.x + destinationRegion.width;
                 i++, j++, x += scaleX) {
                srcPos[j] = x >> 3;
                srcOff[j] = 7 - (x & 7);
                destPos[j] = i >> 3;
                destOff[j] = 7 - (i & 7);
            }

            int k = destinationRegion.y * lineStride;
            if (isBottomUp)
                k += (destinationRegion.height - 1) * lineStride;

            for (int j = 0, y = sourceRegion.y;
                 j < destinationRegion.height; j++, y+=scaleY) {

                if (abortRequested())
                    break;
                iis.read(buf, 0, lineLength);
                for (int i = 0; i < destinationRegion.width; i++) {
                    //get the bit and assign to the data buffer of the raster
                    int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
                    bdata[k + destPos[i]] |= v << destOff[i];
                }

                k += isBottomUp ? -lineStride : lineStride;
                iis.skipBytes(skipLength);
                processImageUpdate(bi, 0, j,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F*j/destinationRegion.height);
            }
        }
    
private voidread24Bit(byte[] bdata)

        // Padding bytes at the end of each scanline
        // width * bitsPerPixel should be divisible by 32
        int padding = width * 3 % 4;
        if ( padding != 0)
            padding = 4 - padding;

        int lineStride = width * 3;
        int lineLength = lineStride + padding;

        if (noTransform) {
            int j = isBottomUp ? (height -1) * width * 3 : 0;

            for (int i=0; i<height; i++) {
                if (abortRequested()) {
                    break;
                }
                iis.readFully(bdata, j, lineStride);
                iis.skipBytes(padding);
                j += isBottomUp ? -lineStride : lineStride;
                processImageUpdate(bi, 0, i,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F * i/destinationRegion.height);
            }
        } else {
            byte[] buf = new byte[lineLength];
            lineStride =
                ((ComponentSampleModel)sampleModel).getScanlineStride();

            if (isBottomUp) {
                int lastLine =
                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
                iis.skipBytes(lineLength * (height - 1 - lastLine));
            } else
                iis.skipBytes(lineLength * sourceRegion.y);

            int skipLength = lineLength * (scaleY - 1);

            int k = destinationRegion.y * lineStride;
            if (isBottomUp)
                k += (destinationRegion.height - 1) * lineStride;
            k += destinationRegion.x * 3;

            for (int j = 0, y = sourceRegion.y;
                 j < destinationRegion.height; j++, y+=scaleY) {

                if (abortRequested())
                    break;
                iis.read(buf, 0, lineLength);
                for (int i = 0, m = 3 * sourceRegion.x;
                     i < destinationRegion.width; i++, m += 3 * scaleX) {
                    //get the bit and assign to the data buffer of the raster
                    int n = 3 * i + k;
                    for (int b = 0; b < destBands.length; b++)
                        bdata[n + destBands[b]] = buf[m + sourceBands[b]];
                }

                k += isBottomUp ? -lineStride : lineStride;
                iis.skipBytes(skipLength);
                processImageUpdate(bi, 0, j,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F*j/destinationRegion.height);
            }
        }
    
private voidread32Bit(int[] idata)

        if (noTransform) {
            int j = isBottomUp ? (height -1) * width : 0;

            for (int i=0; i<height; i++) {
                if (abortRequested()) {
                    break;
                }
                iis.readFully(idata, j, width);
                j += isBottomUp ? -width : width;
                processImageUpdate(bi, 0, i,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F * i/destinationRegion.height);
            }
        } else {
            int[] buf = new int[width];
            int lineStride =
                ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();

            if (isBottomUp) {
                int lastLine =
                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
                iis.skipBytes(width * (height - 1 - lastLine) << 2);
            } else
                iis.skipBytes(width * sourceRegion.y << 2);

            int skipLength = width * (scaleY - 1) << 2;

            int k = destinationRegion.y * lineStride;
            if (isBottomUp)
                k += (destinationRegion.height - 1) * lineStride;
            k += destinationRegion.x;

            for (int j = 0, y = sourceRegion.y;
                 j < destinationRegion.height; j++, y+=scaleY) {

                if (abortRequested())
                    break;
                iis.readFully(buf, 0, width);
                for (int i = 0, m = sourceRegion.x;
                     i < destinationRegion.width; i++, m += scaleX) {
                    //get the bit and assign to the data buffer of the raster
                    idata[k + i] = buf[m];
                }

                k += isBottomUp ? -lineStride : lineStride;
                iis.skipBytes(skipLength);
                processImageUpdate(bi, 0, j,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F*j/destinationRegion.height);
            }
        }
    
private voidread4Bit(byte[] bdata)


        int bytesPerScanline = (width + 1) / 2;

        // Padding bytes at the end of each scanline
        int padding = bytesPerScanline % 4;
        if (padding != 0)
            padding = 4 - padding;

        int lineLength = bytesPerScanline + padding;

        if (noTransform) {
            int j = isBottomUp ? (height -1) * bytesPerScanline : 0;

            for (int i=0; i<height; i++) {
                if (abortRequested()) {
                    break;
                }
                iis.readFully(bdata, j, bytesPerScanline);
                iis.skipBytes(padding);
                j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
                processImageUpdate(bi, 0, i,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F * i/destinationRegion.height);
            }
        } else {
            byte[] buf = new byte[lineLength];
            int lineStride =
                ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();

            if (isBottomUp) {
                int lastLine =
                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
                iis.skipBytes(lineLength * (height - 1 - lastLine));
            } else
                iis.skipBytes(lineLength * sourceRegion.y);

            int skipLength = lineLength * (scaleY - 1);

            // cache the values to avoid duplicated computation
            int[] srcOff = new int[destinationRegion.width];
            int[] destOff = new int[destinationRegion.width];
            int[] srcPos = new int[destinationRegion.width];
            int[] destPos = new int[destinationRegion.width];

            for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
                 i < destinationRegion.x + destinationRegion.width;
                 i++, j++, x += scaleX) {
                srcPos[j] = x >> 1;
                srcOff[j] = (1 - (x & 1)) << 2;
                destPos[j] = i >> 1;
                destOff[j] = (1 - (i & 1)) << 2;
            }

            int k = destinationRegion.y * lineStride;
            if (isBottomUp)
                k += (destinationRegion.height - 1) * lineStride;

            for (int j = 0, y = sourceRegion.y;
                 j < destinationRegion.height; j++, y+=scaleY) {

                if (abortRequested())
                    break;
                iis.read(buf, 0, lineLength);
                for (int i = 0; i < destinationRegion.width; i++) {
                    //get the bit and assign to the data buffer of the raster
                    int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
                    bdata[k + destPos[i]] |= v << destOff[i];
                }

                k += isBottomUp ? -lineStride : lineStride;
                iis.skipBytes(skipLength);
                processImageUpdate(bi, 0, j,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F*j/destinationRegion.height);
            }
        }
    
private voidread8Bit(byte[] bdata)


        // Padding bytes at the end of each scanline
        int padding = width % 4;
        if (padding != 0) {
            padding = 4 - padding;
        }

        int lineLength = width + padding;

        if (noTransform) {
            int j = isBottomUp ? (height -1) * width : 0;

            for (int i=0; i<height; i++) {
                if (abortRequested()) {
                    break;
                }
                iis.readFully(bdata, j, width);
                iis.skipBytes(padding);
                j += isBottomUp ? -width : width;
                processImageUpdate(bi, 0, i,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F * i/destinationRegion.height);
            }
        } else {
            byte[] buf = new byte[lineLength];
            int lineStride =
                ((ComponentSampleModel)sampleModel).getScanlineStride();

            if (isBottomUp) {
                int lastLine =
                    sourceRegion.y + (destinationRegion.height - 1) * scaleY;
                iis.skipBytes(lineLength * (height - 1 - lastLine));
            } else
                iis.skipBytes(lineLength * sourceRegion.y);

            int skipLength = lineLength * (scaleY - 1);

            int k = destinationRegion.y * lineStride;
            if (isBottomUp)
                k += (destinationRegion.height - 1) * lineStride;
            k += destinationRegion.x;

            for (int j = 0, y = sourceRegion.y;
                 j < destinationRegion.height; j++, y+=scaleY) {

                if (abortRequested())
                    break;
                iis.read(buf, 0, lineLength);
                for (int i = 0, m = sourceRegion.x;
                     i < destinationRegion.width; i++, m += scaleX) {
                    //get the bit and assign to the data buffer of the raster
                    bdata[k + i] = buf[m];
                }

                k += isBottomUp ? -lineStride : lineStride;
                iis.skipBytes(skipLength);
                processImageUpdate(bi, 0, j,
                                   destinationRegion.width, 1, 1, 1,
                                   new int[]{0});
                processImageProgress(100.0F*j/destinationRegion.height);
            }
        }
    
private voidreadEmbedded(java.lang.String format, java.awt.image.BufferedImage bi, javax.imageio.ImageReadParam bmpParam)
Decodes the jpeg/png image embedded in the bitmap using any jpeg ImageIO-style plugin.

param
bi The destination BufferedImage.
param
bmpParam The ImageReadParam for decoding this BMP image. The parameters for subregion, band selection and subsampling are used in decoding the jpeg image.

        Iterator iterator = ImageIO.getImageReadersByFormatName(format);
        ImageReader reader = null;
        if (iterator.hasNext())
            reader = (ImageReader)iterator.next();
        if (reader != null) {
            // prepare input
            byte[] buff = new byte[(int)imageSize];
            iis.read(buff);
            reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));

            reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
                    public void imageProgress(ImageReader source,
                                              float percentageDone) 
                    {            
                        processImageProgress(percentageDone);
                    }
                });

            reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
                    public void imageUpdate(ImageReader source, 
                                            BufferedImage theImage, 
                                            int minX, int minY, 
                                            int width, int height, 
                                            int periodX, int periodY, 
                                            int[] bands) 
                    {
                        processImageUpdate(theImage, minX, minY, 
                                           width, height, 
                                           periodX, periodY, bands);
                    }
                    public void passComplete(ImageReader source, 
                                             BufferedImage theImage) 
                    {
                        processPassComplete(theImage);
                    }
                    public void passStarted(ImageReader source, 
                                            BufferedImage theImage, 
                                            int pass, 
                                            int minPass, int maxPass, 
                                            int minX, int minY, 
                                            int periodX, int periodY, 
                                            int[] bands) 
                    {
                        processPassStarted(theImage, pass, minPass, maxPass, 
                                           minX, minY, periodX, periodY, 
                                           bands);
                    }
                    public void thumbnailPassComplete(ImageReader source, 
                                                      BufferedImage thumb) {}
                    public void thumbnailPassStarted(ImageReader source, 
                                                     BufferedImage thumb, 
                                                     int pass, 
                                                     int minPass, int maxPass,
                                                     int minX, int minY,
                                                     int periodX, int periodY,
                                                     int[] bands) {}
                    public void thumbnailUpdate(ImageReader source, 
                                                BufferedImage theThumbnail,
                                                int minX, int minY, 
                                                int width, int height,
                                                int periodX, int periodY,
                                                int[] bands) {}
                });

            reader.addIIOReadWarningListener(new IIOReadWarningListener() {
                    public void warningOccurred(ImageReader source, String warning) {
                        processWarningOccurred(warning);
                    }
                });
 
            ImageReadParam param = reader.getDefaultReadParam();
            param.setDestination(bi);
            param.setDestinationBands(bmpParam.getDestinationBands());
            param.setDestinationOffset(bmpParam.getDestinationOffset());
            param.setSourceBands(bmpParam.getSourceBands());
            param.setSourceRegion(bmpParam.getSourceRegion());
            param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
                                       bmpParam.getSourceYSubsampling(),
                                       bmpParam.getSubsamplingXOffset(),
                                       bmpParam.getSubsamplingYOffset());
            reader.read(0, param);
        } else
            throw new RuntimeException(I18N.getString("BMPImageReader4") + " " + format);
    
public voidreadHeader()

        if (gotHeader)
            return;

        if (iis == null) {
            throw new IllegalStateException("Input source not set!");
        }
        int profileData = 0, profileSize = 0;

        this.metadata = new BMPMetadata();
        iis.mark();

        // read and check the magic marker
        byte[] marker = new byte[2];
        iis.read(marker);
        if (marker[0] != 0x42 || marker[1] != 0x4d)
            throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));

        // Read file size
        bitmapFileSize = iis.readUnsignedInt();
        // skip the two reserved fields
        iis.skipBytes(4);

        // Offset to the bitmap from the beginning
        bitmapOffset = iis.readUnsignedInt();
        // End File Header

        // Start BitmapCoreHeader
        long size = iis.readUnsignedInt();

        if (size == 12) {
            width = iis.readShort();
            height = iis.readShort();
        } else {
            width = iis.readInt();
            height = iis.readInt();
        }

        metadata.width = width;
        metadata.height = height;

        int planes = iis.readUnsignedShort();
        bitsPerPixel = iis.readUnsignedShort();

        //metadata.colorPlane = planes;
        metadata.bitsPerPixel = (short)bitsPerPixel;

        // As BMP always has 3 rgb bands, except for Version 5,
        // which is bgra
        numBands = 3;

        if (size == 12) {
            // Windows 2.x and OS/2 1.x
            metadata.bmpVersion = VERSION_2;

            // Classify the image type
            if (bitsPerPixel == 1) {
                imageType = VERSION_2_1_BIT;
            } else if (bitsPerPixel == 4) {
                imageType = VERSION_2_4_BIT;
            } else if (bitsPerPixel == 8) {
                imageType = VERSION_2_8_BIT;
            } else if (bitsPerPixel == 24) {
                imageType = VERSION_2_24_BIT;
            }

            // Read in the palette
            int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
            int sizeOfPalette = numberOfEntries*3;
            palette = new byte[sizeOfPalette];
            iis.readFully(palette, 0, sizeOfPalette);
            metadata.palette = palette;
            metadata.paletteSize = numberOfEntries;
        } else {
            compression = iis.readUnsignedInt();
            imageSize = iis.readUnsignedInt();
            long xPelsPerMeter = iis.readInt();
            long yPelsPerMeter = iis.readInt();
            long colorsUsed = iis.readUnsignedInt();
            long colorsImportant = iis.readUnsignedInt();

            metadata.compression = (int)compression;
            metadata.xPixelsPerMeter = (int)xPelsPerMeter;
            metadata.yPixelsPerMeter = (int)yPelsPerMeter;
            metadata.colorsUsed = (int)colorsUsed;
            metadata.colorsImportant = (int)colorsImportant;

            if (size == 40) {
                // Windows 3.x and Windows NT
                switch((int)compression) {

                case BI_RGB:  // No compression
                case BI_RLE8:  // 8-bit RLE compression
                case BI_RLE4:  // 4-bit RLE compression
                case BI_PNG:
                case BI_JPEG:

                    // Read in the palette
                    int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
                    int sizeOfPalette = numberOfEntries * 4;
                    palette = new byte[sizeOfPalette];
                    iis.readFully(palette, 0, sizeOfPalette);

                    metadata.palette = palette;
                    metadata.paletteSize = numberOfEntries;

                    if (bitsPerPixel == 1) {
                        imageType = VERSION_3_1_BIT;
                    } else if (bitsPerPixel == 4) {
                        imageType = VERSION_3_4_BIT;
                    } else if (bitsPerPixel == 8) {
                        imageType = VERSION_3_8_BIT;
                    } else if (bitsPerPixel == 24) {
                        imageType = VERSION_3_24_BIT;
                    } else if (bitsPerPixel == 16) {
                        imageType = VERSION_3_NT_16_BIT;
			    
                        redMask = 0x7C00;
                        greenMask = 0x3E0;
                        blueMask =  (1 << 5) - 1;// 0x1F;
                        metadata.redMask = redMask;
                        metadata.greenMask = greenMask;
                        metadata.blueMask = blueMask;
                    } else if (bitsPerPixel == 32) {
                        imageType = VERSION_3_NT_32_BIT;
                        redMask   = 0x00FF0000;
                        greenMask = 0x0000FF00;
                        blueMask  = 0x000000FF;
                        metadata.redMask = redMask;
                        metadata.greenMask = greenMask;
                        metadata.blueMask = blueMask;
                    }

                    metadata.bmpVersion = VERSION_3;
                    break;

                case BI_BITFIELDS:

                    if (bitsPerPixel == 16) {
                        imageType = VERSION_3_NT_16_BIT;
                    } else if (bitsPerPixel == 32) {
                        imageType = VERSION_3_NT_32_BIT;
                    }

                    // BitsField encoding
                    redMask = (int)iis.readUnsignedInt();
                    greenMask = (int)iis.readUnsignedInt();
                    blueMask = (int)iis.readUnsignedInt();
                    metadata.redMask = redMask;
                    metadata.greenMask = greenMask;
                    metadata.blueMask = blueMask;

                    if (colorsUsed != 0) {
                        // there is a palette
                        sizeOfPalette = (int)colorsUsed*4;
                        palette = new byte[sizeOfPalette];
                        iis.readFully(palette, 0, sizeOfPalette);

                        metadata.palette = palette;
                        metadata.paletteSize = (int)colorsUsed;
                    }
                    metadata.bmpVersion = VERSION_3_NT;

                    break;
                default:
                    throw new
                        RuntimeException(I18N.getString("BMPImageReader2"));
                }
            } else if (size == 108 || size == 124) {
                // Windows 4.x BMP
                if (size == 108)
                    metadata.bmpVersion = VERSION_4;
                else if (size == 124)
                    metadata.bmpVersion = VERSION_5;

                // rgb masks, valid only if comp is BI_BITFIELDS
                redMask = (int)iis.readUnsignedInt();
                greenMask = (int)iis.readUnsignedInt();
                blueMask = (int)iis.readUnsignedInt();
                // Only supported for 32bpp BI_RGB argb
                alphaMask = (int)iis.readUnsignedInt();
                long csType = iis.readUnsignedInt();
                int redX = iis.readInt();
                int redY = iis.readInt();
                int redZ = iis.readInt();
                int greenX = iis.readInt();
                int greenY = iis.readInt();
                int greenZ = iis.readInt();
                int blueX = iis.readInt();
                int blueY = iis.readInt();
                int blueZ = iis.readInt();
                long gammaRed = iis.readUnsignedInt();
                long gammaGreen = iis.readUnsignedInt();
                long gammaBlue = iis.readUnsignedInt();

                if (size == 124) {
                    metadata.intent = iis.readInt();
                    profileData = iis.readInt();
                    profileSize = iis.readInt();
                    iis.skipBytes(4);
                }

                metadata.colorSpace = (int)csType;

                if (csType == LCS_CALIBRATED_RGB) {
                    // All the new fields are valid only for this case
                    metadata.redX = redX;
                    metadata.redY = redY;
                    metadata.redZ = redZ;
                    metadata.greenX = greenX;
                    metadata.greenY = greenY;
                    metadata.greenZ = greenZ;
                    metadata.blueX = blueX;
                    metadata.blueY = blueY;
                    metadata.blueZ = blueZ;
                    metadata.gammaRed = (int)gammaRed;
                    metadata.gammaGreen = (int)gammaGreen;
                    metadata.gammaBlue = (int)gammaBlue;
                }

                // Read in the palette
                int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
                int sizeOfPalette = numberOfEntries*4;
                palette = new byte[sizeOfPalette];
                iis.readFully(palette, 0, sizeOfPalette);
                metadata.palette = palette;
                metadata.paletteSize = numberOfEntries;

                if (bitsPerPixel == 1) {
                    imageType = VERSION_4_1_BIT;
                } else if (bitsPerPixel == 4) {
                    imageType = VERSION_4_4_BIT;
                } else if (bitsPerPixel == 8) {
                    imageType = VERSION_4_8_BIT;
                } else if (bitsPerPixel == 16) {
                    imageType = VERSION_4_16_BIT;
                    if ((int)compression == BI_RGB) {
                        redMask = 0x7C00;
                        greenMask = 0x3E0;
                        blueMask = 0x1F;
                    }
                } else if (bitsPerPixel == 24) {
                    imageType = VERSION_4_24_BIT;
                } else if (bitsPerPixel == 32) {
                    imageType = VERSION_4_32_BIT;
                    if ((int)compression == BI_RGB) {
                        redMask   = 0x00FF0000;
                        greenMask = 0x0000FF00;
                        blueMask  = 0x000000FF;
                    }
                }

                metadata.redMask = redMask;
                metadata.greenMask = greenMask;
                metadata.blueMask = blueMask;
                metadata.alphaMask = alphaMask;
            } else {
                throw new
                    RuntimeException(I18N.getString("BMPImageReader3"));
            }
        }

        if (height > 0) {
            // bottom up image
            isBottomUp = true;
        } else {
            // top down image
            isBottomUp = false;
            height = Math.abs(height);
        }

        // Reset Image Layout so there's only one tile.
        //Define the color space
        ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
        if (metadata.colorSpace == PROFILE_LINKED ||
            metadata.colorSpace == PROFILE_EMBEDDED) {

            iis.mark();
            iis.skipBytes(profileData - size);
            byte[] profile = new byte[profileSize];
            iis.readFully(profile, 0, profileSize);
            iis.reset();

            try {
                if (metadata.colorSpace == PROFILE_LINKED)
                    colorSpace =
                        new ICC_ColorSpace(ICC_Profile.getInstance(new String(profile)));
                else
                    colorSpace =
                        new ICC_ColorSpace(ICC_Profile.getInstance(profile));
            } catch (Exception e) {
                colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
            }
        }

        // When number of bitsPerPixel is <= 8, we use IndexColorModel.
        if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {

            numBands = 1;

            if (bitsPerPixel == 8) {
                int[] bandOffsets = new int[numBands];
                for (int i = 0; i < numBands; i++) {
                    bandOffsets[i] = numBands -1 -i;
                }
                sampleModel =
                    new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                                                    width, height,
                                                    numBands,
                                                    numBands * width,
                                                    bandOffsets);
            } else {
                // 1 and 4 bit pixels can be stored in a packed format.
                sampleModel =
                    new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
                                                    width, height,
                                                    bitsPerPixel);
            }

            // Create IndexColorModel from the palette.
            byte r[], g[], b[];
            if (imageType == VERSION_2_1_BIT ||
                imageType == VERSION_2_4_BIT ||
                imageType == VERSION_2_8_BIT) {


                size = palette.length/3;

                if (size > 256) {
                    size = 256;
                }

                int off;
                r = new byte[(int)size];
                g = new byte[(int)size];
                b = new byte[(int)size];
                for (int i=0; i<(int)size; i++) {
                    off = 3 * i;
                    b[i] = palette[off];
                    g[i] = palette[off+1];
                    r[i] = palette[off+2];
                }
            } else {
                size = palette.length/4;

                if (size > 256) {
                    size = 256;
                }

                int off;
                r = new byte[(int)size];
                g = new byte[(int)size];
                b = new byte[(int)size];
                for (int i=0; i<size; i++) {
                    off = 4 * i;
                    b[i] = palette[off];
                    g[i] = palette[off+1];
                    r[i] = palette[off+2];
                }
            }

            if (ImageUtil.isIndicesForGrayscale(r, g, b))
                colorModel =
                    ImageUtil.createColorModel(null, sampleModel);
            else
                colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
        } else if (bitsPerPixel == 16) {
            numBands = 3;
            sampleModel =
                new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
                                                 width, height,
                                                 new int[] {redMask, greenMask, blueMask});

            colorModel =
                new DirectColorModel(colorSpace,
                                     16, redMask, greenMask, blueMask, 0,
                                     false, DataBuffer.TYPE_USHORT);
		   
        } else if (bitsPerPixel == 32) {
            numBands = alphaMask == 0 ? 3 : 4;

            // The number of bands in the SampleModel is determined by
            // the length of the mask array passed in.
            int[] bitMasks = numBands == 3 ?
                new int[] {redMask, greenMask, blueMask} :
                new int[] {redMask, greenMask, blueMask, alphaMask};

                sampleModel =
                    new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
                                                     width, height,
                                                     bitMasks);

                colorModel =
                    new DirectColorModel(colorSpace,
                                         32, redMask, greenMask, blueMask, alphaMask,
                                         false, DataBuffer.TYPE_INT);
        } else {
            numBands = 3;
            // Create SampleModel
            int[] bandOffsets = new int[numBands];
            for (int i = 0; i < numBands; i++) {
                bandOffsets[i] = numBands -1 -i;
            }

            sampleModel =
                new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                                                width, height,
                                                numBands,
                                                numBands * width,
                                                bandOffsets);

            colorModel =
                ImageUtil.createColorModel(colorSpace, sampleModel);
        }

        originalSampleModel = sampleModel;
        originalColorModel = colorModel;

        // Reset to the start of bitmap; then jump to the
        //start of image data
        iis.reset();
        iis.skipBytes(bitmapOffset);
        gotHeader = true;       
    
private voidreadRLE4(byte[] bdata)


        // If imageSize field is not specified, calculate it.
        int imSize = (int)imageSize;
        if (imSize == 0) {
            imSize = (int)(bitmapFileSize - bitmapOffset);
        }
        
        int padding = 0;
        // If width is not 32 byte aligned, then while uncompressing each
        // scanline will have padding bytes, calculate the amount of padding
        int remainder = width % 4;
        if (remainder != 0) {
            padding = 4 - remainder;
        }
        
        // Read till we have the whole image
        byte[] values = new byte[imSize];
        iis.readFully(values, 0, imSize);

        // Decompress the RLE4 compressed data.
        decodeRLE4(imSize, padding, values, bdata);
    
private voidreadRLE8(byte[] bdata)

        // If imageSize field is not provided, calculate it.
        int imSize = (int)imageSize;
        if (imSize == 0) {
            imSize = (int)(bitmapFileSize - bitmapOffset);
        }

        int padding = 0;
        // If width is not 32 bit aligned, then while uncompressing each
        // scanline will have padding bytes, calculate the amount of padding
        int remainder = width % 4;
        if (remainder != 0) {
            padding = 4 - remainder;
        }

        // Read till we have the whole image
        byte values[] = new byte[imSize];
        int bytesRead = 0;
        iis.readFully(values, 0, imSize);

        // Since data is compressed, decompress it
        decodeRLE8(imSize, padding, values, bdata);
    
public java.awt.image.RasterreadRaster(int imageIndex, javax.imageio.ImageReadParam param)

        BufferedImage bi = read(imageIndex, param);
        return bi.getData();
    
public voidreset()

        super.reset();
        iis = null;
        resetHeaderInfo();
    
private voidresetHeaderInfo()

        gotHeader = false;
        bi = null;
        sampleModel = originalSampleModel = null;
        colorModel = originalColorModel = null;
    
public voidsetInput(java.lang.Object input, boolean seekForwardOnly, boolean ignoreMetadata)
Overrides the method defined in the superclass.

        super.setInput(input, seekForwardOnly, ignoreMetadata);
        iis = (ImageInputStream) input; // Always works
        if(iis != null)
            iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
        resetHeaderInfo();