Question

J'ai chargé deux images Buffered à partir de pngs. Le premier contient une image, le second un masque alpha pour l’image.

Je souhaite 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.

Était-ce utile?

La solution

Votre solution pourrait être améliorée en récupérant 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 couleurs objets à 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);
}

Autres conseils

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 seule 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, il est supposé que les deux images ont les mêmes dimensions.

J'ai récemment joué un peu avec ces éléments pour afficher une image plutôt qu'une autre et pour faire passer une image en 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 constituée de niveaux de gris, le noir devenant totalement transparent, le blanc devenant complètement opaque.

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

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.

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

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

En fait, je l'ai compris. Ce n’est probablement pas une façon rapide de le faire, 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());
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top