2010-09-06 4 views
17

sto cercando di disegnare un'immagine, con una fonte di Bitmap e mascherare un alfa Bitmap, utilizzando l'oggetto System.Drawing.Graphics. Al ciclo momento che X e Y e utilizzando GetPixel e SetPixel per scrivere colore sorgente e maschera alfa ad un terzo Bitmap, e quindi rendere tale. Tuttavia questo è molto inefficiente e mi chiedo se c'è un modo più veloce per raggiungere questo obiettivo?Mascheratura alfa in C# System.Drawing?

L'effetto che sto cercando si presenta così:

Effect I’m after

Il modello di griglia rappresenta la trasparenza; probabilmente lo sapevi.

+2

Dal momento che si dice che si sta utilizzando System.Drawing.Graphics, questo è probabilmente off-topic, ma ecco un link per maschere di opacità in WPF, che potranno usufruire di accelerazione hardware: http://msdn.microsoft.com/ en-us/library/system.windows.media.drawinggroup.opacitymask.aspx – Douglas

+0

off-topic, ma comunque affascinante: grazie! – Origamiguy

risposta

15

Sì, il modo più veloce per farlo è utilizzare Bitmap.LockBits e utilizzare l'aritmetica del puntatore per recuperare i valori anziché GetPixel e SetPixel. Lo svantaggio, ovviamente, è che devi usare un codice non sicuro; se commetti un errore, puoi causare alcuni crash davvero brutti nel tuo programma. Ma se lo si mantiene semplice e autonomo, dovrebbe andare bene (hey, se posso farlo, puoi farlo anche tu).

Ad esempio, si potrebbe fare qualcosa di simile (non testato, l'uso a proprio rischio):

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

Questo esempio deriva il canale alfa in uscita dal blu canale l'immagine maschera . Sono sicuro che puoi cambiarlo per usare il canale rosso o alfa della maschera, se necessario.

+0

Dovrei comunque ripetere i pixel, giusto? – Origamiguy

+0

Sì, ma è molto veloce. Qualsiasi altra implementazione che potresti potenzialmente utilizzare dovrebbe ugualmente iterare attraverso i pixel. – Timwi

+0

Beh, funziona, ed è decisamente più veloce del mio metodo precedente, grazie! – Origamiguy

3

seconda dei requisiti puo essere molto più facile:

  • invertire la maschera in modo che il cerchio è trasparente e il resto è da un colore non utilizzato nella bitmap ingresso (come il rosso nel tuo esempio)
  • Disegna la maschera in cima alla vostra immagine utilizzando Graphics.FromImage (immagine) .DrawImage (maschera) ...
  • Impostare il colore della maschera trasparente sull'immagine (image.MakeTransparent (Color.Red))

L'unico inconveniente di questo metodo è che ha bisogno di assicurarsi che il colore della maschera non è usato nella vostra immagine. Non so se questo è più lento o più veloce del modo manuale.