Установить альфа-маску BufferedImage в Java - PullRequest
20 голосов
/ 21 октября 2008

У меня есть два BufferedImage, которые я загрузил из pngs. Первый содержит изображение, второй альфа-маску для изображения.

Я хочу создать комбинированное изображение из двух, применяя альфа-маску. Мой гугл-фу подводит меня.

Я знаю, как загрузить / сохранить изображения, мне просто нужен бит, в котором я перехожу от двух BufferedImage к одному BufferedImage с правильным альфа-каналом.

Ответы [ 5 ]

21 голосов
/ 09 ноября 2011

Я слишком опоздал с этим ответом, но, возможно, он кому-то пригодится. Это более простая и эффективная версия метода Майкла Майерса:

public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
    int width = image.getWidth();
    int height = image.getHeight();

    int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
    int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);

    for (int i = 0; i < imagePixels.length; i++)
    {
        int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
        int alpha = maskPixels[i] << 24; // Shift blue to alpha
        imagePixels[i] = color | alpha;
    }

    image.setRGB(0, 0, width, height, imagePixels, 0, width);
}

Сначала все пиксели считываются в массив, поэтому требуется только один цикл for. Кроме того, он непосредственно смещает синий байт в альфа (цвета маски) вместо того, чтобы сначала маскировать красный байт, а затем смещать его.

Как и другие методы, предполагается, что оба изображения имеют одинаковые размеры.

10 голосов
/ 21 октября 2008

Недавно я немного поиграл с этим материалом, чтобы отобразить изображение поверх другого и сделать изображение серым.
Также маскируем изображение маской с прозрачностью (моя предыдущая версия этого сообщения!).

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

Вот соответствующие биты:

TestMask() throws IOException
{
    m_images = new BufferedImage[3];
    m_images[0] = ImageIO.read(new File("E:/Documents/images/map.png"));
    m_images[1] = ImageIO.read(new File("E:/Documents/images/mapMask3.png"));
    Image transpImg = TransformGrayToTransparency(m_images[1]);
    m_images[2] = ApplyTransparency(m_images[0], transpImg);
}

private Image TransformGrayToTransparency(BufferedImage image)
{
    ImageFilter filter = new RGBImageFilter()
    {
        public final int filterRGB(int x, int y, int rgb)
        {
            return (rgb << 8) & 0xFF000000;
        }
    };

    ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
    return Toolkit.getDefaultToolkit().createImage(ip);
}

private BufferedImage ApplyTransparency(BufferedImage image, Image mask)
{
    BufferedImage dest = new BufferedImage(
            image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = dest.createGraphics();
    g2.drawImage(image, 0, 0, null);
    AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0F);
    g2.setComposite(ac);
    g2.drawImage(mask, 0, 0, null);
    g2.dispose();
    return dest;
}

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

Несмотря на то, что вы решили свою проблему, я мог бы поделиться своим мнением. Он использует немного больше Java-метода, используя стандартные классы для обработки / фильтрации изображений.
На самом деле, мой метод использует немного больше памяти (для создания дополнительного изображения), и я не уверен, что он быстрее (измерение соответствующих характеристик может быть интересным), но он немного более абстрактный.
По крайней мере, у вас есть выбор! : -)

10 голосов
/ 21 октября 2008

Ваше решение можно улучшить, выбирая данные RGB более чем по одному пикселю за раз (см. http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html),, и не создавая три объекта Color на каждой итерации внутреннего цикла.

final int width = image.getWidth();
int[] imgData = new int[width];
int[] maskData = new int[width];

for (int y = 0; y < image.getHeight(); y++) {
    // fetch a line of data from each image
    image.getRGB(0, y, width, 1, imgData, 0, 1);
    mask.getRGB(0, y, width, 1, maskData, 0, 1);
    // apply the mask
    for (int x = 0; x < width; x++) {
        int color = imgData[x] & 0x00FFFFFF; // mask away any alpha present
        int maskColor = (maskData[x] & 0x00FF0000) << 8; // shift red into alpha bits
        color |= maskColor;
        imgData[x] = color;
    }
    // replace the data
    image.setRGB(0, y, width, 1, imgData, 0, 1);
}
0 голосов
/ 28 сентября 2017

Для тех, кто использует альфа в исходном изображении.

Я написал этот код в Koltin, ключевой момент здесь заключается в том, что если у вас есть альфа на исходном изображении, вам нужно умножить эти каналы.

Колтин Версия:

    val width = this.width
    val imgData = IntArray(width)
    val maskData = IntArray(width)

    for(y in 0..(this.height - 1)) {

      this.getRGB(0, y, width, 1, imgData, 0, 1)
      mask.getRGB(0, y, width, 1, maskData, 0, 1)

      for (x in 0..(this.width - 1)) {

        val maskAlpha = (maskData[x] and 0x000000FF)/ 255f
        val imageAlpha = ((imgData[x] shr 24) and 0x000000FF) / 255f
        val rgb = imgData[x] and 0x00FFFFFF
        val alpha = ((maskAlpha * imageAlpha) * 255).toInt() shl 24
        imgData[x] = rgb or alpha
      }
      this.setRGB(0, y, width, 1, imgData, 0, 1)
    }

Java-версия (только что переведена с Kotlin)

    int width = image.getWidth();
    int[] imgData = new int[width];
    int[] maskData = new int[width];

    for (int y = 0; y < image.getHeight(); y ++) {

        image.getRGB(0, y, width, 1, imgData, 0, 1);
        mask.getRGB(0, y, width, 1, maskData, 0, 1);

        for (int x = 0; x < image.getWidth(); x ++) {

            //Normalize (0 - 1)
            float maskAlpha = (maskData[x] & 0x000000FF)/ 255f;
            float imageAlpha = ((imgData[x] >> 24) & 0x000000FF) / 255f;

            //Image without alpha channel
            int rgb = imgData[x] & 0x00FFFFFF;

            //Multiplied alpha
            int alpha = ((int) ((maskAlpha * imageAlpha) * 255)) << 24;

            //Add alpha to image
            imgData[x] = rgb | alpha;
        }
        image.setRGB(0, y, width, 1, imgData, 0, 1);
    }
0 голосов
/ 21 октября 2008

На самом деле, я понял это. Вероятно, это не быстрый способ сделать это, но он работает:

for (int y = 0; y < image.getHeight(); y++) {
    for (int x = 0; x < image.getWidth(); x++) {
        Color c = new Color(image.getRGB(x, y));
        Color maskC = new Color(mask.getRGB(x, y));
        Color maskedColor = new Color(c.getRed(), c.getGreen(), c.getBlue(),
            maskC.getRed());
        resultImg.setRGB(x, y, maskedColor.getRGB());
    }
}
...