java graphics alpha compositing

Establecer máscara alfa BufferedImage en Java



graphics alpha (5)

En realidad, lo he descubierto. Probablemente esta no sea una forma rápida 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()); } }

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

Quiero crear una imagen combinada de los 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 desde dos imágenes Buffered a una imagen Buffered con el canal alfa correcto.


Recientemente jugué un poco con estas cosas, para mostrar una imagen sobre otra, y para fundir una imagen en 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 puse un poco para obtener el resultado deseado.

Aquí están 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 solo muestra las imágenes en un pequeño panel Swing.
Tenga en cuenta que la imagen de la máscara es de niveles grises, el negro se vuelve transparente, el blanco se vuelve opaco.

Aunque hayas resuelto tu problema, pensé 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 usa 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, tienes elección! :-)


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


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

Lee todos los píxeles en una matriz al principio, por lo que requiere solo un for-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, asume que ambas imágenes tienen las mismas dimensiones.


Para aquellos que están usando 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.

Versión de 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) }

Versión de Java (acaba de traducir 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); }