ubernoob
ubernoob

Reputation: 191

how to compress a PNG image using Java

Hi I would like to know if there is any way in Java to reduce the size of an image (use any kind of compression) that was loaded as a BufferedImage and is going to be saved as an PNG.

Maybe some sort of png imagewriteparam? I didnt find anything helpful so im stuck.

heres a sample how the image is loaded and saved

public static BufferedImage load(String imageUrl) {         
    Image image = new ImageIcon(imageUrl).getImage();
    bufferedImage = new BufferedImage(image.getWidth(null),
                                                    image.getHeight(null),
                                                    BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2D = bufferedImage.createGraphics();
    g2D.drawImage(image, 0, 0, null);
    return bufferedImage;
}

public static void storeImageAsPng(BufferedImage image, String imageUrl) throws IOException {
    ImageIO.write(image, "png", new File(imageUrl));
}

Upvotes: 19

Views: 39783

Answers (5)

Anshul
Anshul

Reputation: 1

I was facing the same issue but what worked me was always use jpeg as compression format even in the case of png because in case of png file it was not compressing but increasing the size of file even more.

 ImageIO.getImageWriters(type, "jpeg").next() //always jpeg

profileImage formats for me

"data:image/png;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wB...//Z"

"data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wB...//Z"

Complete Code for reference.

    public String compressProfileImage(String profileImage, float compressionQuality) throws IOException {
    if (profileImage == null || profileImage.isEmpty()) return null;
    String[] strings = profileImage.split(",");
    if (strings.length < 2)
        return null;

    byte[] decodedImg = Utils.decodeBase64Tobytes(strings[1]);
    // Read image
    RenderedImage img = ImageIO.read(new ByteArrayInputStream(decodedImg));

    // Create output stream for compressed image data
    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    // Write image to output stream with compression
    ImageWriteParam imageWriteParam = createJPEGImageWriteParam(compressionQuality);
    ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
    ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
    writer.setOutput(ios);
    writer.write(null, new IIOImage(img, null, null), imageWriteParam);

    // Get the compressed image data
    byte[] compressedImageData = bos.toByteArray();
    return strings[0] + "," + Utils.encodeBase64(compressedImageData);
}

private static ImageWriteParam createJPEGImageWriteParam(float compressionQuality) {
    JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(null);
    jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    jpegParams.setCompressionQuality(compressionQuality);
    return jpegParams;
}

Working code happy coding!!!

Upvotes: 0

Kristof Neirynck
Kristof Neirynck

Reputation: 4162

By calling ImageIO.write you use the default compression quality for pngs. You can choose an explicit compression mode which reduces file size a lot.

String inputPath = "a.png";
String outputPath = "b.png";

BufferedImage image;
IIOMetadata metadata;

try (ImageInputStream in = ImageIO.createImageInputStream(Files.newInputStream(Paths.get(inputPath)))) {
    ImageReader reader = ImageIO.getImageReadersByFormatName("png").next();
    reader.setInput(in, true, false);
    image = reader.read(0);
    metadata = reader.getImageMetadata(0);
    reader.dispose();
}

try (ImageOutputStream out = ImageIO.createImageOutputStream(Files.newOutputStream(Paths.get(outputPath)))) {
    ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image);
    ImageWriter writer = ImageIO.getImageWriters(type, "png").next();

    ImageWriteParam param = writer.getDefaultWriteParam();
    if (param.canWriteCompressed()) {
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(0.0f);
    }

    writer.setOutput(out);
    writer.write(null, new IIOImage(image, null, metadata), param);
    writer.dispose();
}

Upvotes: 5

aioobe
aioobe

Reputation: 421340

Have a look at the ImageWriterParam class in the same package as the ImageIO class. It mentions compression.

https://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageWriteParam.html

Also look at the example at http://exampledepot.com/egs/javax.imageio/JpegWrite.html and see if it translates well for PNG files.

Upvotes: 0

depsypher
depsypher

Reputation: 1219

You may want to try pngtastic. It's a pure java png image optimizer that can shrink may png images down to some degree.

Upvotes: 5

leonbloy
leonbloy

Reputation: 76026

If it's going to be saved as PNG, compression will be done at that stage. PNG has a lossless compression algorithm (basically prediction followed by lempel-ziv compression) with few adjustable parameters (types of "filters") and not much impact in compression amount - in general the default will be optimal.

Upvotes: 2

Related Questions