![](/img/trans.png)
[英]BufferedImage.getGraphics() resulting in memory leak, is there a fix?
[英]BufferedImage.getGraphics().drawImage() alters pixels values
(这是前一个问题的后续内容)
将BufferedImage
从一种类型转换为另一种类型或进行复制的标准推荐方法是使用getGraphics().drawImage()
( 示例 )。 令我惊讶的是,我发现即使源图像和目标图像都属于同一类型,此过程也不会使像素值保持不变! 当有一些透明度时会出现问题。
单像素ARGB图像的示例:
public static void imagesTestBiIssue() throws IOException {
//it also happens with TYPE_INT_ARGB
BufferedImage bi1 = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
// rather arbitrary. low values of alpha tend to give more difference
int argb = 0x11663322;
bi1.setRGB(0, 0, argb);
int p1 = bi1.getRGB(0, 0);
BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(),
bi1.getType());
bi2.getGraphics().drawImage(bi1, 0, 0, null);
int p2 = bi2.getRGB(0, 0);
System.out.printf("im1: %08x %s ", p1, formatARGB(p1));
System.out.printf("im2: %08x %s %s\n", p2,
formatARGB(p2), (p1 == p2 ? "" : "DIF"));
}
public static String formatARGB(int v) {
return String.format("(%d,%d,%d,%d)",
(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF);
}
这给出: im1: 11663322 (17,102,51,34) im2: 11692d1e (17,105,45,30) DIF
在某处似乎有一些颜色转换,我无法想象为什么,因为目标和源是相同的类型。 这是预期的(或可接受的)行为吗?
我终于明白了。 尝试设置graphics.setComposite(AlphaComposite.Src)
。 在我自己的库代码中,我这样做,但我从未考虑过太多...
由于默认复合材料是AlphaComposite.SrcOver
,你实际上构成半透明像素到完全透明的像素,无论是与非预乘alpha,所以这里有一个区别。 使用AlphaComposite.Src
你基本上只说源是重要的。
另请注意,较低的alpha值意味着更透明。 这意味着对于低alpha,RGB值的重要性较小,因为它们在合成时乘以alpha(即示例图像为17/255
),因此当组合到不透明背景上时差异不会产生差异。
所以我会说:有些出乎意料吗? 是。 是否可以接受? 可能是。 :-)
这是使用AlphaComposite.Src
的代码的更新版本,以及没有diff的输出:
public static void main(String[] args) {
//it also happens with TYPE_INT_ARGB
BufferedImage bi1 = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
// rather arbitrary. low values of alpha tend to give more difference
int argb = 0x11663322;
bi1.setRGB(0, 0, argb);
int p1 = bi1.getRGB(0, 0);
BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(),
bi1.getType());
Graphics2D graphics = bi2.createGraphics();
try {
graphics.setComposite(AlphaComposite.Src);
graphics.drawImage(bi1, 0, 0, null);
}
finally {
graphics.dispose();
}
int p2 = bi2.getRGB(0, 0);
System.out.printf("im1: %08x %s ", p1, formatARGB(p1));
System.out.printf("im2: %08x %s %s\n", p2,
formatARGB(p2), (p1 == p2 ? "" : "DIF"));
}
public static String formatARGB(int v) {
return String.format("(%d,%d,%d,%d)",
(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF);
}
输出:
im1: 11663322 (17,102,51,34) im2: 11663322 (17,102,51,34)
我想我已经找到了。
我已经检查过上面的(A,R,G,B)值是“真实的”(它们存储在DataBuffer中)。 但似乎(至少在我的版本中) drawImage()
使用/假设alphaPremultiplied值。 因此,在图像管道中,原始值在绘图上被预乘(具有8位量化),然后,当存储在目标BufferedImage中时,完成逆变换。 对于每个RGB通道,等式约为:
r <-- original red value
r1 = round( r * a / 255.0 ) <-- alpha multiplied
r2 = round( r1 * 255.0 /a ) <-- restored
(这仅仅是经验性的 - 我没有深入研究资料来源 - 并且存在一些四舍五入的问题,但其实质是存在的)。
当然,这种转换是有损的(对于低alpha值更是如此;特别是,如果alpha为零,它会清除所有值),但它仍然无害......如果我们假设我们只对显示或保留帖子感兴趣 - 乘以的值。 对于某些图像处理场景,这种假设可能是错误的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.