java - 使用 JAI(Java 高级成像)压缩 PNG

标签 java compression javax.imageio jai image-compression

我正在使用 javax.imageio API 和 JAI 来压缩不同类型的图像。它适用于使用 JPEGImageWriter 的 JPEG 和使用 GIFImageWriter 的 GIF。但它不支持使用 PNGImageWriter 进行 PNG 压缩,这会引发异常,例如未设置压缩类型或“无有效压缩”等。所以我在下面使用了 ImageWriter PNG。它可以工作,但图像质量太差了。

谁能建议如何使用 PNGImageWriter 进行 PNG 压缩以及哪个 JAR 包含它?

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);

最佳答案

似乎与 JRE 捆绑在一起的默认 com.sun.imageio.plugins.png.PNGImageWriter 不支持设置 压缩。这有点令人惊讶,因为该格式显然支持压缩。但是,PNGImageWriter 总是 写入压缩。

从源码可以看出它使用的是:

Deflater def = new Deflater(Deflater.BEST_COMPRESSION);

这会给你很好但很慢的压缩。这对您来说可能已经足够了,但在某些情况下,使用更快的压缩和更大的文件可能会更好。

要修复您的代码,使其适用于任何格式名称,请更改以下行:

ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.5f);

到:

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);
}

它仍然会写入压缩的 PNG。

如果您需要对 PNG 压缩进行更多控制,例如设置压缩或使用的过滤器,您需要找到支持它的 ImageWriter。正如您提到的 JAI,我认为 jai-imageio.jarjai-imageio-tools.jar 的一部分的 CLibPNGImageWriter 支持设置压缩.您只需要查看 ImageWriter 的迭代器,看看您是否安装了它:

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 ;-)
    }
}

使用正确的 ImageWriter,您的代码应该可以按预期工作。

关于java - 使用 JAI(Java 高级成像)压缩 PNG,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24538455/

相关文章:

Java:创建作为函数参数传递的泛型类型的实例

java - 如何重命名现有文件?

java - 从接口(interface)调用属性与静态类调用属性有什么区别

java - Java中的实时更新秒表?

java - 将字节数组解码为已在 Java 中压缩的位图

perl - 如何在 mod_perl 中打包/压缩 css 和 js 文件?

c++ - zip 中的无效压缩数据

Java - ImageIO.write 的加载文件在 .jar 中不起作用

java - 用ImageIO替换ImageIcon来加载图像

java - ImageIO.read getResource 错误