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

时间:2017-01-31 13:25:05

标签: java graphics png bufferedimage javax.imageio

This should have white background如何在java.awt.image.IndexColorModel PNG中将图像保存在java中?我正在使用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减少了。如何在没有质量损失和失真的情况下正确转换回索引颜色模式?

1 个答案:

答案 0 :(得分:1)

假设我对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块来减小文件大小。您应该能够通过传递原始元数据和图像像素数据来解决这个问题,以指示编写者保留IndexColorModel(即写入PLTE块):

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:对于生产代码,您还想要dispose()reader上面的writer,但我将其留下来保持专注并避免进一步讨论{{1} }。 ; - )