简体   繁体   中英

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

I've written a program to modify images.

First, I get the image, and get its drawing context like this:

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

Graphics g = image.createGraphics();

And then I modify the image like this:

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

After I've finished modifying the image, I save the image like this:

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

Now most of the time this works just fine.

However, when I tried recoloring this texture

在此处输入图片说明

to this

在此处输入图片说明

I get this instead:

在此处输入图片说明

Inside the debugger, though, the Graphics 's color is the shade of pink I want it to be.

The comments seem to suggest that the image the user opens might have some color limitations, and since I'm drawing to the same image I'm reading from, my program has to abide by these limitations. The example image seems to be pretty grayscale-y, and apparently its bit depth is 8 bit. So maybe the pink I'm drawing on it is converted to grayscale, because the image has to stay 8-bit?

As suggested in the comments, the main problem here indeed is the wrong color model. When you load the original image, and print some information about it...

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

it will say

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

The IndexColorModel does not necessarily support all the colors, but only a subset of them. (Basically, the image supports only the colors that it "needs", which allows for a more compact storage).

The solution here is to convert the image into one that has the appropriate color model. A generic method for this is shown in the following example:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM