使用java将透明的gif/png转换为jpeg

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/464825/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:58:41  来源:igfitidea点击:

Converting transparent gif / png to jpeg using java

javatransparencyjpeggifjava-io

提问by asalamon74

I'd like to convert gif images to jpeg using Java. It works great for most images, but I have a simple transparent gif image:

我想使用 Java 将 gif 图像转换为 jpeg。它适用于大多数图像,但我有一个简单的透明 gif 图像:

Input gif image http://img292.imageshack.us/img292/2103/indexedtestal7.gif

输入 gif 图片 http://img292.imageshack.us/img292/2103/indexedtestal7.gif

[In case the image is missing: it's a blue circle with transparent pixels around it]

[如果图像丢失:它是一个蓝色圆圈,周围有透明像素]

When I convert this image using the following code:

当我使用以下代码转换此图像时:

File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
File f = new File("indexed_test.jpg");
ImageIO.write(image, "jpg", f);

This code works without throwing an Exception, but results an invalid jpeg image:

此代码在不引发异常的情况下工作,但会导致无效的 jpeg 图像:

Output jpeg image

输出 jpeg 图像

[In case the image is missing: IE cannot show the jpeg, Firefox shows the image with invalid colors.]

[如果图像丢失:IE 无法显示 jpeg,Firefox 会显示颜色无效的图像。]

I'm using Java 1.5.

我正在使用 Java 1.5。

I also tried converting the sample gif to png with gimp and using the png as an input for the Java code. The result is the same.

我还尝试使用 gimp 将示例 gif 转换为 png,并使用 png 作为 Java 代码的输入。结果是一样的。

Is it a bug in the JDK? How can I convert images correctly preferably without 3rd party libraries?

这是JDK中的错误吗?如何在没有 3rd 方库的情况下正确转换图像?

UPDATE:

更新:

Answers indicate that jpeg conversion cannot handle transparency correctly (I still think that this is a bug) and suggest a workaround for replacing transparent pixels with predefined color. Both of the suggested methods are quite complex, so I've implemented a simpler one (will post as an answer). I accept the first published answer with this workaround (by Markus). I don't know which implementation is the better. I go for the simplest one still I found a gif where it's not working.

答案表明 jpeg 转换无法正确处理透明度(我仍然认为这是一个错误),并建议使用预定义颜色替换透明像素的解决方法。两种建议的方法都非常复杂,所以我实现了一个更简单的方法(将作为答案发布)。我接受此解决方法的第一个已发布答案(由 Markus)。我不知道哪种实现更好。我选择了最简单的一个,但我仍然找到了一个无法正常工作的 gif。

回答by Bombe

JPEG has no support for transparency. So even when you get the circle color correctly you will still have a black or white background, depending on your encoder and/or renderer.

JPEG 不支持透明度。因此,即使您正确获得圆圈颜色,您仍然会有黑色或白色背景,具体取决于您的编码器和/或渲染器。

回答by harmanjd

The problem (at least with png to jpg conversion) is that the color scheme isn't the same, because jpg doesn't support transparency.

问题(至少对于 png 到 jpg 的转换)是配色方案不一样,因为 jpg 不支持透明度。

What we've done successfully is something along these lines (this is pulled from various bits of code - so please forgive the crudeness of the formatting):

我们已经成功地完成了这些事情(这是从各种代码中提取的 - 所以请原谅格式的粗糙):

File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
int width = image.getWidth();
int height = image.getHeight();
BufferedImage jpgImage;

//you can probably do this without the headless check if you just use the first block
if (GraphicsEnvironment.isHeadless()) {
  if (image.getType() == BufferedImage.TYPE_CUSTOM) {
      //coerce it to  TYPE_INT_ARGB and cross fingers -- PNGs give a    TYPE_CUSTOM and that doesn't work with
      //trying to create a new BufferedImage
     jpgImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
  } else {
     jpgImage = new BufferedImage(width, height, image.getType());
  }
} else {
     jgpImage =   GraphicsEnvironment.getLocalGraphicsEnvironment().
        getDefaultScreenDevice().getDefaultConfiguration().
        createCompatibleImage(width, height, image.getTransparency()); 
}

//copy the original to the new image
Graphics2D g2 = null;
try {
 g2 = jpg.createGraphics();

 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 g2.drawImage(image, 0, 0, width, height, null);
}
finally {
   if (g2 != null) {
       g2.dispose();
   }
}

File f = new File("indexed_test.jpg");

ImageIO.write(jpgImage, "jpg", f);

This works for png to jpg and gif to jpg. And you will have a white background where the transparent bits were. You can change this by having g2 fill the image with another color before the drawImage call.

这适用于 png 到 jpg 和 gif 到 jpg。您将在透明位所在的位置获得白色背景。您可以通过在调用 drawImage 之前让 g2 用另一种颜色填充图像来更改此设置。

回答by asalamon74

As already mentioned in the UPDATE of the question I've implemented a simpler way of replacing transparent pixels with predefined color:

正如在问题的更新中已经提到的,我已经实现了一种用预定义颜色替换透明像素的更简单的方法:

public static BufferedImage fillTransparentPixels( BufferedImage image, 
                                                   Color fillColor ) {
    int w = image.getWidth();
    int h = image.getHeight();
    BufferedImage image2 = new BufferedImage(w, h, 
        BufferedImage.TYPE_INT_RGB);
    Graphics2D g = image2.createGraphics();
    g.setColor(fillColor);
    g.fillRect(0,0,w,h);
    g.drawRenderedImage(image, null);
    g.dispose();
    return image2;
}

and I call this method before jpeg conversion in this way:

我在 jpeg 转换之前以这种方式调用此方法:

if( inputImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
    inputImage = fillTransparentPixels(inputImage, Color.WHITE);
}

回答by user106456

3 months late, but I am having a very similar problem (although not even loading a gif, but simply generating a transparent image - say, no background, a colored shape - where when saving to jpeg, all colors are messed up, not only the background)

晚了 3 个月,但我遇到了一个非常相似的问题(虽然甚至没有加载 gif,但只是生成了一个透明图像——比如,没有背景,一个彩色的形状——当保存到 jpeg 时,所有的颜色都搞砸了,不仅背景)

Found this bit of code in this rather old thread of the java2d-interest list, thought I'd share, because after a quick test, it is muchmore performant than your solution:

发现这段代码在这个相当古老的Java2D的利息清单的线程,想我会分享,因为快速测试后,这是很多比你的解决方案更好的性能:

        final WritableRaster raster = img.getRaster();
        final WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), img.getHeight(), 0, 0, new int[]{0, 1, 2});

        // create a ColorModel that represents the one of the ARGB except the alpha channel
        final DirectColorModel cm = (DirectColorModel) img.getColorModel();
        final DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());

        // now create the new buffer that we'll use to write the image
        return new BufferedImage(newCM, newRaster, false, null);

Unfortunately, I can't say I understand exactlywhat it does ;)

不幸的是,我不能说我完全理解它的作用;)

回答by Lana

For Java 6 (and 5 too, I think):

对于 Java 6(我认为也是 5):

BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);

回答by romeok

If you create a BufferedImage of type BufferedImage.TYPE_INT_ARGB and save to JPEG weird things will result. In my case the colors are scewed into orange. In other cases the produced image might be invalid and other readers will refuse loading it.

如果你创建一个 BufferedImage.TYPE_INT_ARGB 类型的 BufferedImage 并保存到 JPEG 会导致奇怪的事情。在我的情况下,颜色被扭曲成橙色。在其他情况下,生成的图像可能无效,其他读者将拒绝加载它。

But if you create an image of type BufferedImage.TYPE_INT_RGB then saving it to JPEG works fine.

但是,如果您创建 BufferedImage.TYPE_INT_RGB 类型的图像,则将其保存为 JPEG 可以正常工作。

I think this is therefore a bug in Java JPEG image writer - it should write only what it can without transparency (like what .NET GDI+ does). Or in the worst case thrown an exception with a meaningful message e.g. "cannot write an image that has transparency".

因此,我认为这是 Java JPEG 图像编写器中的一个错误 - 它应该只编写它可以不透明的内容(就像 .NET GDI+ 所做的那样)。或者在最坏的情况下抛出一个带有有意义消息的异常,例如“无法编写具有透明度的图像”。

回答by Akin Okegbile

BufferedImage originalImage = ImageIO.read(getContent());
BufferedImage newImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);

    for (int x = 0; x < originalImage.getWidth(); x++) {
        for (int y = 0; y < originalImage.getHeight(); y++) {
            newImage.setRGB(x, y, originalImage.getRGB(x, y));
        }
    }