2011-04-16 42 views
16

He encontrado un montón de gente que convierte un BitmapSource en un Bitmap, pero ¿qué pasa con ImageSource a Bitmap? Estoy creando un programa de imágenes y necesito extraer mapas de bits de la imagen que se muestra en el elemento Image. ¿Alguien sabe como hacer esto?C# - Convierte WPF Image.source a System.Drawing.Bitmap

EDIT 1:

Esta es una función para convertir la BitmapImage a un Bitmap. Recuerde establecer la opción 'inseguro' en las preferencias del compilador.

public static System.Drawing.Bitmap BitmapSourceToBitmap(BitmapSource srs) 
{ 
    System.Drawing.Bitmap btm = null; 

    int width = srs.PixelWidth; 

    int height = srs.PixelHeight; 

    int stride = width * ((srs.Format.BitsPerPixel + 7)/8); 

    byte[] bits = new byte[height * stride]; 

    srs.CopyPixels(bits, stride, 0); 

    unsafe 
    { 
     fixed (byte* pB = bits) 
     { 
      IntPtr ptr = new IntPtr(pB); 

      btm = new System.Drawing.Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, ptr); 
     } 
    } 
    return btm; 
} 

siguiente es ahora para obtener un BitmapImage:

RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
    (int)inkCanvas1.ActualWidth, 
    (int)inkCanvas1.ActualHeight, 
    96d, 96d, 
    PixelFormats.Default); 

targetBitmap.Render(inkCanvas1); 

MemoryStream mse = new MemoryStream(); 
System.Windows.Media.Imaging.BmpBitmapEncoder mem = new BmpBitmapEncoder(); 
mem.Frames.Add(BitmapFrame.Create(targetBitmap)); 
mem.Save(mse); 

mse.Position = 0; 
BitmapImage bi = new BitmapImage(); 
bi.BeginInit(); 
bi.StreamSource = mse; 
bi.EndInit(); 

siguiente es convertirlo:

Bitmap b = new Bitmap(BitmapSourceToBitmap(bi)); 

Respuesta

21

Actualmente no es necesario que utilice un código no seguro. Hay una sobrecarga de CopyPixels que acepta un IntPtr:

public static System.Drawing.Bitmap BitmapSourceToBitmap2(BitmapSource srs) 
{ 
    int width = srs.PixelWidth; 
    int height = srs.PixelHeight; 
    int stride = width * ((srs.Format.BitsPerPixel + 7)/8); 
    IntPtr ptr = IntPtr.Zero; 
    try 
    { 
     ptr = Marshal.AllocHGlobal(height * stride); 
     srs.CopyPixels(new Int32Rect(0, 0, width, height), ptr, height * stride, stride); 
     using (var btm = new System.Drawing.Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, ptr)) 
     { 
      // Clone the bitmap so that we can dispose it and 
      // release the unmanaged memory at ptr 
      return new System.Drawing.Bitmap(btm); 
     } 
    } 
    finally 
    { 
     if (ptr != IntPtr.Zero) 
      Marshal.FreeHGlobal(ptr); 
    } 
} 
+0

Tenga en cuenta que internamente, este constructor de 'Bitmap' llama' GdipCreateBitmapFromScan0', que espera que el usuario libere la memoria cuando ya no es necesaria ... – Cameron

+0

@Cameron, buen punto. Edité mi respuesta para tener eso en cuenta. –

+0

Desafortunadamente, la memoria no puede liberarse hasta después de que se haya eliminado el System.Drawing.Bitmap (No puedo encontrar ningún documento sobre esto, pero después de probarlo en una aplicación de interoperabilidad, mis imágenes solo aparecen correctamente si gratis después, por lo que no parece que está copiando los datos internamente). Además, los [documentos de GDI] (http://www.jose.it-berater.org/gdiplus/reference/flatapi/bitmap/gdipcreatebitmapfromscan0.htm) parecen requerir que la zancada sea un múltiplo de 4; entonces '(srs.Format.BitsPerPixel + 7)/8' debería ser' (srs.Format.BitsPerPixPeel + 31)/32 * 8', creo. – Cameron

2

son Tu no ImageSource un BitmapSource? Si estás cargando las imágenes de los archivos, deberían estarlo.

respuesta a su comentario:

Suena como que deberían ser BitmapSource entonces, BitmapSource es un subtipo de ImageSource. Transmite ImageSource a BitmapSource y sigue uno de esos blogs.

+0

No, dice que son ImageSources. Los estoy cargando desde BitmapIamges convertidos desde Bitmaps. – user646265

+0

Gracias, he encontrado un método que creo funciona bien. He publicado una edición de mi pregunta. – user646265

+0

@SimonStenderBoisen - un 'ImageSource' podría ser del archivo' XAML' en lugar de un archivo 'Bitmap'. Si es así, su clase subyacente podría ser 'DrawingBrush' y necesitaría convertirse para obtener un' Bitmap' real. –

1

No es necesario un método BitmapSourceToBitmap en absoluto. Sólo haga lo siguiente después de crear la secuencia de memoria:

mem.Position = 0; 
Bitmap b = new Bitmap(mem); 
2

Ese ejemplo me funcionó:

public static Bitmap ConvertToBitmap(BitmapSource bitmapSource) 
    { 
     var width = bitmapSource.PixelWidth; 
     var height = bitmapSource.PixelHeight; 
     var stride = width * ((bitmapSource.Format.BitsPerPixel + 7)/8); 
     var memoryBlockPointer = Marshal.AllocHGlobal(height * stride); 
     bitmapSource.CopyPixels(new Int32Rect(0, 0, width, height), memoryBlockPointer, height * stride, stride); 
     var bitmap = new Bitmap(width, height, stride, PixelFormat.Format32bppPArgb, memoryBlockPointer); 
     return bitmap; 
    }