Definir máscara alfa de BufferedImage em Java

Eu tenho dois BufferedImages que eu carreguei de pngs. O primeiro contém uma imagem, o segundo, uma máscara alfa para a imagem.

Eu quero criar uma imagem combinada dos dois, aplicando a máscara alfa. Meu google-fu me falha.

Eu sei como carregar / salvar as imagens, eu só preciso do bit onde eu vou de dois BufferedImages para um BufferedImage com o canal alfa direito.

Sua solução poderia ser melhorada buscando os dados RGB mais de um pixel por vez (consulte http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html ) e não criando três objects Color em cada iteração do loop interno.

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

Estou muito atrasado com esta resposta, mas talvez seja útil para alguém de qualquer maneira. Esta é uma versão mais simples e eficiente do método 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); } 

Ele lê todos os pixels em um array no começo, exigindo apenas um loop. Além disso, ele desloca diretamente o byte azul para o alfa (da cor da máscara), em vez de primeiro mascarar o byte vermelho e depois mudá-lo.

Como os outros methods, supõe que ambas as imagens tenham as mesmas dimensões.

Eu joguei recentemente um pouco com essas coisas, para exibir uma imagem em vez de outra, e para diminuir a imagem para cinza.
Também mascarando uma imagem com uma máscara com transparência (minha versão anterior desta mensagem!).

Eu peguei meu pequeno programa de teste e ajustei um pouco para obter o resultado desejado.

Aqui estão os bits relevantes:

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

O restante apenas exibe as imagens em um pequeno painel Swing.
Observe que a imagem da máscara é cinza, tornando-se a transparência total em preto, tornando-se branco opaco.

Embora você tenha resolvido o seu problema, eu poderia compartilhar minha opinião sobre ele. Ele usa um método um pouco mais Java-ish, usando classs padrão para processar / filtrar imagens.
Na verdade, meu método usa um pouco mais de memory (criando uma imagem adicional) e não tenho certeza se é mais rápido (medir os respectivos desempenhos pode ser interessante), mas é um pouco mais abstrato.
Pelo menos, você tem escolha! 🙂

Na verdade, descobri. Esta provavelmente não é uma maneira rápida de fazer isso, mas funciona:

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

Para aqueles que estão usando alfa na imagem original.

Eu escrevi este código em Koltin, o ponto chave aqui é que se você tiver o alfa em sua imagem original, você precisa multiplicar esses canais.

Versão 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) } 

Versão Java (apenas traduzido do 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); } 
    Intereting Posts