< prev index next >

src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java

Print this page

        

*** 24,61 **** */ 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++) { --- 24,57 ---- */ package com.sun.imageio.plugins.png; import java.awt.Rectangle; 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.IOException; 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.spi.ImageWriterSpi; import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStreamImpl; ! final class CRC { ! private static final int[] crcTable = new int[256]; private int crc = 0xffffffff; static { // Initialize CRC table for (int n = 0; n < 256; n++) {
*** 70,134 **** 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(); --- 66,136 ---- crcTable[n] = c; } } } ! CRC() {} ! void reset() { crc = 0xffffffff; } ! void update(byte[] data, int off, int len) { ! int c = crc; for (int n = 0; n < len; n++) { ! c = crcTable[(c ^ data[off + n]) & 0xff] ^ (c >>> 8); } + crc = c; } ! void update(int data) { crc = crcTable[(crc ^ data) & 0xff] ^ (crc >>> 8); } ! int getValue() { return crc ^ 0xffffffff; } } final class ChunkStream extends ImageOutputStreamImpl { ! private final ImageOutputStream stream; ! private final long startPos; ! private final CRC crc = new CRC(); ! ChunkStream(int type, ImageOutputStream stream) throws IOException { this.stream = stream; this.startPos = stream.getStreamPosition(); stream.writeInt(-1); // length, will backpatch writeInt(type); } + @Override public int read() throws IOException { throw new RuntimeException("Method not available"); } + @Override public int read(byte[] b, int off, int len) throws IOException { throw new RuntimeException("Method not available"); } + @Override public void write(byte[] b, int off, int len) throws IOException { crc.update(b, off, len); stream.write(b, off, len); } + @Override public void write(int b) throws IOException { crc.update(b); stream.write(b); } ! void finish() throws IOException { // Write CRC stream.writeInt(crc.getValue()); // Write length long pos = stream.getStreamPosition();
*** 138,175 **** // 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(); --- 140,183 ---- // Return to end of chunk and flush to minimize buffering stream.seek(pos); stream.flushBefore(pos); } + @Override 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 final byte[] chunkType = { (byte)'I', (byte)'D', (byte)'A', (byte)'T' }; ! private final ImageOutputStream stream; ! private final int chunkLength; private long startPos; ! private final CRC crc = new CRC(); ! private final Deflater def; ! private final byte[] buf = new byte[512]; ! // reused 1 byte[] array: ! private final byte[] wbuf1 = new byte[1]; private int bytesRemaining; ! IDATOutputStream(ImageOutputStream stream, int chunkLength, ! int deflaterLevel) throws IOException ! { this.stream = stream; this.chunkLength = chunkLength; + this.def = new Deflater(deflaterLevel); + startChunk(); } private void startChunk() throws IOException { crc.reset();
*** 204,221 **** --- 212,232 ---- this.startPos = stream.getStreamPosition(); throw e; } } + @Override public int read() throws IOException { throw new RuntimeException("Method not available"); } + @Override public int read(byte[] b, int off, int len) throws IOException { throw new RuntimeException("Method not available"); } + @Override public void write(byte[] b, int off, int len) throws IOException { if (len == 0) { return; }
*** 225,235 **** deflate(); } } } ! public void deflate() throws IOException { int len = def.deflate(buf, 0, buf.length); int off = 0; while (len > 0) { if (bytesRemaining == 0) { --- 236,246 ---- deflate(); } } } ! void deflate() throws IOException { int len = def.deflate(buf, 0, buf.length); int off = 0; while (len > 0) { if (bytesRemaining == 0) {
*** 245,261 **** 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(); --- 256,272 ---- len -= nbytes; bytesRemaining -= nbytes; } } + @Override public void write(int b) throws IOException { ! wbuf1[0] = (byte)b; ! write(wbuf1, 0, 1); } ! void finish() throws IOException { try { if (!def.finished()) { def.finish(); while (!def.finished()) { deflate();
*** 265,296 **** } 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; --- 276,369 ---- } finally { def.end(); } } + @Override protected void finalize() throws Throwable { // Empty finalizer (for improved performance; no need to call // super.finalize() in this case) } } ! final class PNGImageWriteParam extends ImageWriteParam { ! ! /** Default quality level = 0.5 ie medium compression */ ! private static final float DEFAULT_QUALITY = 0.5f; ! ! private static final String[] compressionNames = {"Deflate"}; ! private static final float[] qualityVals = { 0.00F, 0.30F, 0.75F, 1.00F }; ! private static final String[] qualityDescs = { ! "High compression", // 0.00 -> 0.30 ! "Medium compression", // 0.30 -> 0.75 ! "Low compression" // 0.75 -> 1.00 ! }; ! PNGImageWriteParam(Locale locale) { super(); this.canWriteProgressive = true; this.locale = locale; + this.canWriteCompressed = true; + this.compressionTypes = compressionNames; + this.compressionType = compressionTypes[0]; + this.compressionMode = MODE_DEFAULT; + this.compressionQuality = DEFAULT_QUALITY; + } + + /** + * Removes any previous compression quality setting. + * + * <p> The default implementation resets the compression quality + * to <code>0.5F</code>. + * + * @exception IllegalStateException if the compression mode is not + * <code>MODE_EXPLICIT</code>. + */ + @Override + public void unsetCompression() { + super.unsetCompression(); + this.compressionType = compressionTypes[0]; + this.compressionQuality = DEFAULT_QUALITY; + } + + /** + * Returns <code>true</code> since the PNG plug-in only supports + * lossless compression. + * + * @return <code>true</code>. + */ + @Override + public boolean isCompressionLossless() { + return true; + } + + @Override + public String[] getCompressionQualityDescriptions() { + super.getCompressionQualityDescriptions(); + return qualityDescs.clone(); + } + + @Override + public float[] getCompressionQualityValues() { + super.getCompressionQualityValues(); + return qualityVals.clone(); } } /** */ ! public final class PNGImageWriter extends ImageWriter { ! ! /** Default compression level = 4 ie medium compression */ ! private static final int DEFAULT_COMPRESSION_LEVEL = 4; ImageOutputStream stream = null; + // compression level + int deflaterLevel = DEFAULT_COMPRESSION_LEVEL; + PNGMetadata metadata = null; // Factors from the ImageWriteParam int sourceXOffset = 0; int sourceYOffset = 0;
*** 332,341 **** --- 405,415 ---- public PNGImageWriter(ImageWriterSpi originatingProvider) { super(originatingProvider); } + @Override public void setOutput(Object output) { super.setOutput(output); if (output != null) { if (!(output instanceof ImageOutputStream)) { throw new IllegalArgumentException("output not an ImageOutputStream!");
*** 344,375 **** } 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) { --- 418,452 ---- } else { this.stream = null; } } ! @Override public ImageWriteParam getDefaultWriteParam() { return new PNGImageWriteParam(getLocale()); } + @Override public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { return null; } + @Override public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) { PNGMetadata m = new PNGMetadata(); m.initialize(imageType, imageType.getSampleModel().getNumBands()); return m; } + @Override public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) { return null; } + @Override public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { // TODO - deal with imageType if (inData instanceof PNGMetadata) {
*** 933,943 **** } } // Use sourceXOffset, etc. 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], --- 1010,1021 ---- } } // Use sourceXOffset, etc. private void write_IDAT(RenderedImage image) throws IOException { ! IDATOutputStream ios = new IDATOutputStream(stream, 32768, ! deflaterLevel); try { if (metadata.IHDR_interlaceMethod == 1) { for (int i = 0; i < 7; i++) { encodePass(ios, image, PNGImageReader.adam7XOffset[i],
*** 1026,1035 **** --- 1104,1114 ---- scale = null; scale0 = null; } } + @Override public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IIOException { if (stream == null) { throw new IllegalStateException("output == null!");
*** 1109,1128 **** } else { metadata = new PNGMetadata(); } if (param != null) { // Use Adam7 interlacing if set in write param 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 } } // Initialize bitDepth and colorType metadata.initialize(new ImageTypeSpecifier(im), numBands); --- 1188,1226 ---- } else { metadata = new PNGMetadata(); } if (param != null) { + switch(param.getCompressionMode()) { + case ImageWriteParam.MODE_DISABLED: + deflaterLevel = Deflater.NO_COMPRESSION; + break; + case ImageWriteParam.MODE_EXPLICIT: + float quality = param.getCompressionQuality(); + if (quality >= 0f && quality <= 1f) { + deflaterLevel = 9 - Math.round(9f * quality); + } else { + deflaterLevel = DEFAULT_COMPRESSION_LEVEL; + } + break; + case ImageWriteParam.MODE_DEFAULT: + deflaterLevel = DEFAULT_COMPRESSION_LEVEL; + break; + default: + } + // Use Adam7 interlacing if set in write param 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 already be taken care of // MODE_EXPLICIT is not allowed + default: } } // Initialize bitDepth and colorType metadata.initialize(new ImageTypeSpecifier(im), numBands);
< prev index next >