java - BufferedImage.getGraphics().drawImage() 改变像素值

标签 java image bufferedimage

(这是 previous 问题的后续)

BufferedImage 从一种类型转换为另一种类型或制作副本的标准推荐方法是使用 getGraphics().drawImage() ( example ) .令我惊讶的是,我发现即使源图像和目标图像的类型相同,此过程也不会保持像素值不变!当有一定的透明度时,问题就会出现。

单像素 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),因此 diff 在组合到不透明的背景上。

所以我会说:有点出乎意料?是的。可以接受吗?可能是。 :-)


这是您的代码的更新版本,使用 AlphaComposite.Src,输出没有差异:

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)

关于java - BufferedImage.getGraphics().drawImage() 改变像素值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23723518/

相关文章:

c# - 为什么 Bitmap.Save 会改变图像的大小?

javascript - 我可以保存我用 Javascript 编辑的图像,也许使用 PHP,以便更改是永久性的吗?

Java、LWJGL、OpenGL 1.1、将BufferedImage解码为Bytebuffer并跨类绑定(bind)到OpenGL

java - BufferedReader.readLine() 有时会挂起

java - 重写代码以仅获取几个变量

html - 将背景图像设置为全屏且可滚动

java - 更新 JFrame 中的 BufferedImage

java - 如何获取不是 .jpeg、.gif、.bmp 或 .png 的文件的 BufferedImage

java - Android 另一个 Activity 调用主 Activity Databasehelper 函数失败

java - 为什么 JML 不作为 Java 中的注解实现?