java - 如何在java中保存索引颜色PNG

标签 java graphics png bufferedimage javax.imageio

This should have white background如何将java中的图像保存为java.awt.image.IndexColorModel PNG?我正在使用 ImageIO 加载索引颜色 png,并使用 Catalino 库对其进行操作,不幸的是,该库将颜色空间转换为 java.awt.image.DirectColorModel

现在我想以与原始图像完全相同的格式保存结果。我尝试了以下代码片段。

private static void testIndexedColor() throws IOException {
        FastBitmap input = new FastBitmap("test.png");
        BufferedImage bi = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
        bi.getGraphics().drawImage(input.toBufferedImage(), 0, 0, null);
        ImageIO.write(bi, "PNG", new File("test_result.png"));
}

但是结果在白色背景中出现了奇怪的浅灰色像素伪影,并且 PPI 下降了。如何正确转换回索引颜色模式而不造成质量损失和失真?

最佳答案

假设我对 Catalano 框架的看法是正确的,您应该能够像这样重写您的方法:

private static void testIndexedColor() throws IOException {
    BufferedImage bi = ImageIO.read(new File("test.png"));
    FastBitmap input = new FastBitmap(bi);

    Graphics2D g = bi.createGraphics();
    try {
        g.drawImage(input.toBufferedImage(), 0, 0, null);
    }
    finally {
         g.dispose(); // Good practice ;-)
    }

    ImageIO.write(bi, "PNG", new File("test_result.png"));
}

至少你应该摆脱固定的调色板和工件。

但是,这可能仍然会修改 PPI(但这不会影响像素)。即使在某些情况下,图像也可能会作为非调色板 PNG 写回。

<小时/>

更新:似乎PNGImageWriter(通过PNGMetadata)实际上将包含完美灰度的IndexColorModel重写为灰度PNG默认情况下。这通常是一个好主意,因为您可以通过不写入 PLTE block 来减小文件大小。您应该能够通过传递原始数据中的元数据以及图像像素数据来解决此问题,以指示编写器保留 IndexColorModel (即写入 PLTE block ):

private static void testIndexedColor() throws IOException {
    File in = new File("test.png");
    File out new File("test_result.png");

    try (ImageInputStream input = ImageIO.createImageInputStream(in);
         ImageOutputStream output = ImageIO.createImageOutputStream(out)) {
        ImageReader reader = ImageIO.getImageReaders(input).next(); // Will fail if no reader
        reader.setInput(input);

        ImageWriter writer = ImageIO.getImageWriter(reader); // Will obtain a writer that understands the metadata from the reader
        writer.setOutput(output);  // Will fail if no writer

        // Now, the important part, we'll read the pixel AND metadata all in one go
        IIOImage image = reader.readAll(0, null); // PNGs only have a single image, so index 0 is safe

        // You can now access and modify the image data using:
        BufferedImage bi = (BufferedImage) image.getRenderedImage();
        FastBitmap fb = new FastBitmap(bi);

        // ...do stuff...

        Graphics2D g = bi.createGraphics();
        try {
            g.drawImage(fb.toBufferedImage(), 0, 0, null);
        }
        finally {
            g.dispose();
        }

        // Write pixel and metadata back
        writer.write(null, image, writer.getDefaultWriteParam());
    }
}

应该(作为奖励)也可以保持您的 PPI 不变。

PS:对于生产代码,您还需要对上面的 readerwriter 进行 dispose(),但我将其遗漏了保持焦点并避免对 try/finally 进行进一步讨论。 ;-)

关于java - 如何在java中保存索引颜色PNG,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41958769/

相关文章:

macos - 查询图形功能以决定是否应用 GPU 密集型效果(通过 SpriteKit)

python - 如何在 imsave() (Agg 后端)中设置 PNG 的压缩参数?

java - 如何更好地理解java上的png表示?

Java outputStream 无法正确打印

java - 带有 Flyway 4.2.0 的 Spring Boot 2.1.0

python - python中图形的交互式可视化

objective-c - 有趣的按钮

c# - 在 C# 中保存索引的 16 位灰度 png

java - <jsp :include> doesn't work in magnolia cms

java - 如何设置 Intellij 以在程序上运行 javap 命令?