| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.imageio.plugins.bmp;  | 
 | 
 | 
 | 
import java.awt.Rectangle;  | 
 | 
import java.awt.image.ColorModel;  | 
 | 
import java.awt.image.ComponentSampleModel;  | 
 | 
import java.awt.image.DataBuffer;  | 
 | 
import java.awt.image.DataBufferByte;  | 
 | 
import java.awt.image.DataBufferInt;  | 
 | 
import java.awt.image.DataBufferShort;  | 
 | 
import java.awt.image.DataBufferUShort;  | 
 | 
import java.awt.image.DirectColorModel;  | 
 | 
import java.awt.image.IndexColorModel;  | 
 | 
import java.awt.image.MultiPixelPackedSampleModel;  | 
 | 
import java.awt.image.BandedSampleModel;  | 
 | 
import java.awt.image.Raster;  | 
 | 
import java.awt.image.RenderedImage;  | 
 | 
import java.awt.image.SampleModel;  | 
 | 
import java.awt.image.SinglePixelPackedSampleModel;  | 
 | 
import java.awt.image.BufferedImage;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.io.ByteArrayOutputStream;  | 
 | 
import java.nio.ByteOrder;  | 
 | 
import java.util.Iterator;  | 
 | 
 | 
 | 
import javax.imageio.IIOImage;  | 
 | 
import javax.imageio.ImageIO;  | 
 | 
import javax.imageio.ImageTypeSpecifier;  | 
 | 
import javax.imageio.ImageWriteParam;  | 
 | 
import javax.imageio.ImageWriter;  | 
 | 
import javax.imageio.metadata.IIOMetadata;  | 
 | 
import javax.imageio.spi.ImageWriterSpi;  | 
 | 
import javax.imageio.stream.ImageOutputStream;  | 
 | 
import javax.imageio.event.IIOWriteProgressListener;  | 
 | 
import javax.imageio.event.IIOWriteWarningListener;  | 
 | 
 | 
 | 
 | 
 | 
import javax.imageio.plugins.bmp.BMPImageWriteParam;  | 
 | 
import com.sun.imageio.plugins.common.ImageUtil;  | 
 | 
import com.sun.imageio.plugins.common.I18N;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public class BMPImageWriter extends ImageWriter implements BMPConstants { | 
 | 
      | 
 | 
    private ImageOutputStream stream = null;  | 
 | 
    private ByteArrayOutputStream embedded_stream = null;  | 
 | 
    private int version;  | 
 | 
    private int compressionType;  | 
 | 
    private boolean isTopDown;  | 
 | 
    private int w, h;  | 
 | 
    private int compImageSize = 0;  | 
 | 
    private int[] bitMasks;  | 
 | 
    private int[] bitPos;  | 
 | 
    private byte[] bpixels;  | 
 | 
    private short[] spixels;  | 
 | 
    private int[] ipixels;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public BMPImageWriter(ImageWriterSpi originator) { | 
 | 
        super(originator);  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setOutput(Object output) { | 
 | 
        super.setOutput(output);   | 
 | 
        if (output != null) { | 
 | 
            if (!(output instanceof ImageOutputStream))  | 
 | 
                throw new IllegalArgumentException(I18N.getString("BMPImageWriter0")); | 
 | 
            this.stream = (ImageOutputStream)output;  | 
 | 
            stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);  | 
 | 
        } else  | 
 | 
            this.stream = null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public ImageWriteParam getDefaultWriteParam() { | 
 | 
        return new BMPImageWriteParam();  | 
 | 
    }  | 
 | 
 | 
 | 
    public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,  | 
 | 
                                               ImageWriteParam param) { | 
 | 
        BMPMetadata meta = new BMPMetadata();  | 
 | 
        meta.bmpVersion = VERSION_3;  | 
 | 
        meta.compression = getPreferredCompressionType(imageType);  | 
 | 
        if (param != null  | 
 | 
            && param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) { | 
 | 
            meta.compression = BMPCompressionTypes.getType(param.getCompressionType());  | 
 | 
        }  | 
 | 
        meta.bitsPerPixel = (short)imageType.getColorModel().getPixelSize();  | 
 | 
        return meta;  | 
 | 
    }  | 
 | 
 | 
 | 
    public IIOMetadata convertStreamMetadata(IIOMetadata inData,  | 
 | 
                                             ImageWriteParam param) { | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public IIOMetadata convertImageMetadata(IIOMetadata metadata,  | 
 | 
                                            ImageTypeSpecifier type,  | 
 | 
                                            ImageWriteParam param) { | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean canWriteRasters() { | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void write(IIOMetadata streamMetadata,  | 
 | 
                      IIOImage image,  | 
 | 
                      ImageWriteParam param) throws IOException { | 
 | 
 | 
 | 
        if (stream == null) { | 
 | 
            throw new IllegalStateException(I18N.getString("BMPImageWriter7")); | 
 | 
        }  | 
 | 
 | 
 | 
        if (image == null) { | 
 | 
            throw new IllegalArgumentException(I18N.getString("BMPImageWriter8")); | 
 | 
        }  | 
 | 
 | 
 | 
        clearAbortRequest();  | 
 | 
        processImageStarted(0);  | 
 | 
        if (param == null)  | 
 | 
            param = getDefaultWriteParam();  | 
 | 
 | 
 | 
        BMPImageWriteParam bmpParam = (BMPImageWriteParam)param;  | 
 | 
 | 
 | 
          | 
 | 
        int bitsPerPixel = 24;  | 
 | 
        boolean isPalette = false;  | 
 | 
        int paletteEntries = 0;  | 
 | 
        IndexColorModel icm = null;  | 
 | 
 | 
 | 
        RenderedImage input = null;  | 
 | 
        Raster inputRaster = null;  | 
 | 
        boolean writeRaster = image.hasRaster();  | 
 | 
        Rectangle sourceRegion = param.getSourceRegion();  | 
 | 
        SampleModel sampleModel = null;  | 
 | 
        ColorModel colorModel = null;  | 
 | 
 | 
 | 
        compImageSize = 0;  | 
 | 
 | 
 | 
        if (writeRaster) { | 
 | 
            inputRaster = image.getRaster();  | 
 | 
            sampleModel = inputRaster.getSampleModel();  | 
 | 
            colorModel = ImageUtil.createColorModel(null, sampleModel);  | 
 | 
            if (sourceRegion == null)  | 
 | 
                sourceRegion = inputRaster.getBounds();  | 
 | 
            else  | 
 | 
                sourceRegion = sourceRegion.intersection(inputRaster.getBounds());  | 
 | 
        } else { | 
 | 
            input = image.getRenderedImage();  | 
 | 
            sampleModel = input.getSampleModel();  | 
 | 
            colorModel = input.getColorModel();  | 
 | 
            Rectangle rect = new Rectangle(input.getMinX(), input.getMinY(),  | 
 | 
                                           input.getWidth(), input.getHeight());  | 
 | 
            if (sourceRegion == null)  | 
 | 
                sourceRegion = rect;  | 
 | 
            else  | 
 | 
                sourceRegion = sourceRegion.intersection(rect);  | 
 | 
        }  | 
 | 
 | 
 | 
        IIOMetadata imageMetadata = image.getMetadata();  | 
 | 
        BMPMetadata bmpImageMetadata = null;  | 
 | 
        if (imageMetadata != null  | 
 | 
            && imageMetadata instanceof BMPMetadata)  | 
 | 
        { | 
 | 
            bmpImageMetadata = (BMPMetadata)imageMetadata;  | 
 | 
        } else { | 
 | 
            ImageTypeSpecifier imageType =  | 
 | 
                new ImageTypeSpecifier(colorModel, sampleModel);  | 
 | 
 | 
 | 
            bmpImageMetadata = (BMPMetadata)getDefaultImageMetadata(imageType,  | 
 | 
                                                                    param);  | 
 | 
        }  | 
 | 
 | 
 | 
        if (sourceRegion.isEmpty())  | 
 | 
            throw new RuntimeException(I18N.getString("BMPImageWrite0")); | 
 | 
 | 
 | 
        int scaleX = param.getSourceXSubsampling();  | 
 | 
        int scaleY = param.getSourceYSubsampling();  | 
 | 
        int xOffset = param.getSubsamplingXOffset();  | 
 | 
        int yOffset = param.getSubsamplingYOffset();  | 
 | 
 | 
 | 
          | 
 | 
        int dataType = sampleModel.getDataType();  | 
 | 
 | 
 | 
        sourceRegion.translate(xOffset, yOffset);  | 
 | 
        sourceRegion.width -= xOffset;  | 
 | 
        sourceRegion.height -= yOffset;  | 
 | 
 | 
 | 
        int minX = sourceRegion.x / scaleX;  | 
 | 
        int minY = sourceRegion.y / scaleY;  | 
 | 
        w = (sourceRegion.width + scaleX - 1) / scaleX;  | 
 | 
        h = (sourceRegion.height + scaleY - 1) / scaleY;  | 
 | 
        xOffset = sourceRegion.x % scaleX;  | 
 | 
        yOffset = sourceRegion.y % scaleY;  | 
 | 
 | 
 | 
        Rectangle destinationRegion = new Rectangle(minX, minY, w, h);  | 
 | 
        boolean noTransform = destinationRegion.equals(sourceRegion);  | 
 | 
 | 
 | 
          | 
 | 
        int[] sourceBands = param.getSourceBands();  | 
 | 
        boolean noSubband = true;  | 
 | 
        int numBands = sampleModel.getNumBands();  | 
 | 
 | 
 | 
        if (sourceBands != null) { | 
 | 
            sampleModel = sampleModel.createSubsetSampleModel(sourceBands);  | 
 | 
            colorModel = null;  | 
 | 
            noSubband = false;  | 
 | 
            numBands = sampleModel.getNumBands();  | 
 | 
        } else { | 
 | 
            sourceBands = new int[numBands];  | 
 | 
            for (int i = 0; i < numBands; i++)  | 
 | 
                sourceBands[i] = i;  | 
 | 
        }  | 
 | 
 | 
 | 
        int[] bandOffsets = null;  | 
 | 
        boolean bgrOrder = true;  | 
 | 
 | 
 | 
        if (sampleModel instanceof ComponentSampleModel) { | 
 | 
            bandOffsets = ((ComponentSampleModel)sampleModel).getBandOffsets();  | 
 | 
            if (sampleModel instanceof BandedSampleModel) { | 
 | 
                // for images with BandedSampleModel we can not work  | 
 | 
                  | 
 | 
                bgrOrder = false;  | 
 | 
            } else { | 
 | 
                // we can work with raster directly only in case of  | 
 | 
                // BGR component order.  | 
 | 
                  | 
 | 
                for (int i = 0; i < bandOffsets.length; i++) { | 
 | 
                    bgrOrder &= (bandOffsets[i] == (bandOffsets.length - i - 1));  | 
 | 
                }  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            if (sampleModel instanceof SinglePixelPackedSampleModel) { | 
 | 
 | 
 | 
                // BugId 4892214: we can not work with raster directly  | 
 | 
                // if image have different color order than RGB.  | 
 | 
                  | 
 | 
                int[] bitOffsets = ((SinglePixelPackedSampleModel)sampleModel).getBitOffsets();  | 
 | 
                for (int i=0; i<bitOffsets.length-1; i++) { | 
 | 
                    bgrOrder &= bitOffsets[i] > bitOffsets[i+1];  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (bandOffsets == null) { | 
 | 
            // we will use getPixels() to extract pixel data for writePixels()  | 
 | 
              | 
 | 
            bandOffsets = new int[numBands];  | 
 | 
            for (int i = 0; i < numBands; i++)  | 
 | 
                bandOffsets[i] = i;  | 
 | 
        }  | 
 | 
 | 
 | 
        noTransform &= bgrOrder;  | 
 | 
 | 
 | 
        int sampleSize[] = sampleModel.getSampleSize();  | 
 | 
 | 
 | 
        //XXX: check more  | 
 | 
 | 
 | 
          | 
 | 
        int destScanlineBytes = w * numBands;  | 
 | 
 | 
 | 
        switch(bmpParam.getCompressionMode()) { | 
 | 
        case ImageWriteParam.MODE_EXPLICIT:  | 
 | 
            compressionType = BMPCompressionTypes.getType(bmpParam.getCompressionType());  | 
 | 
            break;  | 
 | 
        case ImageWriteParam.MODE_COPY_FROM_METADATA:  | 
 | 
            compressionType = bmpImageMetadata.compression;  | 
 | 
            break;  | 
 | 
        case ImageWriteParam.MODE_DEFAULT:  | 
 | 
            compressionType = getPreferredCompressionType(colorModel, sampleModel);  | 
 | 
            break;  | 
 | 
        default:  | 
 | 
              | 
 | 
            compressionType = BI_RGB;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (!canEncodeImage(compressionType, colorModel, sampleModel)) { | 
 | 
            throw new IOException("Image can not be encoded with compression type " | 
 | 
                                  + BMPCompressionTypes.getName(compressionType));  | 
 | 
        }  | 
 | 
 | 
 | 
        byte r[] = null, g[] = null, b[] = null, a[] = null;  | 
 | 
 | 
 | 
        if (compressionType == BI_BITFIELDS) { | 
 | 
            bitsPerPixel =  | 
 | 
                DataBuffer.getDataTypeSize(sampleModel.getDataType());  | 
 | 
 | 
 | 
            if (bitsPerPixel != 16 && bitsPerPixel != 32) { | 
 | 
                // we should use 32bpp images in case of BI_BITFIELD  | 
 | 
                  | 
 | 
                bitsPerPixel = 32;  | 
 | 
 | 
 | 
                // Setting this flag to false ensures that generic  | 
 | 
                  | 
 | 
                noTransform = false;  | 
 | 
            }  | 
 | 
 | 
 | 
            destScanlineBytes = w * bitsPerPixel + 7 >> 3;  | 
 | 
 | 
 | 
            isPalette = true;  | 
 | 
            paletteEntries = 3;  | 
 | 
            r = new byte[paletteEntries];  | 
 | 
            g = new byte[paletteEntries];  | 
 | 
            b = new byte[paletteEntries];  | 
 | 
            a = new byte[paletteEntries];  | 
 | 
 | 
 | 
            int rmask = 0x00ff0000;  | 
 | 
            int gmask = 0x0000ff00;  | 
 | 
            int bmask = 0x000000ff;  | 
 | 
 | 
 | 
            if (bitsPerPixel == 16) { | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (colorModel instanceof DirectColorModel) { | 
 | 
                    DirectColorModel dcm = (DirectColorModel)colorModel;  | 
 | 
                    rmask = dcm.getRedMask();  | 
 | 
                    gmask = dcm.getGreenMask();  | 
 | 
                    bmask = dcm.getBlueMask();  | 
 | 
                } else { | 
 | 
                    // it is unlikely, but if it happens, we should throw  | 
 | 
                      | 
 | 
                    throw new IOException("Image can not be encoded with " + | 
 | 
                                          "compression type " +  | 
 | 
                                          BMPCompressionTypes.getName(compressionType));  | 
 | 
                }  | 
 | 
            }  | 
 | 
            writeMaskToPalette(rmask, 0, r, g, b, a);  | 
 | 
            writeMaskToPalette(gmask, 1, r, g, b, a);  | 
 | 
            writeMaskToPalette(bmask, 2, r, g, b, a);  | 
 | 
 | 
 | 
            if (!noTransform) { | 
 | 
                  | 
 | 
                bitMasks = new int[3];  | 
 | 
                bitMasks[0] = rmask;  | 
 | 
                bitMasks[1] = gmask;  | 
 | 
                bitMasks[2] = bmask;  | 
 | 
 | 
 | 
                bitPos = new int[3];  | 
 | 
                bitPos[0] = firstLowBit(rmask);  | 
 | 
                bitPos[1] = firstLowBit(gmask);  | 
 | 
                bitPos[2] = firstLowBit(bmask);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (colorModel instanceof IndexColorModel) { | 
 | 
                icm = (IndexColorModel)colorModel;  | 
 | 
            }  | 
 | 
        } else {  | 
 | 
            if (colorModel instanceof IndexColorModel) { | 
 | 
                isPalette = true;  | 
 | 
                icm = (IndexColorModel)colorModel;  | 
 | 
                paletteEntries = icm.getMapSize();  | 
 | 
 | 
 | 
                if (paletteEntries <= 2) { | 
 | 
                    bitsPerPixel = 1;  | 
 | 
                    destScanlineBytes = w + 7 >> 3;  | 
 | 
                } else if (paletteEntries <= 16) { | 
 | 
                    bitsPerPixel = 4;  | 
 | 
                    destScanlineBytes = w + 1 >> 1;  | 
 | 
                } else if (paletteEntries <= 256) { | 
 | 
                    bitsPerPixel = 8;  | 
 | 
                } else { | 
 | 
                    // Cannot be written as a Palette image. So write out as  | 
 | 
                      | 
 | 
                    bitsPerPixel = 24;  | 
 | 
                    isPalette = false;  | 
 | 
                    paletteEntries = 0;  | 
 | 
                    destScanlineBytes = w * 3;  | 
 | 
                }  | 
 | 
 | 
 | 
                if (isPalette == true) { | 
 | 
                    r = new byte[paletteEntries];  | 
 | 
                    g = new byte[paletteEntries];  | 
 | 
                    b = new byte[paletteEntries];  | 
 | 
                    a = new byte[paletteEntries];  | 
 | 
 | 
 | 
                    icm.getAlphas(a);  | 
 | 
                    icm.getReds(r);  | 
 | 
                    icm.getGreens(g);  | 
 | 
                    icm.getBlues(b);  | 
 | 
                }  | 
 | 
 | 
 | 
            } else { | 
 | 
                  | 
 | 
                if (numBands == 1) { | 
 | 
 | 
 | 
                    isPalette = true;  | 
 | 
                    paletteEntries = 256;  | 
 | 
                    bitsPerPixel = sampleSize[0];  | 
 | 
 | 
 | 
                    destScanlineBytes = (w * bitsPerPixel + 7 >> 3);  | 
 | 
 | 
 | 
                    r = new byte[256];  | 
 | 
                    g = new byte[256];  | 
 | 
                    b = new byte[256];  | 
 | 
                    a = new byte[256];  | 
 | 
 | 
 | 
                    for (int i = 0; i < 256; i++) { | 
 | 
                        r[i] = (byte)i;  | 
 | 
                        g[i] = (byte)i;  | 
 | 
                        b[i] = (byte)i;  | 
 | 
                        a[i] = (byte)255;  | 
 | 
                    }  | 
 | 
 | 
 | 
                } else { | 
 | 
                    if (sampleModel instanceof SinglePixelPackedSampleModel &&  | 
 | 
                        noSubband)  | 
 | 
                    { | 
 | 
                          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                         */  | 
 | 
                        int[] sample_sizes = sampleModel.getSampleSize();  | 
 | 
                        bitsPerPixel = 0;  | 
 | 
                        for (int size : sample_sizes) { | 
 | 
                            bitsPerPixel += size;  | 
 | 
                        }  | 
 | 
                        bitsPerPixel = roundBpp(bitsPerPixel);  | 
 | 
                        if (bitsPerPixel != DataBuffer.getDataTypeSize(sampleModel.getDataType())) { | 
 | 
                            noTransform = false;  | 
 | 
                        }  | 
 | 
                        destScanlineBytes = w * bitsPerPixel + 7 >> 3;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        int fileSize = 0;  | 
 | 
        int offset = 0;  | 
 | 
        int headerSize = 0;  | 
 | 
        int imageSize = 0;  | 
 | 
        int xPelsPerMeter = 0;  | 
 | 
        int yPelsPerMeter = 0;  | 
 | 
        int colorsUsed = 0;  | 
 | 
        int colorsImportant = paletteEntries;  | 
 | 
 | 
 | 
          | 
 | 
        int padding = destScanlineBytes % 4;  | 
 | 
        if (padding != 0) { | 
 | 
            padding = 4 - padding;  | 
 | 
        }  | 
 | 
 | 
 | 
 | 
 | 
        // FileHeader is 14 bytes, BitmapHeader is 40 bytes,  | 
 | 
          | 
 | 
        offset = 54 + paletteEntries * 4;  | 
 | 
 | 
 | 
        imageSize = (destScanlineBytes + padding) * h;  | 
 | 
        fileSize = imageSize + offset;  | 
 | 
        headerSize = 40;  | 
 | 
 | 
 | 
        long headPos = stream.getStreamPosition();  | 
 | 
 | 
 | 
        writeFileHeader(fileSize, offset);  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (compressionType == BI_RGB ||  | 
 | 
            compressionType == BI_BITFIELDS)  | 
 | 
        { | 
 | 
            isTopDown = bmpParam.isTopDown();  | 
 | 
        } else { | 
 | 
            isTopDown = false;  | 
 | 
        }  | 
 | 
 | 
 | 
        writeInfoHeader(headerSize, bitsPerPixel);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(compressionType);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(imageSize);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(xPelsPerMeter);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(yPelsPerMeter);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(colorsUsed);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(colorsImportant);  | 
 | 
 | 
 | 
          | 
 | 
        if (isPalette == true) { | 
 | 
 | 
 | 
              | 
 | 
            if (compressionType == BI_BITFIELDS) { | 
 | 
                  | 
 | 
                for (int i=0; i<3; i++) { | 
 | 
                    int mask = (a[i]&0xFF) + ((r[i]&0xFF)*0x100) + ((g[i]&0xFF)*0x10000) + ((b[i]&0xFF)*0x1000000);  | 
 | 
                    stream.writeInt(mask);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                for (int i=0; i<paletteEntries; i++) { | 
 | 
                    stream.writeByte(b[i]);  | 
 | 
                    stream.writeByte(g[i]);  | 
 | 
                    stream.writeByte(r[i]);  | 
 | 
                    stream.writeByte(a[i]);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        int scanlineBytes = w * numBands;  | 
 | 
 | 
 | 
          | 
 | 
        int[] pixels = new int[scanlineBytes * scaleX];  | 
 | 
 | 
 | 
        // Also create a buffer to hold one line of the data  | 
 | 
          | 
 | 
        bpixels = new byte[destScanlineBytes];  | 
 | 
 | 
 | 
        int l;  | 
 | 
 | 
 | 
        if (compressionType == BI_JPEG ||  | 
 | 
            compressionType == BI_PNG) { | 
 | 
 | 
 | 
              | 
 | 
            embedded_stream = new ByteArrayOutputStream();  | 
 | 
            writeEmbedded(image, bmpParam);  | 
 | 
              | 
 | 
            embedded_stream.flush();  | 
 | 
            imageSize = embedded_stream.size();  | 
 | 
 | 
 | 
            long endPos = stream.getStreamPosition();  | 
 | 
            fileSize = (int)(offset + imageSize);  | 
 | 
            stream.seek(headPos);  | 
 | 
            writeSize(fileSize, 2);  | 
 | 
            stream.seek(headPos);  | 
 | 
            writeSize(imageSize, 34);  | 
 | 
            stream.seek(endPos);  | 
 | 
            stream.write(embedded_stream.toByteArray());  | 
 | 
            embedded_stream = null;  | 
 | 
 | 
 | 
            if (abortRequested()) { | 
 | 
                processWriteAborted();  | 
 | 
            } else { | 
 | 
                processImageComplete();  | 
 | 
                stream.flushBefore(stream.getStreamPosition());  | 
 | 
            }  | 
 | 
 | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
        int maxBandOffset = bandOffsets[0];  | 
 | 
        for (int i = 1; i < bandOffsets.length; i++)  | 
 | 
            if (bandOffsets[i] > maxBandOffset)  | 
 | 
                maxBandOffset = bandOffsets[i];  | 
 | 
 | 
 | 
        int[] pixel = new int[maxBandOffset + 1];  | 
 | 
 | 
 | 
        int destScanlineLength = destScanlineBytes;  | 
 | 
 | 
 | 
        if (noTransform && noSubband) { | 
 | 
            destScanlineLength = destScanlineBytes / (DataBuffer.getDataTypeSize(dataType)>>3);  | 
 | 
        }  | 
 | 
        for (int i = 0; i < h; i++) { | 
 | 
            if (abortRequested()) { | 
 | 
                break;  | 
 | 
            }  | 
 | 
 | 
 | 
            int row = minY + i;  | 
 | 
 | 
 | 
            if (!isTopDown)  | 
 | 
                row = minY + h - i -1;  | 
 | 
 | 
 | 
              | 
 | 
            Raster src = inputRaster;  | 
 | 
 | 
 | 
            Rectangle srcRect =  | 
 | 
                new Rectangle(minX * scaleX + xOffset,  | 
 | 
                              row * scaleY + yOffset,  | 
 | 
                              (w - 1)* scaleX + 1,  | 
 | 
                              1);  | 
 | 
            if (!writeRaster)  | 
 | 
                src = input.getData(srcRect);  | 
 | 
 | 
 | 
            if (noTransform && noSubband) { | 
 | 
                SampleModel sm = src.getSampleModel();  | 
 | 
                int pos = 0;  | 
 | 
                int startX = srcRect.x - src.getSampleModelTranslateX();  | 
 | 
                int startY = srcRect.y - src.getSampleModelTranslateY();  | 
 | 
                if (sm instanceof ComponentSampleModel) { | 
 | 
                    ComponentSampleModel csm = (ComponentSampleModel)sm;  | 
 | 
                    pos = csm.getOffset(startX, startY, 0);  | 
 | 
                    for(int nb=1; nb < csm.getNumBands(); nb++) { | 
 | 
                        if (pos > csm.getOffset(startX, startY, nb)) { | 
 | 
                            pos = csm.getOffset(startX, startY, nb);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                } else if (sm instanceof MultiPixelPackedSampleModel) { | 
 | 
                    MultiPixelPackedSampleModel mppsm =  | 
 | 
                        (MultiPixelPackedSampleModel)sm;  | 
 | 
                    pos = mppsm.getOffset(startX, startY);  | 
 | 
                } else if (sm instanceof SinglePixelPackedSampleModel) { | 
 | 
                    SinglePixelPackedSampleModel sppsm =  | 
 | 
                        (SinglePixelPackedSampleModel)sm;  | 
 | 
                    pos = sppsm.getOffset(startX, startY);  | 
 | 
                }  | 
 | 
 | 
 | 
                if (compressionType == BI_RGB || compressionType == BI_BITFIELDS){ | 
 | 
                    switch(dataType) { | 
 | 
                    case DataBuffer.TYPE_BYTE:  | 
 | 
                        byte[] bdata =  | 
 | 
                            ((DataBufferByte)src.getDataBuffer()).getData();  | 
 | 
                        stream.write(bdata, pos, destScanlineLength);  | 
 | 
                        break;  | 
 | 
 | 
 | 
                    case DataBuffer.TYPE_SHORT:  | 
 | 
                        short[] sdata =  | 
 | 
                            ((DataBufferShort)src.getDataBuffer()).getData();  | 
 | 
                        stream.writeShorts(sdata, pos, destScanlineLength);  | 
 | 
                        break;  | 
 | 
 | 
 | 
                    case DataBuffer.TYPE_USHORT:  | 
 | 
                        short[] usdata =  | 
 | 
                            ((DataBufferUShort)src.getDataBuffer()).getData();  | 
 | 
                        stream.writeShorts(usdata, pos, destScanlineLength);  | 
 | 
                        break;  | 
 | 
 | 
 | 
                    case DataBuffer.TYPE_INT:  | 
 | 
                        int[] idata =  | 
 | 
                            ((DataBufferInt)src.getDataBuffer()).getData();  | 
 | 
                        stream.writeInts(idata, pos, destScanlineLength);  | 
 | 
                        break;  | 
 | 
                    }  | 
 | 
 | 
 | 
                    for(int k=0; k<padding; k++) { | 
 | 
                        stream.writeByte(0);  | 
 | 
                    }  | 
 | 
                } else if (compressionType == BI_RLE4) { | 
 | 
                    if (bpixels == null || bpixels.length < scanlineBytes)  | 
 | 
                        bpixels = new byte[scanlineBytes];  | 
 | 
                    src.getPixels(srcRect.x, srcRect.y,  | 
 | 
                                  srcRect.width, srcRect.height, pixels);  | 
 | 
                    for (int h=0; h<scanlineBytes; h++) { | 
 | 
                        bpixels[h] = (byte)pixels[h];  | 
 | 
                    }  | 
 | 
                    encodeRLE4(bpixels, scanlineBytes);  | 
 | 
                } else if (compressionType == BI_RLE8) { | 
 | 
                    //byte[] bdata =  | 
 | 
                    //    ((DataBufferByte)src.getDataBuffer()).getData();  | 
 | 
                    //System.out.println("bdata.length="+bdata.length); | 
 | 
                      | 
 | 
                    if (bpixels == null || bpixels.length < scanlineBytes)  | 
 | 
                        bpixels = new byte[scanlineBytes];  | 
 | 
                    src.getPixels(srcRect.x, srcRect.y,  | 
 | 
                                  srcRect.width, srcRect.height, pixels);  | 
 | 
                    for (int h=0; h<scanlineBytes; h++) { | 
 | 
                        bpixels[h] = (byte)pixels[h];  | 
 | 
                    }  | 
 | 
 | 
 | 
                    encodeRLE8(bpixels, scanlineBytes);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                src.getPixels(srcRect.x, srcRect.y,  | 
 | 
                              srcRect.width, srcRect.height, pixels);  | 
 | 
 | 
 | 
                if (scaleX != 1 || maxBandOffset != numBands - 1) { | 
 | 
                    for (int j = 0, k = 0, n=0; j < w;  | 
 | 
                         j++, k += scaleX * numBands, n += numBands)  | 
 | 
                    { | 
 | 
                        System.arraycopy(pixels, k, pixel, 0, pixel.length);  | 
 | 
 | 
 | 
                        for (int m = 0; m < numBands; m++) { | 
 | 
                              | 
 | 
                            pixels[n + m] = pixel[sourceBands[m]];  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                writePixels(0, scanlineBytes, bitsPerPixel, pixels,  | 
 | 
                            padding, numBands, icm);  | 
 | 
            }  | 
 | 
 | 
 | 
            processImageProgress(100.0f * (((float)i) / ((float)h)));  | 
 | 
        }  | 
 | 
 | 
 | 
        if (compressionType == BI_RLE4 ||  | 
 | 
            compressionType == BI_RLE8) { | 
 | 
              | 
 | 
            stream.writeByte(0);  | 
 | 
            stream.writeByte(1);  | 
 | 
            incCompImageSize(2);  | 
 | 
              | 
 | 
            imageSize = compImageSize;  | 
 | 
            fileSize = compImageSize + offset;  | 
 | 
            long endPos = stream.getStreamPosition();  | 
 | 
            stream.seek(headPos);  | 
 | 
            writeSize(fileSize, 2);  | 
 | 
            stream.seek(headPos);  | 
 | 
            writeSize(imageSize, 34);  | 
 | 
            stream.seek(endPos);  | 
 | 
        }  | 
 | 
 | 
 | 
        if (abortRequested()) { | 
 | 
            processWriteAborted();  | 
 | 
        } else { | 
 | 
            processImageComplete();  | 
 | 
            stream.flushBefore(stream.getStreamPosition());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void writePixels(int l, int scanlineBytes, int bitsPerPixel,  | 
 | 
                             int pixels[],  | 
 | 
                             int padding, int numBands,  | 
 | 
                             IndexColorModel icm) throws IOException { | 
 | 
        int pixel = 0;  | 
 | 
        int k = 0;  | 
 | 
        switch (bitsPerPixel) { | 
 | 
 | 
 | 
        case 1:  | 
 | 
 | 
 | 
            for (int j=0; j<scanlineBytes/8; j++) { | 
 | 
                bpixels[k++] = (byte)((pixels[l++]  << 7) |  | 
 | 
                                      (pixels[l++]  << 6) |  | 
 | 
                                      (pixels[l++]  << 5) |  | 
 | 
                                      (pixels[l++]  << 4) |  | 
 | 
                                      (pixels[l++]  << 3) |  | 
 | 
                                      (pixels[l++]  << 2) |  | 
 | 
                                      (pixels[l++]  << 1) |  | 
 | 
                                      pixels[l++]);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (scanlineBytes%8 > 0) { | 
 | 
                pixel = 0;  | 
 | 
                for (int j=0; j<scanlineBytes%8; j++) { | 
 | 
                    pixel |= (pixels[l++] << (7 - j));  | 
 | 
                }  | 
 | 
                bpixels[k++] = (byte)pixel;  | 
 | 
            }  | 
 | 
            stream.write(bpixels, 0, (scanlineBytes+7)/8);  | 
 | 
 | 
 | 
            break;  | 
 | 
 | 
 | 
        case 4:  | 
 | 
            if (compressionType == BI_RLE4){ | 
 | 
                byte[] bipixels = new byte[scanlineBytes];  | 
 | 
                for (int h=0; h<scanlineBytes; h++) { | 
 | 
                    bipixels[h] = (byte)pixels[l++];  | 
 | 
                }  | 
 | 
                encodeRLE4(bipixels, scanlineBytes);  | 
 | 
            }else { | 
 | 
                for (int j=0; j<scanlineBytes/2; j++) { | 
 | 
                    pixel = (pixels[l++] << 4) | pixels[l++];  | 
 | 
                    bpixels[k++] = (byte)pixel;  | 
 | 
                }  | 
 | 
                  | 
 | 
                if ((scanlineBytes%2) == 1) { | 
 | 
                    pixel = pixels[l] << 4;  | 
 | 
                    bpixels[k++] = (byte)pixel;  | 
 | 
                }  | 
 | 
                stream.write(bpixels, 0, (scanlineBytes+1)/2);  | 
 | 
            }  | 
 | 
            break;  | 
 | 
 | 
 | 
        case 8:  | 
 | 
            if(compressionType == BI_RLE8) { | 
 | 
                for (int h=0; h<scanlineBytes; h++) { | 
 | 
                    bpixels[h] = (byte)pixels[l++];  | 
 | 
                }  | 
 | 
                encodeRLE8(bpixels, scanlineBytes);  | 
 | 
            }else { | 
 | 
                for (int j=0; j<scanlineBytes; j++) { | 
 | 
                    bpixels[j] = (byte)pixels[l++];  | 
 | 
                }  | 
 | 
                stream.write(bpixels, 0, scanlineBytes);  | 
 | 
            }  | 
 | 
            break;  | 
 | 
 | 
 | 
        case 16:  | 
 | 
            if (spixels == null)  | 
 | 
                spixels = new short[scanlineBytes / numBands];  | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            for (int j = 0, m = 0; j < scanlineBytes; m++) { | 
 | 
                spixels[m] = 0;  | 
 | 
                if (compressionType == BI_RGB) { | 
 | 
                      | 
 | 
 | 
 | 
 | 
 | 
                     */  | 
 | 
                    spixels[m] = (short)  | 
 | 
                        (((0x1f & pixels[j    ]) << 10) |  | 
 | 
                         ((0x1f & pixels[j + 1]) <<  5) |  | 
 | 
                         ((0x1f & pixels[j + 2])      ));  | 
 | 
                     j += 3;  | 
 | 
                } else { | 
 | 
                    for(int i = 0 ; i < numBands; i++, j++) { | 
 | 
                        spixels[m] |=  | 
 | 
                            (((pixels[j]) << bitPos[i]) & bitMasks[i]);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            stream.writeShorts(spixels, 0, spixels.length);  | 
 | 
            break;  | 
 | 
 | 
 | 
        case 24:  | 
 | 
            if (numBands == 3) { | 
 | 
                for (int j=0; j<scanlineBytes; j+=3) { | 
 | 
                      | 
 | 
                    bpixels[k++] = (byte)(pixels[l+2]);  | 
 | 
                    bpixels[k++] = (byte)(pixels[l+1]);  | 
 | 
                    bpixels[k++] = (byte)(pixels[l]);  | 
 | 
                    l+=3;  | 
 | 
                }  | 
 | 
                stream.write(bpixels, 0, scanlineBytes);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                int entries = icm.getMapSize();  | 
 | 
 | 
 | 
                byte r[] = new byte[entries];  | 
 | 
                byte g[] = new byte[entries];  | 
 | 
                byte b[] = new byte[entries];  | 
 | 
 | 
 | 
                icm.getReds(r);  | 
 | 
                icm.getGreens(g);  | 
 | 
                icm.getBlues(b);  | 
 | 
                int index;  | 
 | 
 | 
 | 
                for (int j=0; j<scanlineBytes; j++) { | 
 | 
                    index = pixels[l];  | 
 | 
                    bpixels[k++] = b[index];  | 
 | 
                    bpixels[k++] = g[index];  | 
 | 
                    bpixels[k++] = b[index];  | 
 | 
                    l++;  | 
 | 
                }  | 
 | 
                stream.write(bpixels, 0, scanlineBytes*3);  | 
 | 
            }  | 
 | 
            break;  | 
 | 
 | 
 | 
        case 32:  | 
 | 
            if (ipixels == null)  | 
 | 
                ipixels = new int[scanlineBytes / numBands];  | 
 | 
            if (numBands == 3) { | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                for (int j = 0, m = 0; j < scanlineBytes; m++) { | 
 | 
                    ipixels[m] = 0;  | 
 | 
                    if (compressionType == BI_RGB) { | 
 | 
                        ipixels[m] =  | 
 | 
                            ((0xff & pixels[j + 2]) << 16) |  | 
 | 
                            ((0xff & pixels[j + 1]) <<  8) |  | 
 | 
                            ((0xff & pixels[j    ])      );  | 
 | 
                        j += 3;  | 
 | 
                    } else { | 
 | 
                        for(int i = 0 ; i < numBands; i++, j++) { | 
 | 
                            ipixels[m] |=  | 
 | 
                                (((pixels[j]) << bitPos[i]) & bitMasks[i]);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                // We have two possibilities here:  | 
 | 
                // 1. we are writing the indexed image with bitfields  | 
 | 
                //    compression (this covers also the case of BYTE_BINARY)  | 
 | 
                //    => use icm to get actual RGB color values.  | 
 | 
                // 2. we are writing the gray-scaled image with BI_BITFIELDS  | 
 | 
                //    compression  | 
 | 
                  | 
 | 
                for (int j = 0; j < scanlineBytes; j++) { | 
 | 
                    if (icm != null) { | 
 | 
                        ipixels[j] = icm.getRGB(pixels[j]);  | 
 | 
                    } else { | 
 | 
                        ipixels[j] =  | 
 | 
                            pixels[j] << 16 | pixels[j] << 8 | pixels[j];  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            stream.writeInts(ipixels, 0, ipixels.length);  | 
 | 
            break;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (compressionType == BI_RGB ||  | 
 | 
            compressionType == BI_BITFIELDS)  | 
 | 
        { | 
 | 
            for(k=0; k<padding; k++) { | 
 | 
                stream.writeByte(0);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void encodeRLE8(byte[] bpixels, int scanlineBytes)  | 
 | 
      throws IOException{ | 
 | 
 | 
 | 
        int runCount = 1, absVal = -1, j = -1;  | 
 | 
        byte runVal = 0, nextVal =0 ;  | 
 | 
 | 
 | 
        runVal = bpixels[++j];  | 
 | 
        byte[] absBuf = new byte[256];  | 
 | 
 | 
 | 
        while (j < scanlineBytes-1) { | 
 | 
            nextVal = bpixels[++j];  | 
 | 
            if (nextVal == runVal ){ | 
 | 
                if(absVal >= 3 ){ | 
 | 
                      | 
 | 
                    stream.writeByte(0);  | 
 | 
                    stream.writeByte(absVal);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    for(int a=0; a<absVal;a++){ | 
 | 
                        stream.writeByte(absBuf[a]);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                    if (!isEven(absVal)){ | 
 | 
                          | 
 | 
                        stream.writeByte(0);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                else if(absVal > -1){ | 
 | 
                    /// Absolute Encoding for less than 3  | 
 | 
                    /// treated as regular encoding  | 
 | 
                    /// Do not include the last element since it will  | 
 | 
                      | 
 | 
                    for (int b=0;b<absVal;b++){ | 
 | 
                        stream.writeByte(1);  | 
 | 
                        stream.writeByte(absBuf[b]);  | 
 | 
                        incCompImageSize(2);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                absVal = -1;  | 
 | 
                runCount++;  | 
 | 
                if (runCount == 256){ | 
 | 
                      | 
 | 
                    stream.writeByte(runCount-1);  | 
 | 
                    stream.writeByte(runVal);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    runCount = 1;  | 
 | 
                }  | 
 | 
            }  | 
 | 
            else { | 
 | 
                if (runCount > 1){ | 
 | 
                      | 
 | 
                    stream.writeByte(runCount);  | 
 | 
                    stream.writeByte(runVal);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                } else if (absVal < 0){ | 
 | 
                      | 
 | 
                    absBuf[++absVal] = runVal;  | 
 | 
                    absBuf[++absVal] = nextVal;  | 
 | 
                } else if (absVal < 254){ | 
 | 
                      | 
 | 
                    absBuf[++absVal] = nextVal;  | 
 | 
                } else { | 
 | 
                    stream.writeByte(0);  | 
 | 
                    stream.writeByte(absVal+1);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    for(int a=0; a<=absVal;a++){ | 
 | 
                        stream.writeByte(absBuf[a]);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                      | 
 | 
                    stream.writeByte(0);  | 
 | 
                    incCompImageSize(1);  | 
 | 
                    absVal = -1;  | 
 | 
                }  | 
 | 
                runVal = nextVal;  | 
 | 
                runCount = 1;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (j == scanlineBytes-1){ // EOF scanline | 
 | 
                  | 
 | 
                if (absVal == -1){ | 
 | 
                    stream.writeByte(runCount);  | 
 | 
                    stream.writeByte(runVal);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    runCount = 1;  | 
 | 
                }  | 
 | 
                else { | 
 | 
                      | 
 | 
                    if(absVal >= 2){ | 
 | 
                        stream.writeByte(0);  | 
 | 
                        stream.writeByte(absVal+1);  | 
 | 
                        incCompImageSize(2);  | 
 | 
                        for(int a=0; a<=absVal;a++){ | 
 | 
                            stream.writeByte(absBuf[a]);  | 
 | 
                            incCompImageSize(1);  | 
 | 
                        }  | 
 | 
                        if (!isEven(absVal+1)){ | 
 | 
                              | 
 | 
                            stream.writeByte(0);  | 
 | 
                            incCompImageSize(1);  | 
 | 
                        }  | 
 | 
 | 
 | 
                    }  | 
 | 
                    else if(absVal > -1){ | 
 | 
                        for (int b=0;b<=absVal;b++){ | 
 | 
                            stream.writeByte(1);  | 
 | 
                            stream.writeByte(absBuf[b]);  | 
 | 
                            incCompImageSize(2);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                /// EOF scanline  | 
 | 
 | 
 | 
                stream.writeByte(0);  | 
 | 
                stream.writeByte(0);  | 
 | 
                incCompImageSize(2);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void encodeRLE4(byte[] bipixels, int scanlineBytes)  | 
 | 
      throws IOException { | 
 | 
 | 
 | 
        int runCount=2, absVal=-1, j=-1, pixel=0, q=0;  | 
 | 
        byte runVal1=0, runVal2=0, nextVal1=0, nextVal2=0;  | 
 | 
        byte[] absBuf = new byte[256];  | 
 | 
 | 
 | 
 | 
 | 
        runVal1 = bipixels[++j];  | 
 | 
        runVal2 = bipixels[++j];  | 
 | 
 | 
 | 
        while (j < scanlineBytes-2){ | 
 | 
            nextVal1 = bipixels[++j];  | 
 | 
            nextVal2 = bipixels[++j];  | 
 | 
 | 
 | 
            if (nextVal1 == runVal1 ) { | 
 | 
 | 
 | 
                  | 
 | 
                if(absVal >= 4){ | 
 | 
                    stream.writeByte(0);  | 
 | 
                    stream.writeByte(absVal - 1);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    // we need to exclude  last 2 elts, similarity of  | 
 | 
                      | 
 | 
                    for(int a=0; a<absVal-2;a+=2){ | 
 | 
                        pixel = (absBuf[a] << 4) | absBuf[a+1];  | 
 | 
                        stream.writeByte((byte)pixel);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                      | 
 | 
                    if(!(isEven(absVal-1))){ | 
 | 
                        q = absBuf[absVal-2] << 4| 0;  | 
 | 
                        stream.writeByte(q);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                      | 
 | 
                    if ( !isEven((int)Math.ceil((absVal-1)/2)) ) { | 
 | 
                        stream.writeByte(0);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                } else if (absVal > -1){ | 
 | 
                    stream.writeByte(2);  | 
 | 
                    pixel = (absBuf[0] << 4) | absBuf[1];  | 
 | 
                    stream.writeByte(pixel);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                }  | 
 | 
                absVal = -1;  | 
 | 
 | 
 | 
                if (nextVal2 == runVal2){ | 
 | 
                      | 
 | 
                    runCount+=2;  | 
 | 
                    if(runCount == 256){ | 
 | 
                        stream.writeByte(runCount-1);  | 
 | 
                        pixel = ( runVal1 << 4) | runVal2;  | 
 | 
                        stream.writeByte(pixel);  | 
 | 
                        incCompImageSize(2);  | 
 | 
                        runCount =2;  | 
 | 
                        if(j< scanlineBytes - 1){ | 
 | 
                            runVal1 = runVal2;  | 
 | 
                            runVal2 = bipixels[++j];  | 
 | 
                        } else { | 
 | 
                            stream.writeByte(01);  | 
 | 
                            int r = runVal2 << 4 | 0;  | 
 | 
                            stream.writeByte(r);  | 
 | 
                            incCompImageSize(2);  | 
 | 
                            runCount = -1;  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // odd runlength and the run ends here  | 
 | 
                    // runCount wont be > 254 since 256/255 case will  | 
 | 
                      | 
 | 
                    runCount++;  | 
 | 
                    pixel = ( runVal1 << 4) | runVal2;  | 
 | 
                    stream.writeByte(runCount);  | 
 | 
                    stream.writeByte(pixel);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    runCount = 2;  | 
 | 
                    runVal1 = nextVal2;  | 
 | 
                      | 
 | 
                    if (j < scanlineBytes -1){ | 
 | 
                        runVal2 = bipixels[++j];  | 
 | 
                    }else { | 
 | 
                        stream.writeByte(01);  | 
 | 
                        int r = nextVal2 << 4 | 0;  | 
 | 
                        stream.writeByte(r);  | 
 | 
                        incCompImageSize(2);  | 
 | 
                        runCount = -1;  | 
 | 
                    }  | 
 | 
 | 
 | 
                }  | 
 | 
            } else{ | 
 | 
                  | 
 | 
                if (runCount > 2){ | 
 | 
                    pixel = ( runVal1 << 4) | runVal2;  | 
 | 
                    stream.writeByte(runCount);  | 
 | 
                    stream.writeByte(pixel);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                } else if (absVal < 0){  | 
 | 
                    absBuf[++absVal] = runVal1;  | 
 | 
                    absBuf[++absVal] = runVal2;  | 
 | 
                    absBuf[++absVal] = nextVal1;  | 
 | 
                    absBuf[++absVal] = nextVal2;  | 
 | 
                } else if (absVal < 253){  | 
 | 
                    absBuf[++absVal] = nextVal1;  | 
 | 
                    absBuf[++absVal] = nextVal2;  | 
 | 
                } else { | 
 | 
                    stream.writeByte(0);  | 
 | 
                    stream.writeByte(absVal+1);  | 
 | 
                    incCompImageSize(2);  | 
 | 
                    for(int a=0; a<absVal;a+=2){ | 
 | 
                        pixel = (absBuf[a] << 4) | absBuf[a+1];  | 
 | 
                        stream.writeByte((byte)pixel);  | 
 | 
                        incCompImageSize(1);  | 
 | 
                    }  | 
 | 
                    // Padding for word align  | 
 | 
                      | 
 | 
                    stream.writeByte(0);  | 
 | 
                    incCompImageSize(1);  | 
 | 
                    absVal = -1;  | 
 | 
                }  | 
 | 
 | 
 | 
                runVal1 = nextVal1;  | 
 | 
                runVal2 = nextVal2;  | 
 | 
                runCount = 2;  | 
 | 
            }  | 
 | 
              | 
 | 
            if (j >= scanlineBytes-2 ) { | 
 | 
                if (absVal == -1 && runCount >= 2){ | 
 | 
                    if (j == scanlineBytes-2){ | 
 | 
                        if(bipixels[++j] == runVal1){ | 
 | 
                            runCount++;  | 
 | 
                            pixel = ( runVal1 << 4) | runVal2;  | 
 | 
                            stream.writeByte(runCount);  | 
 | 
                            stream.writeByte(pixel);  | 
 | 
                            incCompImageSize(2);  | 
 | 
                        } else { | 
 | 
                            pixel = ( runVal1 << 4) | runVal2;  | 
 | 
                            stream.writeByte(runCount);  | 
 | 
                            stream.writeByte(pixel);  | 
 | 
                            stream.writeByte(01);  | 
 | 
                            pixel =  bipixels[j]<<4 |0;  | 
 | 
                            stream.writeByte(pixel);  | 
 | 
                            int n = bipixels[j]<<4|0;  | 
 | 
                            incCompImageSize(4);  | 
 | 
                        }  | 
 | 
                    } else { | 
 | 
                        stream.writeByte(runCount);  | 
 | 
                        pixel =( runVal1 << 4) | runVal2 ;  | 
 | 
                        stream.writeByte(pixel);  | 
 | 
                        incCompImageSize(2);  | 
 | 
                    }  | 
 | 
                } else if(absVal > -1){ | 
 | 
                    if (j == scanlineBytes-2){ | 
 | 
                        absBuf[++absVal] = bipixels[++j];  | 
 | 
                    }  | 
 | 
                    if (absVal >=2){ | 
 | 
                        stream.writeByte(0);  | 
 | 
                        stream.writeByte(absVal+1);  | 
 | 
                        incCompImageSize(2);  | 
 | 
                        for(int a=0; a<absVal;a+=2){ | 
 | 
                            pixel = (absBuf[a] << 4) | absBuf[a+1];  | 
 | 
                            stream.writeByte((byte)pixel);  | 
 | 
                            incCompImageSize(1);  | 
 | 
                        }  | 
 | 
                        if(!(isEven(absVal+1))){ | 
 | 
                            q = absBuf[absVal] << 4|0;  | 
 | 
                            stream.writeByte(q);  | 
 | 
                            incCompImageSize(1);  | 
 | 
                        }  | 
 | 
 | 
 | 
                          | 
 | 
                        if ( !isEven((int)Math.ceil((absVal+1)/2)) ) { | 
 | 
                            stream.writeByte(0);  | 
 | 
                            incCompImageSize(1);  | 
 | 
                        }  | 
 | 
 | 
 | 
                    } else { | 
 | 
                        switch (absVal){ | 
 | 
                        case 0:  | 
 | 
                            stream.writeByte(1);  | 
 | 
                            int n = absBuf[0]<<4 | 0;  | 
 | 
                            stream.writeByte(n);  | 
 | 
                            incCompImageSize(2);  | 
 | 
                            break;  | 
 | 
                        case 1:  | 
 | 
                            stream.writeByte(2);  | 
 | 
                            pixel = (absBuf[0] << 4) | absBuf[1];  | 
 | 
                            stream.writeByte(pixel);  | 
 | 
                            incCompImageSize(2);  | 
 | 
                            break;  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
 | 
 | 
                }  | 
 | 
                stream.writeByte(0);  | 
 | 
                stream.writeByte(0);  | 
 | 
                incCompImageSize(2);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    private synchronized void incCompImageSize(int value){ | 
 | 
        compImageSize = compImageSize + value;  | 
 | 
    }  | 
 | 
 | 
 | 
    private boolean isEven(int number) { | 
 | 
        return (number%2 == 0 ? true : false);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void writeFileHeader(int fileSize, int offset) throws IOException { | 
 | 
          | 
 | 
        stream.writeByte('B'); | 
 | 
        stream.writeByte('M'); | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(fileSize);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(0);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(offset);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    private void writeInfoHeader(int headerSize,  | 
 | 
                                 int bitsPerPixel) throws IOException { | 
 | 
          | 
 | 
        stream.writeInt(headerSize);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(w);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeInt(isTopDown ? -h : h);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeShort(1);  | 
 | 
 | 
 | 
          | 
 | 
        stream.writeShort(bitsPerPixel);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void writeSize(int dword, int offset) throws IOException { | 
 | 
        stream.skipBytes(offset);  | 
 | 
        stream.writeInt(dword);  | 
 | 
    }  | 
 | 
 | 
 | 
    public void reset() { | 
 | 
        super.reset();  | 
 | 
        stream = null;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void writeEmbedded(IIOImage image,  | 
 | 
                               ImageWriteParam bmpParam) throws IOException { | 
 | 
        String format =  | 
 | 
            compressionType == BI_JPEG ? "jpeg" : "png";  | 
 | 
        Iterator iterator = ImageIO.getImageWritersByFormatName(format);  | 
 | 
        ImageWriter writer = null;  | 
 | 
        if (iterator.hasNext())  | 
 | 
            writer = (ImageWriter)iterator.next();  | 
 | 
        if (writer != null) { | 
 | 
            if (embedded_stream == null) { | 
 | 
                throw new RuntimeException("No stream for writing embedded image!"); | 
 | 
            }  | 
 | 
 | 
 | 
            writer.addIIOWriteProgressListener(new IIOWriteProgressAdapter() { | 
 | 
                    public void imageProgress(ImageWriter source, float percentageDone) { | 
 | 
                        processImageProgress(percentageDone);  | 
 | 
                    }  | 
 | 
                });  | 
 | 
 | 
 | 
            writer.addIIOWriteWarningListener(new IIOWriteWarningListener() { | 
 | 
                    public void warningOccurred(ImageWriter source, int imageIndex, String warning) { | 
 | 
                        processWarningOccurred(imageIndex, warning);  | 
 | 
                    }  | 
 | 
                });  | 
 | 
 | 
 | 
            writer.setOutput(ImageIO.createImageOutputStream(embedded_stream));  | 
 | 
            ImageWriteParam param = writer.getDefaultWriteParam();  | 
 | 
              | 
 | 
            param.setDestinationOffset(bmpParam.getDestinationOffset());  | 
 | 
            param.setSourceBands(bmpParam.getSourceBands());  | 
 | 
            param.setSourceRegion(bmpParam.getSourceRegion());  | 
 | 
            param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),  | 
 | 
                                       bmpParam.getSourceYSubsampling(),  | 
 | 
                                       bmpParam.getSubsamplingXOffset(),  | 
 | 
                                       bmpParam.getSubsamplingYOffset());  | 
 | 
            writer.write(null, image, param);  | 
 | 
        } else  | 
 | 
            throw new RuntimeException(I18N.getString("BMPImageWrite5") + " " + format); | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
    private int firstLowBit(int num) { | 
 | 
        int count = 0;  | 
 | 
        while ((num & 1) == 0) { | 
 | 
            count++;  | 
 | 
            num >>>= 1;  | 
 | 
        }  | 
 | 
        return count;  | 
 | 
    }  | 
 | 
 | 
 | 
    private class IIOWriteProgressAdapter implements IIOWriteProgressListener { | 
 | 
 | 
 | 
        public void imageComplete(ImageWriter source) { | 
 | 
        }  | 
 | 
 | 
 | 
        public void imageProgress(ImageWriter source, float percentageDone) { | 
 | 
        }  | 
 | 
 | 
 | 
        public void imageStarted(ImageWriter source, int imageIndex) { | 
 | 
        }  | 
 | 
 | 
 | 
        public void thumbnailComplete(ImageWriter source) { | 
 | 
        }  | 
 | 
 | 
 | 
        public void thumbnailProgress(ImageWriter source, float percentageDone) { | 
 | 
        }  | 
 | 
 | 
 | 
        public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) { | 
 | 
        }  | 
 | 
 | 
 | 
        public void writeAborted(ImageWriter source) { | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected int getPreferredCompressionType(ColorModel cm, SampleModel sm) { | 
 | 
        ImageTypeSpecifier imageType = new ImageTypeSpecifier(cm, sm);  | 
 | 
        return getPreferredCompressionType(imageType);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int getPreferredCompressionType(ImageTypeSpecifier imageType) { | 
 | 
        if (imageType.getBufferedImageType() == BufferedImage.TYPE_USHORT_565_RGB) { | 
 | 
            return  BI_BITFIELDS;  | 
 | 
        }  | 
 | 
        return BI_RGB;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected boolean canEncodeImage(int compression, ColorModel cm, SampleModel sm) { | 
 | 
        ImageTypeSpecifier imgType = new ImageTypeSpecifier(cm, sm);  | 
 | 
        return canEncodeImage(compression, imgType);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected boolean canEncodeImage(int compression, ImageTypeSpecifier imgType) { | 
 | 
        ImageWriterSpi spi = this.getOriginatingProvider();  | 
 | 
        if (!spi.canEncodeImage(imgType)) { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        int biType = imgType.getBufferedImageType();  | 
 | 
        int bpp = imgType.getColorModel().getPixelSize();  | 
 | 
        if (compressionType == BI_RLE4 && bpp != 4) { | 
 | 
              | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        if (compressionType == BI_RLE8 && bpp != 8) { | 
 | 
              | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        if (bpp == 16) { | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            boolean canUseRGB = false;  | 
 | 
            boolean canUseBITFIELDS = false;  | 
 | 
 | 
 | 
            SampleModel sm = imgType.getSampleModel();  | 
 | 
            if (sm instanceof SinglePixelPackedSampleModel) { | 
 | 
                int[] sizes =  | 
 | 
                    ((SinglePixelPackedSampleModel)sm).getSampleSize();  | 
 | 
 | 
 | 
                canUseRGB = true;  | 
 | 
                canUseBITFIELDS = true;  | 
 | 
                for (int i = 0; i < sizes.length; i++) { | 
 | 
                    canUseRGB       &=  (sizes[i] == 5);  | 
 | 
                    canUseBITFIELDS &= ((sizes[i] == 5) ||  | 
 | 
                                        (i == 1 && sizes[i] == 6));  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return (((compressionType == BI_RGB) && canUseRGB) ||  | 
 | 
                    ((compressionType == BI_BITFIELDS) && canUseBITFIELDS));  | 
 | 
        }  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void writeMaskToPalette(int mask, int i,  | 
 | 
                                      byte[] r, byte[]g, byte[] b, byte[]a) { | 
 | 
        b[i] = (byte)(0xff & (mask >> 24));  | 
 | 
        g[i] = (byte)(0xff & (mask >> 16));  | 
 | 
        r[i] = (byte)(0xff & (mask >> 8));  | 
 | 
        a[i] = (byte)(0xff & mask);  | 
 | 
    }  | 
 | 
 | 
 | 
    private int roundBpp(int x) { | 
 | 
        if (x <= 8) { | 
 | 
            return 8;  | 
 | 
        } else if (x <= 16) { | 
 | 
            return 16;  | 
 | 
        } if (x <= 24) { | 
 | 
            return 24;  | 
 | 
        } else { | 
 | 
            return 32;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |