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_RGB
或 TYPE_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();
}
尝试做一些相对简单的事情,给定一张 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_RGB
或 TYPE_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();
}