Drawing Pixels in WPF

后端 未结 5 893
日久生厌
日久生厌 2020-12-01 06:37

how would I manage pixel-by-pixel rendering in WPF (like, say, for a raytracer)? My initial guess was to create a BitmapImage, modify the buffer, and display that in an Imag

相关标签:
5条回答
  • 2020-12-01 06:55

    You can add small rects if you want, but each of those is a FrameworkElement, so it's probably a bit heavyweight. The other option is to create yourself a DrawingVisual, draw on it, render it then stick it in an image:

    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 讨论(0)
  • 2020-12-01 06:56

    Here's a method using 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));
    }
    

    And to use it:

    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
    }
    
    0 讨论(0)
  • 2020-12-01 06:58

    I highly recommend against the previous two suggestions. The WriteableBitmap class provides what you need assuming you are using 3.5 SP1. Before SP1 there's no really good answer to this question in WPF unless you resort to some serious trickery.

    0 讨论(0)
  • 2020-12-01 06:59

    If you are thinking of doing something like a ray tracer where you will need to be plotting lots of points you will probably want to draw in the bitmap's memory space directly instead of through the layers of abstraction. This code will give you significantly better render times, but it comes at the cost of needing "unsafe" code, which may or may not be an option for you.

    
                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
                }
    

    To clean up the hBitmap you will need to declare this near the top of your class file:

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

    Also note that you will need to set the project properties to allow unsafe code. You can do this by right clicking on the project in solution explorer and selecting properties. Goto the Build tab. Check the "Allow unsafe code" box. Also I believe you will need to add a reference to System.Drawing.

    0 讨论(0)
  • 2020-12-01 07:06

    you could place 1X1 Rectangle objects onto a Canvas

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

    That should be pretty close to what you want

    0 讨论(0)
提交回复
热议问题