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

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

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

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

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

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

此代码可以在不抛出exception的情况下工作,但会导致无效的jpeg图像:

输出jpeg图像http://img297.imageshack.us/img297/3493/indexedtest1qe5.jpg

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

我正在使用Java 1.5。

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

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

更新:

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

对于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); 

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

 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; } 

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

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

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

我们成功完成的是这些内容(这是从各种代码中提取的 – 所以请原谅格式化的粗糙):

 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); 

这适用于png到jpg和gif到jpg。 并且你将有一个透明位的白色背景。 你可以通过让g2在drawImage调用之前用另一种颜色填充图像来改变这种情况。

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

在这个相当旧的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); 

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

如果您创建BufferedImage.TYPE_INT_ARGB类型的BufferedImage并保存为JPEG,则会产生奇怪的结果。 就我而言,颜色会变成橙色。 在其他情况下,生成的图像可能无效,其他读者将拒绝加载它。

但是如果你创建一个BufferedImage.TYPE_INT_RGB类型的图像,那么将它保存为JPEG工作正常。

我认为这是Java JPEG图像编写器中的一个错误 – 它应该只写它没有透明度的东西(就像.NET GDI +那样)。 或者在最坏的情况下,抛出有意义消息的exception,例如“无法写入具有透明度的图像”。

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

 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)); } }