2010-09-06 24 views
17

Estoy tratando de dibujar una imagen, con una fuente Bitmap y una máscara alfa Bitmap, usando el objeto System.Drawing.Graphics. En este momento hago un bucle X e Y y uso GetPixel y SetPixel para escribir el color de fuente y enmascara alfa en un tercer Bitmap, y luego lo renderizo. Sin embargo, esto es muy ineficiente y me pregunto si hay una forma más rápida de lograr esto.Enmascaramiento alfa en C# System.Drawing?

El efecto que busco tiene este aspecto:

Effect I’m after

El patrón de cuadrícula representa la transparencia; probablemente lo sabías

+2

Como dices que estás utilizando System.Drawing.Graphics, es probable que este tema no esté relacionado, pero aquí hay un enlace a las máscaras de opacidad en WPF, que aprovecharán la aceleración de hardware: http://msdn.microsoft.com/ en-us/library/system.windows.media.drawinggroup.opacitymask.aspx – Douglas

+0

fuera de tema, pero fascinante, ¡sin embargo, gracias! – Origamiguy

Respuesta

15

Sí, la forma más rápida de hacerlo es usar Bitmap.LockBits y usar la aritmética del puntero para recuperar los valores en lugar de GetPixel y SetPixel. La desventaja, por supuesto, es que tienes que usar un código inseguro; si comete un error, puede causar algunos bloqueos realmente graves en su programa. Pero si lo mantienes simple y autónomo, debería estar bien (hey, si puedo, también puedes hacerlo).

Por ejemplo, se podría hacer algo como esto (no probado, uso bajo su propio riesgo):

Bitmap mask = ...; 
Bitmap input = ...; 

Bitmap output = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb); 
var rect = new Rectangle(0, 0, input.Width, input.Height); 
var bitsMask = mask.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
var bitsInput = input.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
var bitsOutput = output.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); 
unsafe 
{ 
    for (int y = 0; y < input.Height; y++) 
    { 
     byte* ptrMask = (byte*) bitsMask.Scan0 + y * bitsMask.Stride; 
     byte* ptrInput = (byte*) bitsInput.Scan0 + y * bitsInput.Stride; 
     byte* ptrOutput = (byte*) bitsOutput.Scan0 + y * bitsOutput.Stride; 
     for (int x = 0; x < input.Width; x++) 
     { 
      ptrOutput[4 * x] = ptrInput[4 * x];   // blue 
      ptrOutput[4 * x + 1] = ptrInput[4 * x + 1]; // green 
      ptrOutput[4 * x + 2] = ptrInput[4 * x + 2]; // red 
      ptrOutput[4 * x + 3] = ptrMask[4 * x];  // alpha 
     } 
    } 
} 
mask.UnlockBits(bitsMask); 
input.UnlockBits(bitsInput); 
output.UnlockBits(bitsOutput); 

output.Save(...); 

En este ejemplo se deriva el canal alfa en la salida del azul canal la imagen de máscara en . Estoy seguro de que puedes cambiarlo para usar el canal rojo o alfa de la máscara si es necesario.

+0

Todavía tendría que repetir los píxeles, ¿verdad? – Origamiguy

+0

Sí, pero es muy rápido. Cualquier otra implementación que potencialmente podría utilizar tendría igualmente que recorrer los píxeles. – Timwi

+0

Bueno, funciona, y definitivamente es más rápido que mi método anterior, ¡gracias! – Origamiguy

3

Dependiendo de sus necesidades esto puede ser mucho más fácil:

  • invertir la máscara para que el círculo es transparente y el resto es de un color no se utiliza en su mapa de bits de entrada (como el rojo en su ejemplo)
  • Dibuja tu máscara en la parte superior de la imagen con Graphics.FromImage (imagen) .DrawImage (máscara) ...
  • Ajuste el color de la máscara lo más transparente en la imagen (image.MakeTransparent (Color.Red))

El único inconveniente de este método es que necesita asegurarse de que el color de la máscara no se utiliza en su imagen. No sé si esto es más lento o más rápido que el modo manual.

Cuestiones relacionadas