Compression of PNG using JAI (Java Advanced Imaging)
Asked Answered
H

2

7

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);
Horripilation answered 2/7, 2014 at 18:28 Comment(0)
S
13

Update: The default com.sun.imageio.plugins.png.PNGImageWriter that is bundled with the JDK now supports setting and specifying the compression level, since Java 9.

There is also a back port available, if you are still on Java 8 or older.

The rest of this answer assumes you are using an older version of Java, without the backport (don't do that! πŸ˜‰ ) .


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 ImageWriters 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.

Sedum answered 4/7, 2014 at 8:41 Comment(5)
Thanks for your detailed explanation. But when i tried this code it throws an exception like "Compression not supported ".... Any idea? – Horripilation
Yes. You didn't apply the if (param.canWriteCompressed()) line. And more importantly, you don't have JAI properly installed, so you are still not using CLibPNGImageWriter. Try printing the class of writer, or use a debugger to verify. – Sedum
It works fine. But after compression (32 bit)output PNG image increases the size than the source image – Horripilation
it increases the size double or triple instead of decrease. – Horripilation
2x-3x the size of what? Another PNG? Or a JPEG? – Sedum
S
0

Please check that this code may work for you.

ImageWriteParam writeParam = writer.getDefaultWriteParam();

writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);

writeParam.setCompressionType("PackBits");

writeParam.setCompressionQuality(0.5f);

Thanks.

Somnifacient answered 2/8, 2014 at 9:26 Comment(1)
I tried the solution but it says IllegalArgumentException: Unknown compression type! with Java 11 – Balmoral

© 2022 - 2024 β€” McMap. All rights reserved.