1532 lines · 57.2 KB · Java
RawDownload
/* * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.bmp; import java.awt.Point;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.WritableRaster;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.IIOException;import javax.imageio.ImageIO;import javax.imageio.ImageTypeSpecifier;import javax.imageio.ImageWriteParam;import javax.imageio.ImageWriter;import javax.imageio.metadata.IIOMetadata;import javax.imageio.metadata.IIOMetadataNode;import javax.imageio.metadata.IIOMetadataFormatImpl;import javax.imageio.metadata.IIOInvalidTreeException;import javax.imageio.spi.ImageWriterSpi;import javax.imageio.stream.ImageOutputStream;import javax.imageio.event.IIOWriteProgressListener;import javax.imageio.event.IIOWriteWarningListener; import org.w3c.dom.Node;import org.w3c.dom.NodeList; import javax.imageio.plugins.bmp.BMPImageWriteParam;import com.sun.imageio.plugins.common.ImageUtil;import com.sun.imageio.plugins.common.I18N; /** * The Java Image IO plugin writer for encoding a binary RenderedImage into * a BMP format. * * The encoding process may clip, subsample using the parameters * specified in the <code>ImageWriteParam</code>. * * @see javax.imageio.plugins.bmp.BMPImageWriteParam */public class BMPImageWriter extends ImageWriter implements BMPConstants {    /** The output stream to write into */    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;     /** Constructs <code>BMPImageWriter</code> based on the provided     *  <code>ImageWriterSpi</code>.     */    public BMPImageWriter(ImageWriterSpi originator) {        super(originator);    }     public void setOutput(Object output) {        super.setOutput(output); // validates 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 = getCompressionType(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;         // Default is using 24 bits per pixel.        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();         // cache the data type;        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);         // Raw data can only handle bytes, everything greater must be ASCII.        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                //  with raster directly and must use writePixels()                bgrOrder = false;            } else {                // we can work with raster directly only in case of                // BGR component order.                // In any other case we must use writePixels()                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.                // We should use writePixels() for such images.                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()            // Please note that getPixels() provides rgb bands order.            bandOffsets = new int[numBands];            for (int i = 0; i < numBands; i++)                bandOffsets[i] = i;        }         noTransform &= bgrOrder;         int sampleSize[] = sampleModel.getSampleSize();         //XXX: check more         // Number of bytes that a scanline for the image written out will have.        int destScanlineBytes = w * numBands;         switch(bmpParam.getCompressionMode()) {        case ImageWriteParam.MODE_EXPLICIT:            compressionType = getCompressionType(bmpParam.getCompressionType());            break;        case ImageWriteParam.MODE_COPY_FROM_METADATA:            compressionType = bmpImageMetadata.compression;            break;        case ImageWriteParam.MODE_DEFAULT:            compressionType = getPreferredCompressionType(colorModel, sampleModel);            break;        default:            // ImageWriteParam.MODE_DISABLED:            compressionType = BI_RGB;        }         if (!canEncodeImage(compressionType, colorModel, sampleModel)) {            throw new IOException("Image can not be encoded with compression type "                                  + compressionTypeNames[compressionType]);        }         byte r[] = null, g[] = null, b[] = null, a[] = null;         if (compressionType == BMPConstants.BI_BITFIELDS) {            bitsPerPixel =                DataBuffer.getDataTypeSize(sampleModel.getDataType());             if (bitsPerPixel != 16 && bitsPerPixel != 32) {                // we should use 32bpp images in case of BI_BITFIELD                // compression to avoid color conversion artefacts                bitsPerPixel = 32;                 // Setting this flag to false ensures that generic                // writePixels() will be used to store image data                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) {                /* NB: canEncodeImage() ensures we have image of                 * either USHORT_565_RGB or USHORT_555_RGB type here.                 * Technically, it should work for other direct color                 * model types but it might be non compatible with win98                 * and friends.                 */                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                    // an exception related to unsupported image format                    throw new IOException("Image can not be encoded with " +                                          "compression type " +                                          compressionTypeNames[compressionType]);                }            }            writeMaskToPalette(rmask, 0, r, g, b, a);            writeMaskToPalette(gmask, 1, r, g, b, a);            writeMaskToPalette(bmask, 2, r, g, b, a);             if (!noTransform) {                // prepare info for writePixels procedure                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 { // handle BI_RGB compression            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                    // 24 bit image.                    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 {                // Grey scale images                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)                    {                        /* NB: the actual pixel size can be smaller than                         * size of used DataBuffer element.                         * For example: in case of TYPE_INT_RGB actual pixel                         * size is 24 bits, but size of DataBuffere element                         * is 32 bits                         */                        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;                    }                }            }        }         // actual writing of image data        int fileSize = 0;        int offset = 0;        int headerSize = 0;        int imageSize = 0;        int xPelsPerMeter = 0;        int yPelsPerMeter = 0;        int colorsUsed = 0;        int colorsImportant = paletteEntries;         // Calculate padding for each scanline        int padding = destScanlineBytes % 4;        if (padding != 0) {            padding = 4 - padding;        }          // FileHeader is 14 bytes, BitmapHeader is 40 bytes,        // add palette size and that is where the data will begin        offset = 54 + paletteEntries * 4; 

Lines 1–500 of 1532.