| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
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 { | 
 | 
          | 
 | 
        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);   | 
 | 
        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 { | 
 | 
          | 
 | 
        stream.writeInt(crc.getValue());  | 
 | 
 | 
 | 
          | 
 | 
        long pos = stream.getStreamPosition();  | 
 | 
        stream.seek(startPos);  | 
 | 
        stream.writeInt((int)(pos - startPos) - 12);  | 
 | 
 | 
 | 
          | 
 | 
        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  | 
 | 
 | 
 | 
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);   | 
 | 
 | 
 | 
        crc.update(chunkType, 0, 4);  | 
 | 
        stream.write(chunkType, 0, 4);  | 
 | 
 | 
 | 
        this.bytesRemaining = chunkLength;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void finishChunk() throws IOException { | 
 | 
          | 
 | 
        stream.writeInt(crc.getValue());  | 
 | 
 | 
 | 
          | 
 | 
        long pos = stream.getStreamPosition();  | 
 | 
        stream.seek(startPos);  | 
 | 
        stream.writeInt((int)(pos - startPos) - 12);  | 
 | 
 | 
 | 
          | 
 | 
        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;  | 
 | 
 | 
 | 
      | 
 | 
    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;   | 
 | 
    int scalingBitDepth = -1;   | 
 | 
 | 
 | 
    // Tables for 1, 2, 4, or 8 bit output  | 
 | 
    byte[][] scale = null;   | 
 | 
    byte[] scale0 = null;   | 
 | 
 | 
 | 
    // Tables for 16 bit output  | 
 | 
    byte[][] scaleh = null;   | 
 | 
    byte[][] scalel = null;   | 
 | 
 | 
 | 
    int totalPixels;   | 
 | 
    int pixelsDone;   | 
 | 
 | 
 | 
    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) { | 
 | 
          | 
 | 
        if (inData instanceof PNGMetadata) { | 
 | 
            return (PNGMetadata)((PNGMetadata)inData).clone();  | 
 | 
        } else { | 
 | 
            return new PNGMetadata(inData);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_magic() throws IOException { | 
 | 
          | 
 | 
        byte[] magic = { (byte)137, 80, 78, 71, 13, 10, 26, 10 }; | 
 | 
        stream.write(magic);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_IHDR() throws IOException { | 
 | 
          | 
 | 
        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);   | 
 | 
 | 
 | 
            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);  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_hIST() throws IOException, IIOException { | 
 | 
        if (metadata.hIST_present) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.hIST_TYPE, stream);  | 
 | 
 | 
 | 
            if (!metadata.PLTE_present) { | 
 | 
                throw new IIOException("hIST chunk without PLTE chunk!"); | 
 | 
            }  | 
 | 
 | 
 | 
            cs.writeChars(metadata.hIST_histogram,  | 
 | 
                          0, metadata.hIST_histogram.length);  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_tRNS() throws IOException, IIOException { | 
 | 
        if (metadata.tRNS_present) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.tRNS_TYPE, stream);  | 
 | 
            int colorType = metadata.IHDR_colorType;  | 
 | 
            int chunkType = metadata.tRNS_colorType;  | 
 | 
 | 
 | 
            // Special case: image is RGB and chunk is Gray  | 
 | 
              | 
 | 
            int chunkRed = metadata.tRNS_red;  | 
 | 
            int chunkGreen = metadata.tRNS_green;  | 
 | 
            int chunkBlue = metadata.tRNS_blue;  | 
 | 
            if (colorType == PNGImageReader.PNG_COLOR_RGB &&  | 
 | 
                chunkType == PNGImageReader.PNG_COLOR_GRAY) { | 
 | 
                chunkType = colorType;  | 
 | 
                chunkRed = chunkGreen = chunkBlue =  | 
 | 
                    metadata.tRNS_gray;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (chunkType != colorType) { | 
 | 
                processWarningOccurred(0,  | 
 | 
"tRNS metadata has incompatible color type.\n" +  | 
 | 
"The chunk will not be written.");  | 
 | 
                return;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (colorType == PNGImageReader.PNG_COLOR_PALETTE) { | 
 | 
                if (!metadata.PLTE_present) { | 
 | 
                    throw new IIOException("tRNS chunk without PLTE chunk!"); | 
 | 
                }  | 
 | 
                cs.write(metadata.tRNS_alpha);  | 
 | 
            } else if (colorType == PNGImageReader.PNG_COLOR_GRAY) { | 
 | 
                cs.writeShort(metadata.tRNS_gray);  | 
 | 
            } else if (colorType == PNGImageReader.PNG_COLOR_RGB) { | 
 | 
                cs.writeShort(chunkRed);  | 
 | 
                cs.writeShort(chunkGreen);  | 
 | 
                cs.writeShort(chunkBlue);  | 
 | 
            } else { | 
 | 
                throw new IIOException("tRNS chunk for color type 4 or 6!"); | 
 | 
            }  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_bKGD() throws IOException { | 
 | 
        if (metadata.bKGD_present) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.bKGD_TYPE, stream);  | 
 | 
            int colorType = metadata.IHDR_colorType & 0x3;  | 
 | 
            int chunkType = metadata.bKGD_colorType;  | 
 | 
 | 
 | 
            // Special case: image is RGB(A) and chunk is Gray  | 
 | 
              | 
 | 
            int chunkRed = metadata.bKGD_red;  | 
 | 
            int chunkGreen = metadata.bKGD_red;  | 
 | 
            int chunkBlue = metadata.bKGD_red;  | 
 | 
            if (colorType == PNGImageReader.PNG_COLOR_RGB &&  | 
 | 
                chunkType == PNGImageReader.PNG_COLOR_GRAY) { | 
 | 
                  | 
 | 
                chunkType = colorType;  | 
 | 
                chunkRed = chunkGreen = chunkBlue =  | 
 | 
                    metadata.bKGD_gray;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (chunkType != colorType) { | 
 | 
                processWarningOccurred(0,  | 
 | 
"bKGD metadata has incompatible color type.\n" +  | 
 | 
"The chunk will not be written.");  | 
 | 
                return;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (colorType == PNGImageReader.PNG_COLOR_PALETTE) { | 
 | 
                cs.writeByte(metadata.bKGD_index);  | 
 | 
            } else if (colorType == PNGImageReader.PNG_COLOR_GRAY ||  | 
 | 
                       colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { | 
 | 
                cs.writeShort(metadata.bKGD_gray);  | 
 | 
            } else { // colorType == PNGImageReader.PNG_COLOR_RGB || | 
 | 
                       | 
 | 
                cs.writeShort(chunkRed);  | 
 | 
                cs.writeShort(chunkGreen);  | 
 | 
                cs.writeShort(chunkBlue);  | 
 | 
            }  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_pHYs() throws IOException { | 
 | 
        if (metadata.pHYs_present) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.pHYs_TYPE, stream);  | 
 | 
            cs.writeInt(metadata.pHYs_pixelsPerUnitXAxis);  | 
 | 
            cs.writeInt(metadata.pHYs_pixelsPerUnitYAxis);  | 
 | 
            cs.writeByte(metadata.pHYs_unitSpecifier);  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_sPLT() throws IOException { | 
 | 
        if (metadata.sPLT_present) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.sPLT_TYPE, stream);  | 
 | 
 | 
 | 
            cs.writeBytes(metadata.sPLT_paletteName);  | 
 | 
            cs.writeByte(0);   | 
 | 
 | 
 | 
            cs.writeByte(metadata.sPLT_sampleDepth);  | 
 | 
            int numEntries = metadata.sPLT_red.length;  | 
 | 
 | 
 | 
            if (metadata.sPLT_sampleDepth == 8) { | 
 | 
                for (int i = 0; i < numEntries; i++) { | 
 | 
                    cs.writeByte(metadata.sPLT_red[i]);  | 
 | 
                    cs.writeByte(metadata.sPLT_green[i]);  | 
 | 
                    cs.writeByte(metadata.sPLT_blue[i]);  | 
 | 
                    cs.writeByte(metadata.sPLT_alpha[i]);  | 
 | 
                    cs.writeShort(metadata.sPLT_frequency[i]);  | 
 | 
                }  | 
 | 
            } else {  | 
 | 
                for (int i = 0; i < numEntries; i++) { | 
 | 
                    cs.writeShort(metadata.sPLT_red[i]);  | 
 | 
                    cs.writeShort(metadata.sPLT_green[i]);  | 
 | 
                    cs.writeShort(metadata.sPLT_blue[i]);  | 
 | 
                    cs.writeShort(metadata.sPLT_alpha[i]);  | 
 | 
                    cs.writeShort(metadata.sPLT_frequency[i]);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_tIME() throws IOException { | 
 | 
        if (metadata.tIME_present) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.tIME_TYPE, stream);  | 
 | 
            cs.writeShort(metadata.tIME_year);  | 
 | 
            cs.writeByte(metadata.tIME_month);  | 
 | 
            cs.writeByte(metadata.tIME_day);  | 
 | 
            cs.writeByte(metadata.tIME_hour);  | 
 | 
            cs.writeByte(metadata.tIME_minute);  | 
 | 
            cs.writeByte(metadata.tIME_second);  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_tEXt() throws IOException { | 
 | 
        Iterator keywordIter = metadata.tEXt_keyword.iterator();  | 
 | 
        Iterator textIter = metadata.tEXt_text.iterator();  | 
 | 
 | 
 | 
        while (keywordIter.hasNext()) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.tEXt_TYPE, stream);  | 
 | 
            String keyword = (String)keywordIter.next();  | 
 | 
            cs.writeBytes(keyword);  | 
 | 
            cs.writeByte(0);  | 
 | 
 | 
 | 
            String text = (String)textIter.next();  | 
 | 
            cs.writeBytes(text);  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private byte[] deflate(byte[] b) throws IOException { | 
 | 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  | 
 | 
        DeflaterOutputStream dos = new DeflaterOutputStream(baos);  | 
 | 
        dos.write(b);  | 
 | 
        dos.close();  | 
 | 
        return baos.toByteArray();  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_iTXt() throws IOException { | 
 | 
        Iterator<String> keywordIter = metadata.iTXt_keyword.iterator();  | 
 | 
        Iterator<Boolean> flagIter = metadata.iTXt_compressionFlag.iterator();  | 
 | 
        Iterator<Integer> methodIter = metadata.iTXt_compressionMethod.iterator();  | 
 | 
        Iterator<String> languageIter = metadata.iTXt_languageTag.iterator();  | 
 | 
        Iterator<String> translatedKeywordIter =  | 
 | 
            metadata.iTXt_translatedKeyword.iterator();  | 
 | 
        Iterator<String> textIter = metadata.iTXt_text.iterator();  | 
 | 
 | 
 | 
        while (keywordIter.hasNext()) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.iTXt_TYPE, stream);  | 
 | 
 | 
 | 
            cs.writeBytes(keywordIter.next());  | 
 | 
            cs.writeByte(0);  | 
 | 
 | 
 | 
            Boolean compressed = flagIter.next();  | 
 | 
            cs.writeByte(compressed ? 1 : 0);  | 
 | 
 | 
 | 
            cs.writeByte(methodIter.next().intValue());  | 
 | 
 | 
 | 
            cs.writeBytes(languageIter.next());  | 
 | 
            cs.writeByte(0);  | 
 | 
 | 
 | 
 | 
 | 
            cs.write(translatedKeywordIter.next().getBytes("UTF8")); | 
 | 
            cs.writeByte(0);  | 
 | 
 | 
 | 
            String text = textIter.next();  | 
 | 
            if (compressed) { | 
 | 
                cs.write(deflate(text.getBytes("UTF8"))); | 
 | 
            } else { | 
 | 
                cs.write(text.getBytes("UTF8")); | 
 | 
            }  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void write_zTXt() throws IOException { | 
 | 
        Iterator keywordIter = metadata.zTXt_keyword.iterator();  | 
 | 
        Iterator methodIter = metadata.zTXt_compressionMethod.iterator();  | 
 | 
        Iterator textIter = metadata.zTXt_text.iterator();  | 
 | 
 | 
 | 
        while (keywordIter.hasNext()) { | 
 | 
            ChunkStream cs = new ChunkStream(PNGImageReader.zTXt_TYPE, stream);  | 
 | 
            String keyword = (String)keywordIter.next();  | 
 | 
            cs.writeBytes(keyword);  | 
 | 
            cs.writeByte(0);  | 
 | 
 | 
 | 
            int compressionMethod = ((Integer)methodIter.next()).intValue();  | 
 | 
            cs.writeByte(compressionMethod);  | 
 | 
 | 
 | 
            String text = (String)textIter.next();  | 
 | 
            cs.write(deflate(text.getBytes("ISO-8859-1"))); | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void writeUnknownChunks() throws IOException { | 
 | 
        Iterator typeIter = metadata.unknownChunkType.iterator();  | 
 | 
        Iterator dataIter = metadata.unknownChunkData.iterator();  | 
 | 
 | 
 | 
        while (typeIter.hasNext() && dataIter.hasNext()) { | 
 | 
            String type = (String)typeIter.next();  | 
 | 
            ChunkStream cs = new ChunkStream(chunkType(type), stream);  | 
 | 
            byte[] data = (byte[])dataIter.next();  | 
 | 
            cs.write(data);  | 
 | 
            cs.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static int chunkType(String typeString) { | 
 | 
        char c0 = typeString.charAt(0);  | 
 | 
        char c1 = typeString.charAt(1);  | 
 | 
        char c2 = typeString.charAt(2);  | 
 | 
        char c3 = typeString.charAt(3);  | 
 | 
 | 
 | 
        int type = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;  | 
 | 
        return type;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void encodePass(ImageOutputStream os,  | 
 | 
                            RenderedImage image,  | 
 | 
                            int xOffset, int yOffset,  | 
 | 
                            int xSkip, int ySkip) throws IOException { | 
 | 
        int minX = sourceXOffset;  | 
 | 
        int minY = sourceYOffset;  | 
 | 
        int width = sourceWidth;  | 
 | 
        int height = sourceHeight;  | 
 | 
 | 
 | 
          | 
 | 
        xOffset *= periodX;  | 
 | 
        xSkip *= periodX;  | 
 | 
        yOffset *= periodY;  | 
 | 
        ySkip *= periodY;  | 
 | 
 | 
 | 
          | 
 | 
        int hpixels = (width - xOffset + xSkip - 1)/xSkip;  | 
 | 
        int vpixels = (height - yOffset + ySkip - 1)/ySkip;  | 
 | 
        if (hpixels == 0 || vpixels == 0) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        xOffset *= numBands;  | 
 | 
        xSkip *= numBands;  | 
 | 
 | 
 | 
          | 
 | 
        int samplesPerByte = 8/metadata.IHDR_bitDepth;  | 
 | 
        int numSamples = width*numBands;  | 
 | 
        int[] samples = new int[numSamples];  | 
 | 
 | 
 | 
        int bytesPerRow = hpixels*numBands;  | 
 | 
        if (metadata.IHDR_bitDepth < 8) { | 
 | 
            bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte;  | 
 | 
        } else if (metadata.IHDR_bitDepth == 16) { | 
 | 
            bytesPerRow *= 2;  | 
 | 
        }  | 
 | 
 | 
 | 
        IndexColorModel icm_gray_alpha = null;  | 
 | 
        if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA &&  | 
 | 
            image.getColorModel() instanceof IndexColorModel)  | 
 | 
        { | 
 | 
              | 
 | 
            bytesPerRow *= 2;  | 
 | 
 | 
 | 
              | 
 | 
            icm_gray_alpha = (IndexColorModel)image.getColorModel();  | 
 | 
        }  | 
 | 
 | 
 | 
        currRow = new byte[bytesPerRow + bpp];  | 
 | 
        prevRow = new byte[bytesPerRow + bpp];  | 
 | 
        filteredRows = new byte[5][bytesPerRow + bpp];  | 
 | 
 | 
 | 
        int bitDepth = metadata.IHDR_bitDepth;  | 
 | 
        for (int row = minY + yOffset; row < minY + height; row += ySkip) { | 
 | 
            Rectangle rect = new Rectangle(minX, row, width, 1);  | 
 | 
            Raster ras = image.getData(rect);  | 
 | 
            if (sourceBands != null) { | 
 | 
                ras = ras.createChild(minX, row, width, 1, minX, row,  | 
 | 
                                      sourceBands);  | 
 | 
            }  | 
 | 
 | 
 | 
            ras.getPixels(minX, row, width, 1, samples);  | 
 | 
 | 
 | 
            if (image.getColorModel().isAlphaPremultiplied()) { | 
 | 
                WritableRaster wr = ras.createCompatibleWritableRaster();  | 
 | 
                wr.setPixels(wr.getMinX(), wr.getMinY(),  | 
 | 
                             wr.getWidth(), wr.getHeight(),  | 
 | 
                             samples);  | 
 | 
 | 
 | 
                image.getColorModel().coerceData(wr, false);  | 
 | 
                wr.getPixels(wr.getMinX(), wr.getMinY(),  | 
 | 
                             wr.getWidth(), wr.getHeight(),  | 
 | 
                             samples);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            int[] paletteOrder = metadata.PLTE_order;  | 
 | 
            if (paletteOrder != null) { | 
 | 
                for (int i = 0; i < numSamples; i++) { | 
 | 
                    samples[i] = paletteOrder[samples[i]];  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            int count = bpp;   | 
 | 
            int pos = 0;  | 
 | 
            int tmp = 0;  | 
 | 
 | 
 | 
            switch (bitDepth) { | 
 | 
            case 1: case 2: case 4:  | 
 | 
                // Image can only have a single band  | 
 | 
 | 
 | 
                int mask = samplesPerByte - 1;  | 
 | 
                for (int s = xOffset; s < numSamples; s += xSkip) { | 
 | 
                    byte val = scale0[samples[s]];  | 
 | 
                    tmp = (tmp << bitDepth) | val;  | 
 | 
 | 
 | 
                    if ((pos++ & mask) == mask) { | 
 | 
                        currRow[count++] = (byte)tmp;  | 
 | 
                        tmp = 0;  | 
 | 
                        pos = 0;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                if ((pos & mask) != 0) { | 
 | 
                    tmp <<= ((8/bitDepth) - pos)*bitDepth;  | 
 | 
                    currRow[count++] = (byte)tmp;  | 
 | 
                }  | 
 | 
                break;  | 
 | 
 | 
 | 
            case 8:  | 
 | 
                if (numBands == 1) { | 
 | 
                    for (int s = xOffset; s < numSamples; s += xSkip) { | 
 | 
                        currRow[count++] = scale0[samples[s]];  | 
 | 
                        if (icm_gray_alpha != null) { | 
 | 
                            currRow[count++] =  | 
 | 
                                scale0[icm_gray_alpha.getAlpha(0xff & samples[s])];  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    for (int s = xOffset; s < numSamples; s += xSkip) { | 
 | 
                        for (int b = 0; b < numBands; b++) { | 
 | 
                            currRow[count++] = scale[b][samples[s + b]];  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                break;  | 
 | 
 | 
 | 
            case 16:  | 
 | 
                for (int s = xOffset; s < numSamples; s += xSkip) { | 
 | 
                    for (int b = 0; b < numBands; b++) { | 
 | 
                        currRow[count++] = scaleh[b][samples[s + b]];  | 
 | 
                        currRow[count++] = scalel[b][samples[s + b]];  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                break;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            int filterType = rowFilter.filterRow(metadata.IHDR_colorType,  | 
 | 
                                                 currRow, prevRow,  | 
 | 
                                                 filteredRows,  | 
 | 
                                                 bytesPerRow, bpp);  | 
 | 
 | 
 | 
            os.write(filterType);  | 
 | 
            os.write(filteredRows[filterType], bpp, bytesPerRow);  | 
 | 
 | 
 | 
              | 
 | 
            byte[] swap = currRow;  | 
 | 
            currRow = prevRow;  | 
 | 
            prevRow = swap;  | 
 | 
 | 
 | 
            pixelsDone += hpixels;  | 
 | 
            processImageProgress(100.0F*pixelsDone/totalPixels);  | 
 | 
 | 
 | 
            // If write has been aborted, just return;  | 
 | 
              | 
 | 
            if (abortRequested()) { | 
 | 
                return;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private void write_IDAT(RenderedImage image) throws IOException { | 
 | 
        IDATOutputStream ios = new IDATOutputStream(stream, 32768);  | 
 | 
        try { | 
 | 
            if (metadata.IHDR_interlaceMethod == 1) { | 
 | 
                for (int i = 0; i < 7; i++) { | 
 | 
                    encodePass(ios, image,  | 
 | 
                               PNGImageReader.adam7XOffset[i],  | 
 | 
                               PNGImageReader.adam7YOffset[i],  | 
 | 
                               PNGImageReader.adam7XSubsampling[i],  | 
 | 
                               PNGImageReader.adam7YSubsampling[i]);  | 
 | 
                    if (abortRequested()) { | 
 | 
                        break;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                encodePass(ios, image, 0, 0, 1, 1);  | 
 | 
            }  | 
 | 
        } finally { | 
 | 
            ios.finish();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void writeIEND() throws IOException { | 
 | 
        ChunkStream cs = new ChunkStream(PNGImageReader.IEND_TYPE, stream);  | 
 | 
        cs.finish();  | 
 | 
    }  | 
 | 
 | 
 | 
    // Check two int arrays for value equality, always returns false  | 
 | 
      | 
 | 
    private boolean equals(int[] s0, int[] s1) { | 
 | 
        if (s0 == null || s1 == null) { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        if (s0.length != s1.length) { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        for (int i = 0; i < s0.length; i++) { | 
 | 
            if (s0[i] != s1[i]) { | 
 | 
                return false;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    // Initialize the scale/scale0 or scaleh/scalel arrays to  | 
 | 
    // hold the results of scaling an input value to the desired  | 
 | 
      | 
 | 
    private void initializeScaleTables(int[] sampleSize) { | 
 | 
        int bitDepth = metadata.IHDR_bitDepth;  | 
 | 
 | 
 | 
          | 
 | 
        if (bitDepth == scalingBitDepth &&  | 
 | 
            equals(sampleSize, this.sampleSize)) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        this.sampleSize = sampleSize;  | 
 | 
        this.scalingBitDepth = bitDepth;  | 
 | 
        int maxOutSample = (1 << bitDepth) - 1;  | 
 | 
        if (bitDepth <= 8) { | 
 | 
            scale = new byte[numBands][];  | 
 | 
            for (int b = 0; b < numBands; b++) { | 
 | 
                int maxInSample = (1 << sampleSize[b]) - 1;  | 
 | 
                int halfMaxInSample = maxInSample/2;  | 
 | 
                scale[b] = new byte[maxInSample + 1];  | 
 | 
                for (int s = 0; s <= maxInSample; s++) { | 
 | 
                    scale[b][s] =  | 
 | 
                        (byte)((s*maxOutSample + halfMaxInSample)/maxInSample);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            scale0 = scale[0];  | 
 | 
            scaleh = scalel = null;  | 
 | 
        } else { // bitDepth == 16 | 
 | 
              | 
 | 
            scaleh = new byte[numBands][];  | 
 | 
            scalel = new byte[numBands][];  | 
 | 
 | 
 | 
            for (int b = 0; b < numBands; b++) { | 
 | 
                int maxInSample = (1 << sampleSize[b]) - 1;  | 
 | 
                int halfMaxInSample = maxInSample/2;  | 
 | 
                scaleh[b] = new byte[maxInSample + 1];  | 
 | 
                scalel[b] = new byte[maxInSample + 1];  | 
 | 
                for (int s = 0; s <= maxInSample; s++) { | 
 | 
                    int val = (s*maxOutSample + halfMaxInSample)/maxInSample;  | 
 | 
                    scaleh[b][s] = (byte)(val >> 8);  | 
 | 
                    scalel[b][s] = (byte)(val & 0xff);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            scale = null;  | 
 | 
            scale0 = null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void write(IIOMetadata streamMetadata,  | 
 | 
                      IIOImage image,  | 
 | 
                      ImageWriteParam param) throws IIOException { | 
 | 
        if (stream == null) { | 
 | 
            throw new IllegalStateException("output == null!"); | 
 | 
        }  | 
 | 
        if (image == null) { | 
 | 
            throw new IllegalArgumentException("image == null!"); | 
 | 
        }  | 
 | 
        if (image.hasRaster()) { | 
 | 
            throw new UnsupportedOperationException("image has a Raster!"); | 
 | 
        }  | 
 | 
 | 
 | 
        RenderedImage im = image.getRenderedImage();  | 
 | 
        SampleModel sampleModel = im.getSampleModel();  | 
 | 
        this.numBands = sampleModel.getNumBands();  | 
 | 
 | 
 | 
          | 
 | 
        this.sourceXOffset = im.getMinX();  | 
 | 
        this.sourceYOffset = im.getMinY();  | 
 | 
        this.sourceWidth = im.getWidth();  | 
 | 
        this.sourceHeight = im.getHeight();  | 
 | 
        this.sourceBands = null;  | 
 | 
        this.periodX = 1;  | 
 | 
        this.periodY = 1;  | 
 | 
 | 
 | 
        if (param != null) { | 
 | 
              | 
 | 
            Rectangle sourceRegion = param.getSourceRegion();  | 
 | 
            if (sourceRegion != null) { | 
 | 
                Rectangle imageBounds = new Rectangle(im.getMinX(),  | 
 | 
                                                      im.getMinY(),  | 
 | 
                                                      im.getWidth(),  | 
 | 
                                                      im.getHeight());  | 
 | 
                  | 
 | 
                sourceRegion = sourceRegion.intersection(imageBounds);  | 
 | 
                sourceXOffset = sourceRegion.x;  | 
 | 
                sourceYOffset = sourceRegion.y;  | 
 | 
                sourceWidth = sourceRegion.width;  | 
 | 
                sourceHeight = sourceRegion.height;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            int gridX = param.getSubsamplingXOffset();  | 
 | 
            int gridY = param.getSubsamplingYOffset();  | 
 | 
            sourceXOffset += gridX;  | 
 | 
            sourceYOffset += gridY;  | 
 | 
            sourceWidth -= gridX;  | 
 | 
            sourceHeight -= gridY;  | 
 | 
 | 
 | 
              | 
 | 
            periodX = param.getSourceXSubsampling();  | 
 | 
            periodY = param.getSourceYSubsampling();  | 
 | 
 | 
 | 
            int[] sBands = param.getSourceBands();  | 
 | 
            if (sBands != null) { | 
 | 
                sourceBands = sBands;  | 
 | 
                numBands = sourceBands.length;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        int destWidth = (sourceWidth + periodX - 1)/periodX;  | 
 | 
        int destHeight = (sourceHeight + periodY - 1)/periodY;  | 
 | 
        if (destWidth <= 0 || destHeight <= 0) { | 
 | 
            throw new IllegalArgumentException("Empty source region!"); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        this.totalPixels = destWidth*destHeight;  | 
 | 
        this.pixelsDone = 0;  | 
 | 
 | 
 | 
          | 
 | 
        IIOMetadata imd = image.getMetadata();  | 
 | 
        if (imd != null) { | 
 | 
            metadata = (PNGMetadata)convertImageMetadata(imd,  | 
 | 
                               ImageTypeSpecifier.createFromRenderedImage(im),  | 
 | 
                                                         null);  | 
 | 
        } else { | 
 | 
            metadata = new PNGMetadata();  | 
 | 
        }  | 
 | 
 | 
 | 
        if (param != null) { | 
 | 
              | 
 | 
            switch (param.getProgressiveMode()) { | 
 | 
            case ImageWriteParam.MODE_DEFAULT:  | 
 | 
                metadata.IHDR_interlaceMethod = 1;  | 
 | 
                break;  | 
 | 
            case ImageWriteParam.MODE_DISABLED:  | 
 | 
                metadata.IHDR_interlaceMethod = 0;  | 
 | 
                break;  | 
 | 
                // MODE_COPY_FROM_METADATA should alreay be taken care of  | 
 | 
                // MODE_EXPLICIT is not allowed  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        metadata.initialize(new ImageTypeSpecifier(im), numBands);  | 
 | 
 | 
 | 
          | 
 | 
        metadata.IHDR_width = destWidth;  | 
 | 
        metadata.IHDR_height = destHeight;  | 
 | 
 | 
 | 
        this.bpp = numBands*((metadata.IHDR_bitDepth == 16) ? 2 : 1);  | 
 | 
 | 
 | 
          | 
 | 
        initializeScaleTables(sampleModel.getSampleSize());  | 
 | 
 | 
 | 
        clearAbortRequest();  | 
 | 
 | 
 | 
        processImageStarted(0);  | 
 | 
 | 
 | 
        try { | 
 | 
            write_magic();  | 
 | 
            write_IHDR();  | 
 | 
 | 
 | 
            write_cHRM();  | 
 | 
            write_gAMA();  | 
 | 
            write_iCCP();  | 
 | 
            write_sBIT();  | 
 | 
            write_sRGB();  | 
 | 
 | 
 | 
            write_PLTE();  | 
 | 
 | 
 | 
            write_hIST();  | 
 | 
            write_tRNS();  | 
 | 
            write_bKGD();  | 
 | 
 | 
 | 
            write_pHYs();  | 
 | 
            write_sPLT();  | 
 | 
            write_tIME();  | 
 | 
            write_tEXt();  | 
 | 
            write_iTXt();  | 
 | 
            write_zTXt();  | 
 | 
 | 
 | 
            writeUnknownChunks();  | 
 | 
 | 
 | 
            write_IDAT(im);  | 
 | 
 | 
 | 
            if (abortRequested()) { | 
 | 
                processWriteAborted();  | 
 | 
            } else { | 
 | 
                  | 
 | 
                writeIEND();  | 
 | 
                processImageComplete();  | 
 | 
            }  | 
 | 
        } catch (IOException e) { | 
 | 
            throw new IIOException("I/O error writing PNG file!", e); | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |