2008-10-21 23 views
19

Tengo dos imágenes almacenadas en Buffered que cargué desde pngs. El primero contiene una imagen, el segundo una máscara alfa para la imagen.Establecer la máscara alfa BufferedImage en Java

Quiero crear una imagen combinada de las dos, aplicando la máscara alfa. Mi google-fu me falla.

Sé cómo cargar/guardar las imágenes, solo necesito el bit donde voy de dos imágenes Buffered a una imagen Buffered con el canal alfa correcto.

+0

he editado mi respuesta para dar un código correcto (haciendo lo que solicitó!) De una manera alternativa. – PhiLho

Respuesta

9

Su solución podría mejorarse obteniendo los datos RGB más de un píxel por vez (consulte http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html) y no creando tres objetos Color en cada iteración del ciclo 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); 
} 
0

En realidad, lo he descubierto. Esto probablemente no es un ayuno forma de hacerlo , pero 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()); 
    } 
} 
9

jugué hace poco un poco con estas cosas, para mostrar una imagen sobre otra, y al desaparecer una imagen a gris.
También enmascara una imagen con una máscara con transparencia (¡mi versión anterior de este mensaje!).

Tomé mi pequeño programa de prueba y lo pellizqué un poco para obtener el resultado deseado.

Éstos son los 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; 
} 

El resto acaba de mostrar las imágenes en un pequeño panel de oscilación.
Tenga en cuenta que la imagen de la máscara es de niveles grises, el negro se convierte en transparencia total, el blanco se vuelve opaco.

Aunque ha resuelto su problema, creo que podría compartir mi opinión sobre él. Utiliza un método ligeramente más Java-ish, utilizando clases estándar para procesar/filtrar imágenes.
En realidad, mi método utiliza un poco más de memoria (haciendo una imagen adicional) y no estoy seguro de que sea más rápido (medir las actuaciones respectivas podría ser interesante), pero es un poco más abstracto.
¡Al menos, tiene elección! :-)

21

Estoy demasiado tarde con esta respuesta, pero tal vez sea útil para alguien de todos modos. Esta es una versión más simple y más eficiente de método 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); 
} 

Se lee todos los píxeles en una matriz al comienzo, requiriendo así sólo una para-loop. Además, cambia directamente el byte azul al alfa (del color de la máscara), en lugar de enmascarar primero el byte rojo y luego cambiarlo.

Al igual que los otros métodos, se supone que ambas imágenes tienen las mismas dimensiones.

0

Para aquellos que están utilizando alfa en la imagen original.

Escribí este código en Koltin, el punto clave aquí es que si tienes el alfa en tu imagen original, necesitas multiplicar estos canales.

Koltin Versión:

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

versión de Java (justo traducido del 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); 
    } 
Cuestiones relacionadas