1171 lines · 39.4 KB · Java
RawDownload
/* * Copyright (c) 2000, 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.png; import java.awt.Rectangle;import java.awt.image.ColorModel;import java.awt.image.IndexColorModel;import java.awt.image.Raster;import java.awt.image.WritableRaster;import java.awt.image.RenderedImage;import java.awt.image.SampleModel;import java.io.ByteArrayOutputStream;import java.io.DataOutput;import java.io.IOException;import java.io.OutputStream;import java.util.Iterator;import java.util.Locale;import java.util.zip.Deflater;import java.util.zip.DeflaterOutputStream;import javax.imageio.IIOException;import javax.imageio.IIOImage;import javax.imageio.ImageTypeSpecifier;import javax.imageio.ImageWriteParam;import javax.imageio.ImageWriter;import javax.imageio.metadata.IIOMetadata;import javax.imageio.metadata.IIOMetadata;import javax.imageio.spi.ImageWriterSpi;import javax.imageio.stream.ImageOutputStream;import javax.imageio.stream.ImageOutputStreamImpl; class CRC {     private static int[] crcTable = new int[256];    private int crc = 0xffffffff;     static {        // Initialize CRC table        for (int n = 0; n < 256; n++) {            int c = n;            for (int k = 0; k < 8; k++) {                if ((c & 1) == 1) {                    c = 0xedb88320 ^ (c >>> 1);                } else {                    c >>>= 1;                }                 crcTable[n] = c;            }        }    }     public CRC() {}     public void reset() {        crc = 0xffffffff;    }     public void update(byte[] data, int off, int len) {        for (int n = 0; n < len; n++) {            crc = crcTable[(crc ^ data[off + n]) & 0xff] ^ (crc >>> 8);        }    }     public void update(int data) {        crc = crcTable[(crc ^ data) & 0xff] ^ (crc >>> 8);    }     public int getValue() {        return crc ^ 0xffffffff;    }}  final class ChunkStream extends ImageOutputStreamImpl {     private ImageOutputStream stream;    private long startPos;    private CRC crc = new CRC();     public ChunkStream(int type, ImageOutputStream stream) throws IOException {        this.stream = stream;        this.startPos = stream.getStreamPosition();         stream.writeInt(-1); // length, will backpatch        writeInt(type);    }     public int read() throws IOException {        throw new RuntimeException("Method not available");    }     public int read(byte[] b, int off, int len) throws IOException {        throw new RuntimeException("Method not available");    }     public void write(byte[] b, int off, int len) throws IOException {        crc.update(b, off, len);        stream.write(b, off, len);    }     public void write(int b) throws IOException {        crc.update(b);        stream.write(b);    }     public void finish() throws IOException {        // Write CRC        stream.writeInt(crc.getValue());         // Write length        long pos = stream.getStreamPosition();        stream.seek(startPos);        stream.writeInt((int)(pos - startPos) - 12);         // Return to end of chunk and flush to minimize buffering        stream.seek(pos);        stream.flushBefore(pos);    }     protected void finalize() throws Throwable {        // Empty finalizer (for improved performance; no need to call        // super.finalize() in this case)    }} // Compress output and write as a series of 'IDAT' chunks of// fixed length.final class IDATOutputStream extends ImageOutputStreamImpl {     private static byte[] chunkType = {        (byte)'I', (byte)'D', (byte)'A', (byte)'T'    };     private ImageOutputStream stream;    private int chunkLength;    private long startPos;    private CRC crc = new CRC();     Deflater def = new Deflater(Deflater.BEST_COMPRESSION);    byte[] buf = new byte[512];     private int bytesRemaining;     public IDATOutputStream(ImageOutputStream stream, int chunkLength)        throws IOException {        this.stream = stream;        this.chunkLength = chunkLength;        startChunk();    }     private void startChunk() throws IOException {        crc.reset();        this.startPos = stream.getStreamPosition();        stream.writeInt(-1); // length, will backpatch         crc.update(chunkType, 0, 4);        stream.write(chunkType, 0, 4);         this.bytesRemaining = chunkLength;    }     private void finishChunk() throws IOException {        // Write CRC        stream.writeInt(crc.getValue());         // Write length        long pos = stream.getStreamPosition();        stream.seek(startPos);        stream.writeInt((int)(pos - startPos) - 12);         // Return to end of chunk and flush to minimize buffering        stream.seek(pos);        stream.flushBefore(pos);    }     public int read() throws IOException {        throw new RuntimeException("Method not available");    }     public int read(byte[] b, int off, int len) throws IOException {        throw new RuntimeException("Method not available");    }     public void write(byte[] b, int off, int len) throws IOException {        if (len == 0) {            return;        }         if (!def.finished()) {            def.setInput(b, off, len);            while (!def.needsInput()) {                deflate();            }        }    }     public void deflate() throws IOException {        int len = def.deflate(buf, 0, buf.length);        int off = 0;         while (len > 0) {            if (bytesRemaining == 0) {                finishChunk();                startChunk();            }             int nbytes = Math.min(len, bytesRemaining);            crc.update(buf, off, nbytes);            stream.write(buf, off, nbytes);             off += nbytes;            len -= nbytes;            bytesRemaining -= nbytes;        }    }     public void write(int b) throws IOException {        byte[] wbuf = new byte[1];        wbuf[0] = (byte)b;        write(wbuf, 0, 1);    }     public void finish() throws IOException {        try {            if (!def.finished()) {                def.finish();                while (!def.finished()) {                    deflate();                }            }            finishChunk();        } finally {            def.end();        }    }     protected void finalize() throws Throwable {        // Empty finalizer (for improved performance; no need to call        // super.finalize() in this case)    }}  class PNGImageWriteParam extends ImageWriteParam {     public PNGImageWriteParam(Locale locale) {        super();        this.canWriteProgressive = true;        this.locale = locale;    }} /** */public class PNGImageWriter extends ImageWriter {     ImageOutputStream stream = null;     PNGMetadata metadata = null;     // Factors from the ImageWriteParam    int sourceXOffset = 0;    int sourceYOffset = 0;    int sourceWidth = 0;    int sourceHeight = 0;    int[] sourceBands = null;    int periodX = 1;    int periodY = 1;     int numBands;    int bpp;     RowFilter rowFilter = new RowFilter();    byte[] prevRow = null;    byte[] currRow = null;    byte[][] filteredRows = null;     // Per-band scaling tables    //    // After the first call to initializeScaleTables, either scale and scale0    // will be valid, or scaleh and scalel will be valid, but not both.    //    // The tables will be designed for use with a set of input but depths    // given by sampleSize, and an output bit depth given by scalingBitDepth.    //    int[] sampleSize = null; // Sample size per band, in bits    int scalingBitDepth = -1; // Output bit depth of the scaling tables     // Tables for 1, 2, 4, or 8 bit output    byte[][] scale = null; // 8 bit table    byte[] scale0 = null; // equivalent to scale[0]     // Tables for 16 bit output    byte[][] scaleh = null; // High bytes of output    byte[][] scalel = null; // Low bytes of output     int totalPixels; // Total number of pixels to be written by write_IDAT    int pixelsDone; // Running count of pixels written by write_IDAT     public PNGImageWriter(ImageWriterSpi originatingProvider) {        super(originatingProvider);    }     public void setOutput(Object output) {        super.setOutput(output);        if (output != null) {            if (!(output instanceof ImageOutputStream)) {                throw new IllegalArgumentException("output not an ImageOutputStream!");            }            this.stream = (ImageOutputStream)output;        } else {            this.stream = null;        }    }     private static int[] allowedProgressivePasses = { 1, 7 };     public ImageWriteParam getDefaultWriteParam() {        return new PNGImageWriteParam(getLocale());    }     public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {        return null;    }     public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,                                               ImageWriteParam param) {        PNGMetadata m = new PNGMetadata();        m.initialize(imageType, imageType.getSampleModel().getNumBands());        return m;    }     public IIOMetadata convertStreamMetadata(IIOMetadata inData,                                             ImageWriteParam param) {        return null;    }     public IIOMetadata convertImageMetadata(IIOMetadata inData,                                            ImageTypeSpecifier imageType,                                            ImageWriteParam param) {        // TODO - deal with imageType        if (inData instanceof PNGMetadata) {            return (PNGMetadata)((PNGMetadata)inData).clone();        } else {            return new PNGMetadata(inData);        }    }     private void write_magic() throws IOException {        // Write signature        byte[] magic = { (byte)137, 80, 78, 71, 13, 10, 26, 10 };        stream.write(magic);    }     private void write_IHDR() throws IOException {        // Write IHDR chunk        ChunkStream cs = new ChunkStream(PNGImageReader.IHDR_TYPE, stream);        cs.writeInt(metadata.IHDR_width);        cs.writeInt(metadata.IHDR_height);        cs.writeByte(metadata.IHDR_bitDepth);        cs.writeByte(metadata.IHDR_colorType);        if (metadata.IHDR_compressionMethod != 0) {            throw new IIOException("Only compression method 0 is defined in PNG 1.1");        }        cs.writeByte(metadata.IHDR_compressionMethod);        if (metadata.IHDR_filterMethod != 0) {            throw new IIOException("Only filter method 0 is defined in PNG 1.1");        }        cs.writeByte(metadata.IHDR_filterMethod);        if (metadata.IHDR_interlaceMethod < 0 ||            metadata.IHDR_interlaceMethod > 1) {            throw new IIOException("Only interlace methods 0 (node) and 1 (adam7) are defined in PNG 1.1");        }        cs.writeByte(metadata.IHDR_interlaceMethod);        cs.finish();    }     private void write_cHRM() throws IOException {        if (metadata.cHRM_present) {            ChunkStream cs = new ChunkStream(PNGImageReader.cHRM_TYPE, stream);            cs.writeInt(metadata.cHRM_whitePointX);            cs.writeInt(metadata.cHRM_whitePointY);            cs.writeInt(metadata.cHRM_redX);            cs.writeInt(metadata.cHRM_redY);            cs.writeInt(metadata.cHRM_greenX);            cs.writeInt(metadata.cHRM_greenY);            cs.writeInt(metadata.cHRM_blueX);            cs.writeInt(metadata.cHRM_blueY);            cs.finish();        }    }     private void write_gAMA() throws IOException {        if (metadata.gAMA_present) {            ChunkStream cs = new ChunkStream(PNGImageReader.gAMA_TYPE, stream);            cs.writeInt(metadata.gAMA_gamma);            cs.finish();        }    }     private void write_iCCP() throws IOException {        if (metadata.iCCP_present) {            ChunkStream cs = new ChunkStream(PNGImageReader.iCCP_TYPE, stream);            cs.writeBytes(metadata.iCCP_profileName);            cs.writeByte(0); // null terminator             cs.writeByte(metadata.iCCP_compressionMethod);            cs.write(metadata.iCCP_compressedProfile);            cs.finish();        }    }     private void write_sBIT() throws IOException {        if (metadata.sBIT_present) {            ChunkStream cs = new ChunkStream(PNGImageReader.sBIT_TYPE, stream);            int colorType = metadata.IHDR_colorType;            if (metadata.sBIT_colorType != colorType) {                processWarningOccurred(0,"sBIT metadata has wrong color type.\n" +"The chunk will not be written.");                return;            }             if (colorType == PNGImageReader.PNG_COLOR_GRAY ||                colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {                cs.writeByte(metadata.sBIT_grayBits);            } else if (colorType == PNGImageReader.PNG_COLOR_RGB ||                       colorType == PNGImageReader.PNG_COLOR_PALETTE ||                       colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {                cs.writeByte(metadata.sBIT_redBits);                cs.writeByte(metadata.sBIT_greenBits);                cs.writeByte(metadata.sBIT_blueBits);            }             if (colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA ||                colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {                cs.writeByte(metadata.sBIT_alphaBits);            }            cs.finish();        }    }     private void write_sRGB() throws IOException {        if (metadata.sRGB_present) {            ChunkStream cs = new ChunkStream(PNGImageReader.sRGB_TYPE, stream);            cs.writeByte(metadata.sRGB_renderingIntent);            cs.finish();        }    }     private void write_PLTE() throws IOException {        if (metadata.PLTE_present) {            if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY ||              metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {                // PLTE cannot occur in a gray image                 processWarningOccurred(0,"A PLTE chunk may not appear in a gray or gray alpha image.\n" +"The chunk will not be written");                return;            }             ChunkStream cs = new ChunkStream(PNGImageReader.PLTE_TYPE, stream);             int numEntries = metadata.PLTE_red.length;            byte[] palette = new byte[numEntries*3];            int index = 0;            for (int i = 0; i < numEntries; i++) {                palette[index++] = metadata.PLTE_red[i];                palette[index++] = metadata.PLTE_green[i];                palette[index++] = metadata.PLTE_blue[i];            }             cs.write(palette);

Lines 1–500 of 1171.