I am using javax.imageio API and JAI for compressing different types of images. It works fine for JPEG using JPEGImageWriter
and GIF using GIFImageWriter
. But it is not supporting for PNG compression using PNGImageWriter
which throws an exception like compression type is not set or "No valid compression", etc. So I used this below ImageWriter
for PNG. It works but image quality is too bad.
Can anyone suggest how to use PNGImageWriter
for PNG compression and which JAR contains it?
File input = new File("test.png");
InputStream is = new FileInputStream(input);
BufferedImage image = ImageIO.read(is);
File compressedImageFile = new File(input.getName());
OutputStream os =new FileOutputStream(compressedImageFile);
Iterator<ImageWriter>writers =
ImageIO.getImageWritersByFormatName("jpg"); // here "png" does not work
ImageWriter writer = (ImageWriter) writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
writer.write(null, new IIOImage(image, null, null), param);
PNG uses DEFLATE, a non-patented lossless data compression algorithm involving a combination of LZ77 and Huffman coding.
In contrast, PNG files benefit from lossless compression. This means no data is lost when the image is compressed — the quality stays the same no matter how many times you edit and save the file.
Overview. An important adjunct to the Java Advanced Imaging (JAI) API is a set of image encoder/decoder (codec) classes, which have been packaged with JAI since the release of JAI 1.0. The encoders and decoders for several popular image storage formats have been implemented.
It seems the default com.sun.imageio.plugins.png.PNGImageWriter
that is bundled with the JRE does not support setting compression. This is kind of surprising, as the format obviously supports compression. However, the PNGImageWriter
always writes compressed.
You can see from the source code that it uses:
Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
Which will give you good, but slow compression. It might be good enough for you, but for some cases it might be better to use faster compression and larger files.
To fix your code, so that it would work with any format name, change the lines:
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
to:
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed()) {
// NOTE: Any method named [set|get]Compression.* throws UnsupportedOperationException if false
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);
}
It will still write compressed PNGs.
If you need more control over the PNG compression, like setting compression or filter used, you need to find an ImageWriter
that supports it. As you mention JAI, I think the CLibPNGImageWriter
that is part of jai-imageio.jar
or jai-imageio-tools.jar
supports setting compression. You just need to look through the ImageWriter
s iterator, to see if you have it installed:
Iterator<ImageWriter>writers = ImageIO.getImageWritersByFormatName("png");
ImageWriter writer = null;
while (writers.hasNext()) {
ImageWriter candidate = writers.next();
if (candidate.getClass().getSimpleName().equals("CLibPNGImageWriter")) {
writer = candidate; // This is the one we want
break;
}
else if (writer == null) {
writer = candidate; // Any writer is better than no writer ;-)
}
}
With the correct ImageWriter
, your code should work as expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With