2009-01-14 99 views
26

我將如何管理WPF中的逐像素渲染(例如,對於光線追蹤器)?我最初的猜測是創建一個BitmapImage,修改緩衝區,並顯示在一個Image控件,但我不知道如何創建一個(創建方法需要一個非管理內存塊)在WPF中繪製像素

回答

37

我強烈建議前兩個建議。假設您使用3.5 SP1,The WriteableBitmap class提供了您需要的內容。在SP1之前,除非你訴諸一些嚴重的欺騙,否則在WPF中這個問題沒有很好的答案。

+7

WriteableBitmap類自.NET 3.0起可用,這是WPF的第一個版本。你不需要.NET 3.5來使用這個類。 – 2011-09-19 01:27:12

7

你可以放置1X1矩形對象到畫布上

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

這應該是相當接近你想要

6

您可以添加小rects如果你想要的,但每一類又是一個FrameworkElement的是什麼,所以它可能是一個有點重量級。另一種選擇是創建自己DrawingVisual,借鑑它,使它然後堅持它的圖像:

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

的最佳解決方案。像魅力一樣工作。只有一件小事: dc.Close();不需要 。使用調用Dispose(),它和Close()一樣。我檢查了WPF源代碼:-) – 2016-01-03 18:12:33

10

如果您正在考慮做類似光線追蹤的事情,您需要繪製很多點,您可能需要直接在位圖的內存空間中繪製點,而不是通過抽象層。這段代碼會給你提供更好的渲染時間,但是它的代價是需要「不安全」的代碼,這可能或可能不適合你。


      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 
      } 

要清理HBITMAP你需要聲明這附近你的類文件的頂部:


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

另外請注意,您將需要設置項目屬性,允許不安全的代碼。您可以通過右鍵單擊解決方案資源管理器中的項目並選擇屬性來完成此操作。轉到生成選項卡。選中「允許不安全的代碼」框。另外我相信你需要添加一個對System.Drawing的引用。

+2

非常感謝這個解決方案。 – 2010-10-13 12:32:15

1

這是一個使用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)); 
} 

,並使用它:

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 
}