В Java, как вы пишете java.awt.image.BufferedImage в 8-битный файл png? - PullRequest
8 голосов
/ 05 ноября 2008

Я пытаюсь записать файл png из файла java.awt.image.BufferedImage. Все работает нормально, но полученный 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;
    }

Ответы [ 4 ]

4 голосов
/ 06 ноября 2008

Сборка в imageio png Writer будет записывать 32-битные png-файлы на всех платформах, на которых я его использовал, независимо от исходного образа. Вы также должны знать, что многие люди жаловались, что результирующее сжатие намного ниже, чем это возможно в формате png. Существует несколько независимых png библиотек , которые позволяют вам указать точный формат, но у меня нет никакого опыта работы с ними.

1 голос
/ 07 ноября 2008

Я нашел ответ о том, как конвертировать RGBA в индексированный здесь: http://www.eichberger.de/2007/07/transparent-gifs-in-java.html

Однако полученный 8-битный png-файл имеет прозрачность 100% или 0%. Возможно, вы могли бы настроить массивы IndexColorModel, но мы решили превратить сгенерированный файл (что было маской наложения) в jpg-подложку и использовать статическую основу в качестве прозрачного наложения.

1 голос
/ 06 ноября 2008

Это интересный вопрос ... Уже поздно, завтра буду экспериментировать. Сначала я попытаюсь использовать BufferedImage.TYPE_BYTE_INDEXED (возможно, после рисования), чтобы проверить, достаточно ли умен Java для создания 8-битного PNG.
Или, может быть, какая-то библиотека изображений может это позволить.

[EDIT] Несколько лет спустя ... На самом деле, я сделал код в то время, но забыл обновить эту ветку ... Я использовал код, на который указывает 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);
  }
}
0 голосов
/ 06 ноября 2008

Спасибо, что ответили, я собирался попробовать TYPE_BYTE_INDEXED с IndexColorModel и, возможно, все еще, но если ImageIO записывает 32-битный независимо от того, кажется, что я могу тратить свое время там.

Изображение, которое я пытаюсь записать, может быть очень большим (до 8000x4000), но представляет собой простую маску для изображения внизу, поэтому будет иметь только ~ 30% прозрачного серого и 100% прозрачного выреза. Я бы использовал GIF, но IE6, похоже, испытывает проблемы с отображением такого большого размера.

Он генерируется только один раз и на внутреннем экране типа настройки, поэтому производительность также не является проблемой, но это нужно делать в коде Java, а не с помощью автономной утилиты.

Библиотеки, которые вы указали, могут быть использованы для преобразования во время записи ... Я собираюсь проверить это.

Если у кого есть способ получше, пожалуйста, дайте мне знать !!

Спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...