Mettre le masque alpha de BufferedImage en Java

j'ai deux tampons que j'ai chargés depuis pngs. La première contient une image, la seconde un masque alpha pour l'image.

je veux créer une image combinée des deux, en appliquant le masque alpha. Mon google-fu me lâche.

je sais comment charger/enregistrer les images, j'ai juste besoin du bit où je passe de deux BufferedImages à un BufferedImage avec le canal alpha droit.

20
demandé sur Zarkonnen 2008-10-21 17:14:45

5 réponses

votre solution pourrait être améliorée en récupérant les données RVB 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 de couleur à chaque itération de la boucle intérieure.

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
répondu Michael Myers 2012-03-21 03:01:57

je suis trop en retard avec cette réponse, mais peut-être qu'elle est utile pour quelqu'un de toute façon. C'est 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 dans un tableau au début, n'exigeant donc qu'une seule boucle. En outre, il déplace directement le byte bleu à l'alpha (de la couleur du masque), au lieu de masquer d'abord le byte rouge et puis le déplacer.

comme les autres méthodes, il suppose les deux images ont les mêmes dimensions.

21
répondu Meyer 2016-02-11 16:09:10

j'ai récemment joué un peu avec ce truc, pour afficher une image sur une autre, et pour faire passer une image en gris.

Aussi masquer une image avec un masque avec transparence (ma version précédente de ce message!).

j'ai pris mon petit programme de test et modifié un peu pour obtenir le résultat voulu.

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 affiche juste les images dans un petit Swing panneau.

Notez que l'image du masque est de niveaux gris, noir devenant pleine transparence, Blanc devenant pleinement opaque.

bien que vous ayez résolu votre problème, je pensais pouvoir partager mon point de vue. Il utilise une méthode légèrement plus Java-ish, en utilisant des classes standard pour traiter / filtrer les images.

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'elle soit plus rapide (mesurer les performances respectives pourrait être intéressant), mais il est un peu plus abstrait.

Au moins, vous avez le choix! :- )

10
répondu PhiLho 2008-10-21 15:18:05

en fait, je l'ai compris. Ce n'est probablement pas une rapide façon de le faire, mais il 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
répondu Zarkonnen 2008-10-21 13:25:13

pour ceux qui utilisent alpha dans l'image originale.

j'ai écrit ce code dans Koltin, le point clé ici est que si vous avez l'alpha sur votre image originale 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 de Java (juste 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);
    }
0
répondu Thiago Medeiros dos Santos 2017-09-28 17:20:56