2009-01-14 11 views
26

¿cómo gestionaría la representación píxel por píxel en WPF (como, por ejemplo, para un raytracer)? Mi conjetura inicial fue crear un BitmapImage, modificar el búfer y mostrarlo en un control de imagen, pero no pude encontrar cómo crear uno (el método create requiere un bloque de memoria no administrada)Píxeles de dibujo en WPF

Respuesta

37

le recomiendo contra las dos sugerencias anteriores. The WriteableBitmap class proporciona lo que necesita suponiendo que está utilizando 3.5 SP1. Antes de SP1 no hay una respuesta realmente buena a esta pregunta en WPF a menos que recurras a algún truco serio.

+7

La clase WriteableBitmap está disponible desde .NET 3.0 y esta es la primera versión de WPF. No necesita .NET 3.5 para usar esta clase. –

7

podrías colocar 1X1 rectángulo objetos en un lienzo

private void AddPixel(double x, double y) 
    { 
    Rectangle rec = new Rectangle(); 
    Canvas.SetTop(rec, y); 
    Canvas.SetLeft(rec, x); 
    rec.Width = 1; 
    rec.Height = 1; 
    rec.Fill = new SolidColorBrush(Colors.Red); 
    myCanvas.Children.Add(rec); 
    } 

que deberían estar bastante cerca de lo que desea

6

se pueden añadir pequeñas rectas si lo desea, pero cada uno de ellos es un FrameworkElement, por lo que es probable que un peso pesado poco. La otra opción es crear usted mismo un DrawingVisual, dibujar en ella, hacerla a continuación pegarlo en una imagen:

private void DrawRubbish() 
{ 
    DrawingVisual dv = new DrawingVisual(); 
    using (DrawingContext dc = dv.RenderOpen()) 
    { 
     Random rand = new Random(); 

     for (int i = 0; i < 200; i++) 
      dc.DrawRectangle(Brushes.Red, null, new Rect(rand.NextDouble() * 200, rand.NextDouble() * 200, 1, 1)); 

     dc.Close(); 
    } 
    RenderTargetBitmap rtb = new RenderTargetBitmap(200, 200, 96, 96, PixelFormats.Pbgra32); 
    rtb.Render(dv); 
    Image img = new Image(); 
    img.Source = rtb; 
    MainGrid.Children.Add(img); 
} 
+0

gran solución. Trabajado como un encanto. Solo una pequeña cosa: dc.Close(); no es necesario. Usando llamadas Dispose(), que está haciendo lo mismo que Close(). Comprobé el código fuente de WPF :-) –

10

Si está pensando en hacer algo así como un rastreador de rayos en el que necesitará trazar muchos puntos, probablemente querrá dibujar en el espacio de memoria del mapa de bits directamente en lugar de atravesar las capas de abstracción. Este código le dará tiempos de renderización significativamente mejores, pero tiene el costo de necesitar un código "inseguro", que puede o no ser una opción para usted.


      unsafe 
      { 
       System.Drawing.Point point = new System.Drawing.Point(320, 240); 
       IntPtr hBmp; 
       Bitmap bmp = new Bitmap(640, 480); 
       Rectangle lockRegion = new Rectangle(0, 0, 640, 480); 
       BitmapData data = bmp.LockBits(lockRegion, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
       byte* p; 

       p = (byte*)data.Scan0 + (point.Y * data.Stride) + (point.X * 3); 
       p[0] = 0; //B pixel 
       p[1] = 255; //G pixel 
       p[2] = 255; //R pixel 

       bmp.UnlockBits(data); 

       //Convert the bitmap to BitmapSource for use with WPF controls 
       hBmp = bmp.GetHbitmap(); 
       Canvas.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbmpCanvas, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); 
       Canvas.Source.Freeze(); 
       DeleteObject(hBmp); //Clean up original bitmap 
      } 

Para limpiar el hBitmap tendrá que declarar esta cerca de la parte superior de su archivo de clase:


     [System.Runtime.InteropServices.DllImport("gdi32.dll")] 
     public static extern bool DeleteObject(IntPtr hObject); 

También tenga en cuenta que tendrá que establecer las propiedades del proyecto para permitir código no seguro. Puede hacer esto haciendo clic derecho en el proyecto en el explorador de soluciones y seleccionando propiedades. Ir a la pestaña Construir Marca la casilla "Permitir código no seguro". También creo que deberá agregar una referencia a System.Drawing.

+2

Muchas gracias por esta solución. –

1

Aquí hay un método que usa WritableBitmap.

public void DrawRectangle(WriteableBitmap writeableBitmap, int left, int top, int width, int height, Color color) 
{ 
    // Compute the pixel's color 
    int colorData = color.R << 16; // R 
    colorData |= color.G << 8; // G 
    colorData |= color.B << 0; // B 
    int bpp = writeableBitmap.Format.BitsPerPixel/8; 

    unsafe 
    { 
     for (int y = 0; y < height; y++) 
     { 
      // Get a pointer to the back buffer 
      int pBackBuffer = (int)writeableBitmap.BackBuffer; 

      // Find the address of the pixel to draw 
      pBackBuffer += (top + y) * writeableBitmap.BackBufferStride; 
      pBackBuffer += left * bpp; 

      for (int x = 0; x < width; x++) 
      { 
       // Assign the color data to the pixel 
       *((int*)pBackBuffer) = colorData; 

       // Increment the address of the pixel to draw 
       pBackBuffer += bpp; 
      } 
     } 
    } 

    writeableBitmap.AddDirtyRect(new Int32Rect(left, top, width, height)); 
} 

y utilizarla:

private WriteableBitmap bitmap = new WriteableBitmap(1100, 1100, 96d, 96d, PixelFormats.Bgr24, null); 

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    int size = 10; 

    Random rnd = new Random(DateTime.Now.Millisecond); 
    bitmap.Lock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests. 

    for (int y = 0; y < 99; y++) 
    { 
     for (int x = 0; x < 99; x++) 
     { 
      byte colR = (byte)rnd.Next(256); 
      byte colG = (byte)rnd.Next(256); 
      byte colB = (byte)rnd.Next(256); 

      DrawRectangle(bitmap, (size + 1) * x, (size + 1) * y, size, size, Color.FromRgb(colR, colG, colB)); 
     } 
    } 

    bitmap.Unlock(); // Lock() and Unlock() could be moved to the DrawRectangle() method. Just do some performance tests. 

    image.Source = bitmap; // This should be done only once 
}