.net 在 WPF 中绘制像素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/443867/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Drawing Pixels in WPF
提问by Jimmy
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 Image control, but I couldn't figure out how to create one (the create method requires a block of unmanaged memory)
我将如何在 WPF 中管理逐像素渲染(例如,对于光线跟踪器)?我最初的猜测是创建一个 BitmapImage,修改缓冲区,并将其显示在 Image 控件中,但我无法弄清楚如何创建一个(create 方法需要一块非托管内存)
回答by
I highly recommend againstthe previous two suggestions. The WriteableBitmap classprovides 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.
我强烈反对前两个建议。假设您使用的是 3.5 SP1 ,WriteableBitmap 类提供了您需要的内容。在 SP1 之前,WPF 中的这个问题没有很好的答案,除非您使用一些严肃的技巧。
回答by Mr Bell
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:
要清理 hBitmap,您需要在类文件的顶部附近声明它:
[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.
另请注意,您需要设置项目属性以允许不安全代码。您可以通过右键单击解决方案资源管理器中的项目并选择属性来执行此操作。转到构建选项卡。选中“允许不安全代码”框。另外我相信您需要添加对 System.Drawing 的引用。
回答by wegrata
you could place 1X1 Rectangle objects onto a Canvas
您可以将 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);
}
That should be pretty close to what you want
那应该非常接近你想要的
回答by Steven Robbins
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:
如果需要,您可以添加小矩形,但每个矩形都是一个 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);
}
回答by reflog
Here's a method using WritableBitmap.
这是一个使用 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
}

