📄 gifimagewriter.java
字号:
/* * @(#)GIFImageWriter.java 1.13 05/11/17 * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */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;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!");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -