【问题标题】:Converting transparent gif / png to jpeg using java使用java将透明的gif / png转换为jpeg
【发布时间】:2010-10-02 15:56:55
【问题描述】:

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

Input gif image 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);

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

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

我使用的是 Java 1.5。

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

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

更新:

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

【问题讨论】:

    标签: java transparency jpeg gif java-io


    【解决方案1】:

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

    【讨论】:

    • 你好,如何将g重新返回到BufferedImage
    • @akiong ImageIO.write() 会为你做到这一点
    • 别忘了处理 g: g.dispose()
    • @user2809386 gGraphics2D 所以你可以这样做 Graphics2D g2 = null; g2 = jpgImage.createGraphics();
    【解决方案2】:

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

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

    【讨论】:

    • 这种更简单的方法消耗的内存是公认答案的两倍吗?
    【解决方案3】:

    问题(至少对于 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。您将在透明位所在的位置获得白色背景。您可以通过在调用 drawImage 之前让 g2 用另一种颜色填充图像来更改此设置。

    【讨论】:

      【解决方案4】:

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

      this rather old thread of the java2d-interest list 中找到了这段代码,我想分享一下,因为经过快速测试,它比您的解决方案性能要好得多

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

      不幸的是,我不能说我理解确切它的作用;)

      【讨论】:

      • final DirectColorModel cm = (DirectColorModel) img.getColorModel(); 行似乎是不可能的转换——ColorModel 无法转换为 DirectColorModel(我尝试了代码并在运行时遇到了转换异常)
      【解决方案5】:

      如果您创建一个 BufferedImage.TYPE_INT_ARGB 类型的 BufferedImage 并保存为 JPEG,就会出现奇怪的结果。在我的情况下,颜色被切成橙色。在其他情况下,生成的图像可能无效,其他阅读器将拒绝加载它。

      但是,如果您创建 BufferedImage.TYPE_INT_RGB 类型的图像,那么将其保存为 JPEG 就可以了。

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

      【讨论】:

      • 我认为最好的解决方案。简单、客观,并且还说明了 JDK 中的错误或设计缺陷。我同意,这绝对是一个错误,格式错误的 JPEG 并不酷。
      • Java 认为他们的透明 JPEG 很好,其他人都是问题:bugs.sun.com/bugdatabase/view_bug.do?bug_id=4836466
      【解决方案6】:

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

      【讨论】:

      • 黑色或白色(最好是可配置的)背景对我来说是可以接受的。但目前该代码会创建无效图像。
      • 是的,当前代码不会将透明自动转换为白色(或黑色或其他),它不会引发任何异常,也不会创建有效的 JPEG。创建的 JPEG 不是真的有效,我的 Android 手机在处理它们时遇到了一些问题(我正在开发一个网络服务器 android 客户端环境)。
      【解决方案7】:
      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));
              }
          }
       ImageIO.write(newImage, "jpg", f);
      

      7/9/2020 编辑: 添加了imageIO.write

      【讨论】:

      • 你错过了ImageIO.write
      • 公平,我认为有人需要图像并且知道如何处理它:p
      猜你喜欢
      • 1970-01-01
      • 2011-11-28
      • 1970-01-01
      • 1970-01-01
      • 2021-06-01
      • 2015-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多