2008-10-21 5 views
19

J'ai deux images tamponnées que j'ai chargées à partir de pngs. Le premier contient une image, le second un masque alpha pour l'image.Définir le masque alpha de BufferedImage en Java

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

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

+0

J'ai modifié ma réponse à donner un code correct (faire ce que vous avez demandé!) D'une autre manière. – PhiLho

Répondre

9

Votre solution pourrait être améliorée en extrayant 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 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); 
} 
0

En fait, je l'ai compris. Ceci est probablement pas un moyen 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()); 
    } 
} 
9

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

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

Voici les bits correspondants:

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 afficher seulement les images dans un petit panneau de Swing.
Notez que l'image du masque est des niveaux de gris, le noir devenant totalement transparent, le blanc devenant totalement opaque.

Bien que vous ayez résolu votre problème, je pourrais partager mes impressions. 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 c'est un peu plus abstrait.
Au moins, vous avez le choix! :-)

21

Je suis trop en retard avec cette réponse, mais peut-être que c'est utile pour quelqu'un de toute façon. Ceci 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, ce qui nécessite une seule boucle for. En outre, il déplace directement l'octet bleu vers l'alpha (de la couleur du masque), au lieu de masquer d'abord l'octet rouge puis de le décaler.

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

0

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