【问题标题】:In java, how do you write a java.awt.image.BufferedImage to an 8-bit png file?在 java 中,如何将 java.awt.image.BufferedImage 写入 8 位 png 文件?
【发布时间】:2010-09-20 22:58:06
【问题描述】:

我正在尝试从 java.awt.image.BufferedImage 中写出 png 文件。一切正常,但生成的 png 是 32 位文件。

有没有办法让 png 文件变成 8 位?图像是灰度的,但我确实需要透明度,因为这是叠加图像。我使用的是 java 6,我希望返回一个 OutputStream,以便调用类处理将文件写入磁盘/db。

以下是代码的相关部分:

 public static ByteArrayOutputStream createImage(InputStream originalStream)
            throws IOException {

        ByteArrayOutputStream oStream = null;

        java.awt.Image newImg = javax.imageio.ImageIO.read(originalStream);
        int imgWidth = newImg.getWidth(null);
        int imgHeight = newImg.getHeight(null);
        java.awt.image.BufferedImage bim = new java.awt.image.BufferedImage(imgWidth,
                imgHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);

        Color bckgrndColor = new Color(0x80, 0x80, 0x80);

        Graphics2D gf = (Graphics2D)bim.getGraphics();

        // set transparency for fill image
        gf.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
        gf.setColor(bckgrndColor);
        gf.fillRect(0, 0, imgWidth, imgHeight);

        oStream = new ByteArrayOutputStream();
        javax.imageio.ImageIO.write(bim, "png", oStream);
        oStream.close();

        return oStream;
    }

【问题讨论】:

标签: java image io awt


【解决方案1】:

无论源图像是什么,imageio png writer 中的构建将在我使用过的所有平台上编写 32 位 png 文件。您还应该知道,许多人抱怨生成的压缩比使用 png 格式的压缩要低得多。有几个独立的png libraries 可用,可让您指定确切的格式,但我实际上对其中任何一个都没有任何经验。

【讨论】:

    【解决方案2】:

    我在这里找到了如何将 RGBA 转换为索引的答案:http://www.eichberger.de/2007/07/transparent-gifs-in-java.html

    但是,生成的 8 位 png 文件只有 100% 或 0% 的透明度。您可能可以调整 IndexColorModel 数组,但我们决定将生成的文件(什么是覆盖蒙版)制作为底层 jpg 并使用静态基础作为透明覆盖。

    【讨论】:

    • 您实际上可以通过单击编辑将信息添加到您的原始问题中,而不是在您的帖子中添加答案。用户可以看到已更改的内容,而不仅仅是添加到答案列中。
    【解决方案3】:

    这是一个有趣的问题……时间不早了,我明天做实验。我将首先尝试使用 BufferedImage.TYPE_BYTE_INDEXED(可能在绘制之后)来查看 Java 是否足够智能以生成 8 位 PNG。
    或者也许某些图像库可以允许这样做。

    [编辑] 几年后...实际上,我当时编写了代码,但忘记更新此线程...我使用了the code pointed at by Kat,对透明度的处理进行了一些改进,并保存在PNG 格式而不是 Gif 格式。它适用于制作具有全有或全无透明度的 8 位 PNG 文件。

    您可以在http://bazaar.launchpad.net/~philho/+junk/Java/view/head:/Tests/src/org/philhosoft/tests/image/AddTransparency.java 找到工作测试文件 使用我的ImageUtil 课程。

    由于代码不是那么大,为了后代,我将其发布在这里,没有JavaDoc以节省一些行。

    public class ImageUtil
    {
      public static int ALPHA_BIT_MASK = 0xFF000000;
    
      public static BufferedImage imageToBufferedImage(Image image, int width, int height)
      {
        return imageToBufferedImage(image, width, height, BufferedImage.TYPE_INT_ARGB);
      }
    
      public static BufferedImage imageToBufferedImage(Image image, int width, int height, int type)
      {
        BufferedImage dest = new BufferedImage(width, height, type);
        Graphics2D g2 = dest.createGraphics();
        g2.drawImage(image, 0, 0, null);
        g2.dispose();
        return dest;
      }
    
      public static BufferedImage convertRGBAToIndexed(BufferedImage srcImage)
      {
        // Create a non-transparent palletized image
        Image flattenedImage = transformTransparencyToMagenta(srcImage);
        BufferedImage flatImage = imageToBufferedImage(flattenedImage,
            srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
        BufferedImage destImage = makeColorTransparent(flatImage, 0, 0);
        return destImage;
      }
    
      private static Image transformTransparencyToMagenta(BufferedImage image)
      {
        ImageFilter filter = new RGBImageFilter()
        {
          @Override
          public final int filterRGB(int x, int y, int rgb)
          {
            int pixelValue = 0;
            int opacity = (rgb & ALPHA_BIT_MASK) >>> 24;
            if (opacity < 128)
            {
              // Quite transparent: replace color with transparent magenta
              // (traditional color for binary transparency)
              pixelValue = 0x00FF00FF;
            }
            else
            {
              // Quite opaque: get pure color
              pixelValue = (rgb & 0xFFFFFF) | ALPHA_BIT_MASK;
            }
            return pixelValue;
          }
        };
    
        ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
          return Toolkit.getDefaultToolkit().createImage(ip);
      }
    
      public static BufferedImage makeColorTransparent(BufferedImage image, int x, int y)
      {
        ColorModel cm = image.getColorModel();
        if (!(cm instanceof IndexColorModel))
          return image; // No transparency added as we don't have an indexed image
    
        IndexColorModel originalICM = (IndexColorModel) cm;
        WritableRaster raster = image.getRaster();
        int colorIndex = raster.getSample(x, y, 0); // colorIndex is an offset in the palette of the ICM'
        // Number of indexed colors
        int size = originalICM.getMapSize();
        byte[] reds = new byte[size];
        byte[] greens = new byte[size];
        byte[] blues = new byte[size];
        originalICM.getReds(reds);
        originalICM.getGreens(greens);
        originalICM.getBlues(blues);
        IndexColorModel newICM = new IndexColorModel(8, size, reds, greens, blues, colorIndex);
        return new BufferedImage(newICM, raster, image.isAlphaPremultiplied(), null);
      }
    }
    

    【讨论】:

    • 4 年后,仍在等待这是一个答案,而不是另一个问题。
    • 我在回复中没有看到任何问题,尽管老实说我也没有看到答案(嘿,我当时是 SO 新手……)。无论如何,尽管语气,谢谢你的提醒。
    • 很抱歉语气,它不应该听起来咄咄逼人。 SO 要求您说明为什么要投反对票,所以我只是在回答这个问题。
    【解决方案4】:

    感谢您的回复,我打算尝试使用 IndexColorModel 的TYPE_BYTE_INDEXED,但如果 ImageIO 写出 32 位,无论看起来我可能在那里浪费时间。

    我要写出的图像可能非常大(最大 8000x4000),但只是下面图像的简单蒙版,因此只有约 30% 的透明灰色和 100% 的透明切口。我会使用 GIF,但 IE6 似乎无法显示那么大的 GIF。

    它只会在内部设置类型屏幕中生成一次,因此性能也不是问题,但它必须在 java 代码中完成,而不是通过离线实用程序完成。

    您指定的库可能用于在编写时对其进行转换...我要去看看。

    如果有人有更好的方法,请告诉我!!

    谢谢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 1970-01-01
      • 2013-09-22
      • 2020-07-10
      • 1970-01-01
      • 2014-09-17
      相关资源
      最近更新 更多