1317 lines · 46.5 KB · Java
RawDownload
/* * Copyright (c) 2005, 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.gif; import java.awt.Dimension;import java.awt.Rectangle;import java.awt.image.ColorModel;import java.awt.image.ComponentSampleModel;import java.awt.image.DataBufferByte;import java.awt.image.IndexColorModel;import java.awt.image.Raster;import java.awt.image.RenderedImage;import java.awt.image.SampleModel;import java.awt.image.WritableRaster;import java.io.IOException;import java.nio.ByteOrder;import java.util.Arrays;import java.util.Iterator;import java.util.Locale;import javax.imageio.IIOException;import javax.imageio.IIOImage;import javax.imageio.ImageTypeSpecifier;import javax.imageio.ImageWriteParam;import javax.imageio.ImageWriter;import javax.imageio.spi.ImageWriterSpi;import javax.imageio.metadata.IIOInvalidTreeException;import javax.imageio.metadata.IIOMetadata;import javax.imageio.metadata.IIOMetadataFormatImpl;import javax.imageio.metadata.IIOMetadataNode;import javax.imageio.stream.ImageOutputStream;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import com.sun.imageio.plugins.common.LZWCompressor;import com.sun.imageio.plugins.common.PaletteBuilder;import sun.awt.image.ByteComponentRaster; public class GIFImageWriter extends ImageWriter {    private static final boolean DEBUG = false; // XXX false for release!     static final String STANDARD_METADATA_NAME =    IIOMetadataFormatImpl.standardMetadataFormatName;     static final String STREAM_METADATA_NAME =    GIFWritableStreamMetadata.NATIVE_FORMAT_NAME;     static final String IMAGE_METADATA_NAME =    GIFWritableImageMetadata.NATIVE_FORMAT_NAME;     /**     * The <code>output</code> case to an <code>ImageOutputStream</code>.     */    private ImageOutputStream stream = null;     /**     * Whether a sequence is being written.     */    private boolean isWritingSequence = false;     /**     * Whether the header has been written.     */    private boolean wroteSequenceHeader = false;     /**     * The stream metadata of a sequence.     */    private GIFWritableStreamMetadata theStreamMetadata = null;     /**     * The index of the image being written.     */    private int imageIndex = 0;     /**     * The number of bits represented by the value which should be a     * legal length for a color table.     */    private static int getNumBits(int value) throws IOException {        int numBits;        switch(value) {        case 2:            numBits = 1;            break;        case 4:            numBits = 2;            break;        case 8:            numBits = 3;            break;        case 16:            numBits = 4;            break;        case 32:            numBits = 5;            break;        case 64:            numBits = 6;            break;        case 128:            numBits = 7;            break;        case 256:            numBits = 8;            break;        default:            throw new IOException("Bad palette length: "+value+"!");        }         return numBits;    }     /**     * Compute the source region and destination dimensions taking any     * parameter settings into account.     */    private static void computeRegions(Rectangle sourceBounds,                                       Dimension destSize,                                       ImageWriteParam p) {        ImageWriteParam param;        int periodX = 1;        int periodY = 1;        if (p != null) {            int[] sourceBands = p.getSourceBands();            if (sourceBands != null &&                (sourceBands.length != 1 ||                 sourceBands[0] != 0)) {                throw new IllegalArgumentException("Cannot sub-band image!");            }             // Get source region and subsampling factors            Rectangle sourceRegion = p.getSourceRegion();            if (sourceRegion != null) {                // Clip to actual image bounds                sourceRegion = sourceRegion.intersection(sourceBounds);                sourceBounds.setBounds(sourceRegion);            }             // Adjust for subsampling offsets            int gridX = p.getSubsamplingXOffset();            int gridY = p.getSubsamplingYOffset();            sourceBounds.x += gridX;            sourceBounds.y += gridY;            sourceBounds.width -= gridX;            sourceBounds.height -= gridY;             // Get subsampling factors            periodX = p.getSourceXSubsampling();            periodY = p.getSourceYSubsampling();        }         // Compute output dimensions        destSize.setSize((sourceBounds.width + periodX - 1)/periodX,                         (sourceBounds.height + periodY - 1)/periodY);        if (destSize.width <= 0 || destSize.height <= 0) {            throw new IllegalArgumentException("Empty source region!");        }    }     /**     * Create a color table from the image ColorModel and SampleModel.     */    private static byte[] createColorTable(ColorModel colorModel,                                           SampleModel sampleModel)    {        byte[] colorTable;        if (colorModel instanceof IndexColorModel) {            IndexColorModel icm = (IndexColorModel)colorModel;            int mapSize = icm.getMapSize();             /**             * The GIF image format assumes that size of image palette             * is power of two. We will use closest larger power of two             * as size of color table.             */            int ctSize = getGifPaletteSize(mapSize);             byte[] reds = new byte[ctSize];            byte[] greens = new byte[ctSize];            byte[] blues = new byte[ctSize];            icm.getReds(reds);            icm.getGreens(greens);            icm.getBlues(blues);             /**             * fill tail of color component arrays by replica of first color             * in order to avoid appearance of extra colors in the color table             */            for (int i = mapSize; i < ctSize; i++) {                reds[i] = reds[0];                greens[i] = greens[0];                blues[i] = blues[0];            }             colorTable = new byte[3*ctSize];            int idx = 0;            for (int i = 0; i < ctSize; i++) {                colorTable[idx++] = reds[i];                colorTable[idx++] = greens[i];                colorTable[idx++] = blues[i];            }        } else if (sampleModel.getNumBands() == 1) {            // create gray-scaled color table for single-banded images            int numBits = sampleModel.getSampleSize()[0];            if (numBits > 8) {                numBits = 8;            }            int colorTableLength = 3*(1 << numBits);            colorTable = new byte[colorTableLength];            for (int i = 0; i < colorTableLength; i++) {                colorTable[i] = (byte)(i/3);            }        } else {            // We do not have enough information here            // to create well-fit color table for RGB image.            colorTable = null;        }         return colorTable;    }     /**     * According do GIF specification size of clor table (palette here)     * must be in range from 2 to 256 and must be power of 2.     */    private static int getGifPaletteSize(int x) {        if (x <= 2) {            return 2;        }        x = x - 1;        x = x | (x >> 1);        x = x | (x >> 2);        x = x | (x >> 4);        x = x | (x >> 8);        x = x | (x >> 16);        return x + 1;    }       public GIFImageWriter(GIFImageWriterSpi originatingProvider) {        super(originatingProvider);        if (DEBUG) {            System.err.println("GIF Writer is created");        }    }     public boolean canWriteSequence() {        return true;    }     /**     * Merges <code>inData</code> into <code>outData</code>. The supplied     * metadata format name is attempted first and failing that the standard     * metadata format name is attempted.     */    private void convertMetadata(String metadataFormatName,                                 IIOMetadata inData,                                 IIOMetadata outData) {        String formatName = null;         String nativeFormatName = inData.getNativeMetadataFormatName();        if (nativeFormatName != null &&            nativeFormatName.equals(metadataFormatName)) {            formatName = metadataFormatName;        } else {            String[] extraFormatNames = inData.getExtraMetadataFormatNames();             if (extraFormatNames != null) {                for (int i = 0; i < extraFormatNames.length; i++) {                    if (extraFormatNames[i].equals(metadataFormatName)) {                        formatName = metadataFormatName;                        break;                    }                }            }        }         if (formatName == null &&            inData.isStandardMetadataFormatSupported()) {            formatName = STANDARD_METADATA_NAME;        }         if (formatName != null) {            try {                Node root = inData.getAsTree(formatName);                outData.mergeTree(formatName, root);            } catch(IIOInvalidTreeException e) {                // ignore            }        }    }     /**     * Creates a default stream metadata object and merges in the     * supplied metadata.     */    public IIOMetadata convertStreamMetadata(IIOMetadata inData,                                             ImageWriteParam param) {        if (inData == null) {            throw new IllegalArgumentException("inData == null!");        }         IIOMetadata sm = getDefaultStreamMetadata(param);         convertMetadata(STREAM_METADATA_NAME, inData, sm);         return sm;    }     /**     * Creates a default image metadata object and merges in the     * supplied metadata.     */    public IIOMetadata convertImageMetadata(IIOMetadata inData,                                            ImageTypeSpecifier imageType,                                            ImageWriteParam param) {        if (inData == null) {            throw new IllegalArgumentException("inData == null!");        }        if (imageType == null) {            throw new IllegalArgumentException("imageType == null!");        }         GIFWritableImageMetadata im =            (GIFWritableImageMetadata)getDefaultImageMetadata(imageType,                                                              param);         // Save interlace flag state.         boolean isProgressive = im.interlaceFlag;         convertMetadata(IMAGE_METADATA_NAME, inData, im);         // Undo change to interlace flag if not MODE_COPY_FROM_METADATA.         if (param != null && param.canWriteProgressive() &&            param.getProgressiveMode() != param.MODE_COPY_FROM_METADATA) {            im.interlaceFlag = isProgressive;        }         return im;    }     public void endWriteSequence() throws IOException {        if (stream == null) {            throw new IllegalStateException("output == null!");        }        if (!isWritingSequence) {            throw new IllegalStateException("prepareWriteSequence() was not invoked!");        }        writeTrailer();        resetLocal();    }     public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,                                               ImageWriteParam param) {        GIFWritableImageMetadata imageMetadata =            new GIFWritableImageMetadata();         // Image dimensions         SampleModel sampleModel = imageType.getSampleModel();         Rectangle sourceBounds = new Rectangle(sampleModel.getWidth(),                                               sampleModel.getHeight());        Dimension destSize = new Dimension();        computeRegions(sourceBounds, destSize, param);         imageMetadata.imageWidth = destSize.width;        imageMetadata.imageHeight = destSize.height;         // Interlacing         if (param != null && param.canWriteProgressive() &&            param.getProgressiveMode() == ImageWriteParam.MODE_DISABLED) {            imageMetadata.interlaceFlag = false;        } else {            imageMetadata.interlaceFlag = true;        }         // Local color table         ColorModel colorModel = imageType.getColorModel();         imageMetadata.localColorTable =            createColorTable(colorModel, sampleModel);         // Transparency         if (colorModel instanceof IndexColorModel) {            int transparentIndex =                ((IndexColorModel)colorModel).getTransparentPixel();            if (transparentIndex != -1) {                imageMetadata.transparentColorFlag = true;                imageMetadata.transparentColorIndex = transparentIndex;            }        }         return imageMetadata;    }     public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {        GIFWritableStreamMetadata streamMetadata =            new GIFWritableStreamMetadata();        streamMetadata.version = "89a";        return streamMetadata;    }     public ImageWriteParam getDefaultWriteParam() {        return new GIFImageWriteParam(getLocale());    }     public void prepareWriteSequence(IIOMetadata streamMetadata)      throws IOException {         if (stream == null) {            throw new IllegalStateException("Output is not set.");        }         resetLocal();         // Save the possibly converted stream metadata as an instance variable.        if (streamMetadata == null) {            this.theStreamMetadata =                (GIFWritableStreamMetadata)getDefaultStreamMetadata(null);        } else {            this.theStreamMetadata = new GIFWritableStreamMetadata();            convertMetadata(STREAM_METADATA_NAME, streamMetadata,                            theStreamMetadata);        }         this.isWritingSequence = true;    }     public void reset() {        super.reset();        resetLocal();    }     /**     * Resets locally defined instance variables.     */    private void resetLocal() {        this.isWritingSequence = false;        this.wroteSequenceHeader = false;        this.theStreamMetadata = null;        this.imageIndex = 0;    }     public void setOutput(Object output) {        super.setOutput(output);        if (output != null) {            if (!(output instanceof ImageOutputStream)) {                throw new                    IllegalArgumentException("output is not an ImageOutputStream");            }            this.stream = (ImageOutputStream)output;            this.stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);        } else {            this.stream = null;        }    }     public void write(IIOMetadata sm,                      IIOImage iioimage,                      ImageWriteParam p) throws IOException {        if (stream == null) {            throw new IllegalStateException("output == null!");        }        if (iioimage == null) {            throw new IllegalArgumentException("iioimage == null!");        }        if (iioimage.hasRaster()) {            throw new UnsupportedOperationException("canWriteRasters() == false!");        }         resetLocal();

Lines 1–500 of 1317.