绘制到 BufferedImage 有时但始终会产生错误的颜色

Drawing to a BufferedImage sometimes, but consistently, yields wrong colors

我写了一个修改图片的程序。

首先,我获取图像,并获取其绘图上下文,如下所示:

BufferedImage image;
try {
    image = ImageIO.read(inputFile);
} catch (IOException ioe) { /* exception handling ... */ }

Graphics g = image.createGraphics();

然后我像这样修改图像:

for (int x = 0; x < image.getWidth(); x++) {
    for (int y = 0; y < image.getHeight(); y++) {
        g.setColor( /* calculate color ... */ );
        g.fillRect(x, y, 1, 1);
    }
}

我修改完图片后,我这样保存图片:

try {
    ImageIO.write(image, "PNG", save.getSelectedFile());
} catch (IOException ioe) { /* exception handling ... */ }

现在大部分时间都可以正常工作。

但是,当我尝试重新着色此纹理时

至此

我改为:

不过,在调试器内部,Graphics 的颜色是我想要的粉红色。

这些评论似乎表明用户打开的图像可能有一些颜色限制,并且由于我绘制的图像与我正在阅读的图像相同,所以我的程序必须遵守这些限制。示例图像似乎是非常灰度的,显然它的位深度是 8 位。所以也许我在上面画的粉红色被转换为灰度,因为图像必须保持 8 位?

正如评论中所建议的,这里的主要问题确实是错误的颜色模型。当您加载原始图像并打印有关它的一些信息时...

BufferedImage image = ImageIO.read(
    new URL("https://i.stack.imgur.com/pSUFR.png"));
System.out.println(image);

它会说

BufferedImage@5419f379: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@7dc7cbad transparency = 1 transIndex   = -1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 128 height = 128 #numDataElements 1 dataOff[0] = 0

IndexColorModel不一定支持所有的颜色,只是其中的一个子集。 (基本上,图像只支持它 "needs" 的颜色,这样可以更紧凑地存储)。

此处的解决方案是将图像转换为具有适当颜色模型的图像。下面的示例显示了一个通用方法:

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

public class ImageColors
{
    public static void main(String[] args) throws IOException
    {
        BufferedImage image = ImageIO.read(
            new URL("https://i.stack.imgur.com/pSUFR.png"));

        // This will show that the image has an IndexColorModel.
        // This does not necessarily support all colors.
        System.out.println(image);

        // Convert the image to a generic ARGB image
        image = convertToARGB(image);

        // Now, the image has a DirectColorModel, supporting all colors
        System.out.println(image);

        Graphics2D g = image.createGraphics();
        g.setColor(Color.PINK);
        g.fillRect(50, 50, 50, 50);
        g.dispose();

        ImageIO.write(image, "PNG", new File("RightColors.png"));
    }

    public static BufferedImage convertToARGB(BufferedImage image)
    {
        BufferedImage newImage = new BufferedImage(
            image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }    
}