web-dev-qa-db-fra.com

Définir le masque alpha BufferedImage en Java

J'ai deux BufferedImages j'ai chargé de pngs. Le premier contient une image, le second un masque alpha pour l'image.

Je veux créer une image combinée à partir des deux, en appliquant le masque alpha. Mon google-fu me fait défaut.

Je sais comment charger/enregistrer les images, il me faut juste le passage où je passe de deux BufferedImages à une BufferedImage avec le canal alpha droit.

20
Zarkonnen

Votre solution pourrait être améliorée en extrayant les données RVB de plus d’un pixel à la fois (voir http://Java.Sun.com/javase/6/docs/api/Java/awt/image/BufferedImage.html ), et en ne créant pas trois objets Color à chaque itération de la boucle interne.

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);
}
10
Michael Myers

Je suis trop en retard avec cette réponse, mais peut-être est-elle utile à quelqu'un de toute façon. Voici une version plus simple et plus efficace de la méthode de Michael Myers:

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

Il lit tous les pixels d'un tableau au début, ne nécessitant qu'une boucle for. De plus, il déplace directement l'octet bleu en alpha (de la couleur du masque), au lieu de d'abord masquer l'octet rouge puis de le déplacer.

Comme les autres méthodes, cela suppose que les deux images ont les mêmes dimensions.

21
Meyer

J'ai joué récemment un peu avec ce genre de choses, pour afficher une image sur l'autre, et à disparaître une image au gris.
Masquer également une image avec un masque transparent (version précédente de ce message!).

J'ai pris mon petit programme de test et l'ai légèrement modifié pour obtenir le résultat souhaité.

Voici les bits pertinents:

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

Le reste n'affiche que les images dans un petit panneau Swing.
Notez que l'image de masque est des niveaux de gris, noir devenant une transparence totale, blanc opaque devenant pleine.

Bien que vous ayez résolu votre problème, je pensais pouvoir vous donner mon point de vue. Il utilise un peu plus de méthode Java-ish, en utilisant des classes standard pour traiter/images de filtre.
En fait, ma méthode utilise un peu plus de mémoire (faire une image supplémentaire) et je ne suis pas sûr qu'il est plus rapide (pourrait être intéressant de mesurer les performances respectives), mais il est un peu plus abstrait.
Au moins, vous avez le choix! :-)

10
PhiLho

En fait, je l'ai compris. Ce n'est probablement pas une façon de le faire fast, mais cela fonctionne:

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());
    }
}
0
Zarkonnen

Pour ceux qui utilisent l'alpha dans l'image d'origine.

J'ai écrit ce code dans Koltin, le point clé ici est que si vous avez l'alpha sur votre image d'origine, vous devez multiplier ces canaux.

Koltin Version:

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

Version Java (traduit de 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);
    }