BufferedImage.setRGB 像素颜色错误

BufferedImage.setRGB pixel wrong color

尝试做一些相对简单的事情,给定一张 512x512 png 的地图,我正在尝试绘制点。我的代码相当简单,我尝试使用 setRGB 函数和 createGraphics 函数返回的 Graphics2D 对象。我一定是忽略了一些简单的事情。编辑:我应该提到我不是要创建一个新的 BufferedImage,而是要修改现有的 BufferedImage,因为连续的库调用将继续修改我正在使用的 BufferedImage。 (在下面的示例代码中,我从一个文件中读取了 BufferedImage,以一种简单的方式来重现该问题。

            File outputImage = new File("before.png");
            BufferedImage img = ImageIO.read(outputImage);


            img.setRGB(255, 255, new Color(0f, 1f, 0).getRGB());

            File after = new File("after.png");
            ImageIO.write(img, "png", after);

如果放大结果像素,它不是绿色,而是一些更深的灰色。由于此行为与 Graphics2D 一致,我希望解决此问题也能解决该问题。

BufferedImage 的颜色 space 一定是问题所在。

在下面的代码中,我使用您的原始图像并将其绘制为具有指定颜色的 BufferedImage space:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.net.*;

public class SSCCE extends JPanel
{
    SSCCE()
    {
        try
        {
            BufferedImage original = ImageIO.read( new File("map.png") );
            int width = original.getWidth(null);
            int height = original.getHeight(null);
            int size = 100;
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = bi.createGraphics();
            g2d.drawImage(original, 0, 0, null);

            int color = new Color(0f, 1f, 0f).getRGB();
            bi.setRGB(10, 10, color);
            bi.setRGB(10, 11, color);
            bi.setRGB(11, 10, color);
            bi.setRGB(11, 11, color);
            add( new JLabel( new ImageIcon(bi) ) );
        }
        catch(Exception e2) {}
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}

根据评论中的讨论,这里尝试描述一些解决问题的方法:

问题在于原始图像使用的是 IndexColorModel(或者彩色地图或 "palette",如果您愿意)。没有 完全 匹配您指定的颜色的绿色,因此颜色模型会进行查找以获取 "closest" 颜色到您指定的那个(您可能不同意这种颜色是最接近的匹配,但给出了所使用的算法)。

如果将颜色设置为与图像颜色匹配的颜色,则可以使用该颜色进行绘画。尝试 new Color(0.8f, 0.9019608f, 0.6392157f) 或 RGB 值 0xffcce6a3 以获得浅绿色。对浅蓝色使用 new Color(0.6392157f, 0.8f, 1f)0xffa3ccff

如果您想知道我是如何找到这些值的,请看这里的解释。假设 colorModel 是一个 IndexColorModel,你可以使用:

int[] rgbs = new int[colorModel.getMapSize()]; 
colorModel.getRGBs(rgbs); 

...获取颜色图中的颜色。选择其中一种颜色应该总是有效。

现在,如果您的 "library"(您没有透露太多细节)使用固定调色板生成这些图像,那么您很好,可以使用我提到的其中一种颜色,或者使用描述的方法获取颜色,然后选择合适的颜色。如果不是,则需要动态找到最佳颜色。如果你真的不走运,可能根本没有合适的颜色可用(即,你的地图块全是海洋,唯一可用的颜色是海蓝色,不可能绘制绿点)。那实在没办法解决了,只能修改库了。

一种完全不同的方法,可能类似于@camickr 的解决方案,您暂时将图像转换为真彩色(BufferedImage.TYPE_INT_RGBTYPE_3BYTE_BGR),将您的更改绘制到该临时图像上,然后将该图像重新绘制到原始图像上。这个 可能 工作得更好的原因是合成机制将使用抖动和更好的颜色查找算法。但是您仍然会遇到与上一段中所述的可用颜色相关的相同问题。


这是一个代码示例,使用暖黄色,输出:

Color color = new Color(0.89411765f, 0.5686275f, 0.019607844f);
int argb = color.getRGB();

Graphics2D g = image.createGraphics();
try {
    g.setColor(color);
    g.fillRect(10, 10, 50, 50);
}
finally {
    g.dispose();
}