获取用于 wpf WriteableBitmap 的 DrawingContext

23

有没有一种方法可以为WriteableBitmap获取一个DrawingContext(或类似的东西)? 也就是说,有一些简单的方法(如DrawLine/DrawRectangle/等),而不是直接操作原始像素。

5个回答

22

我认为sixlettervariables的解决方案最可行。但是缺少一个 "drawingContext.Close()" 语句。根据MSDN的说明,"在呈现其内容之前,必须关闭DrawingContext"。下面是相应的实用函数:

public static BitmapSource CreateBitmap(
    int width, int height, double dpi, Action<DrawingContext> render)
{
    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        render(drawingContext);
    }
    RenderTargetBitmap bitmap = new RenderTargetBitmap(
        width, height, dpi, dpi, PixelFormats.Default);
    bitmap.Render(drawingVisual);

    return bitmap;
}
这样就很容易使用了:
BitmapSource image = ImageTools.CreateBitmap(
    320, 240, 96,
    drawingContext =>
    {
        drawingContext.DrawRectangle(
            Brushes.Green, null, new Rect(50, 50, 200, 100));
        drawingContext.DrawLine(
            new Pen(Brushes.White, 2), new Point(0, 0), new Point(320, 240));
    });

5
.Close()方法在.Dispose()方法中隐含了,这也是using语句的全部意义所在。如果你只在using块内留下render(...)命令,就不需要加任何.Close()操作。 - Eamon Nerbonne
4
为什么这个答案中没有“WriteableBitmap”,但却是排名第一呢?它完全与问题无关。 - Roman Starkov
6
我猜测它被点赞是因为原问题可能类似于问:“我该如何用这个锤子拧这个螺丝?”而答案是“使用螺丝刀”,而不是如何使用锤子来完成它。 - codekaizen
如果您想从中创建WritableBitmap,只需调用:WritableBitmap result = new WritableBitmap(image); - vac

18

如果您不介意使用System.Drawing,您可以执行以下操作:

var wb = new WriteableBitmap( width, height, dpi, dpi, 
                              PixelFormats.Pbgra32, null );
wb.Lock();
var bmp = new System.Drawing.Bitmap( wb.PixelWidth, wb.PixelHeight,
                                     wb.BackBufferStride, 
                                     PixelFormat.Format32bppPArgb, 
                                     wb.BackBuffer );

Graphics g = System.Drawing.Graphics.FromImage( bmp ); // Good old Graphics

g.DrawLine( ... ); // etc...

// ...and finally:
g.Dispose(); 
bmp.Dispose();
wb.AddDirtyRect( ... );
wb.Unlock();                    

我还没有试过,但这似乎是WPF中一个非常合理的解决方法。谢谢! - Emperor XLII
4
万一有人想知道,我测试了这个话题中提到的DrawingVisual WPF方法和System.Drawing方法,而这个System.Drawing方法要快得多。我对WPF感到非常失望。 - Verax
1
你可以使用RenderTargetBitmap和DrawingVisual / DrawingContext在WPF中完成相同的事情,并获得类似(或更好?)的性能。 - David Jeske

5

我也在想同样的问题,因为目前我做的事情类似于:

DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
   //
   // ... draw on the drawingContext
   //
   RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, dpi, dpi, PixelFormats.Default);
   bmp.Render(drawingVisual);
   image.Source = bmp;
}

我正在尝试使用WriteableBitmap来允许多线程访问像素缓冲区,但是使用DrawingContext或RenderTargetBitmap都不允许。也许可以基于从RenderTargetBitmap检索到的内容编写一些WritePixels例程?

渲染花费太长时间。 - lindexi
你不能使用该方法写入bmp.Render,因为无法获取drawingVisual。 - lindexi
如果您在线程之间共享位图,则可能需要使用bmp.Freeze() - fadden

4

看起来这个词不存在


为了以后的参考,我们计划使用WPF的可写位图扩展的端口。

对于只使用现有代码的解决方案,以下任何其他建议都可以工作。


1
那不是官方的说法,只是一些人在论坛上说的话。 - Roman Starkov
明白了,已更新的文字。我想你可以理解为它的意思是“官方的”,就像“发布在由官方监控的论坛上,并未经过他们纠正”,但这有点牵强 :) - Emperor XLII

1
另一种解决此问题的方法是使用RenderTargetBitmap作为备份存储,就像在WriteableBitmap示例中一样。然后,您可以在需要时创建和发出WPF绘图命令。例如:
// create the backing store in a constructor
var backingStore = 
      new RenderTargetBitmap(200,200,97,97,PixelFormats.Pbgra32);
myImage.Source = backingStore;

// whenever you want to update the bitmap, do:
var drawingVisual = new DrawingVisual();
var drawingContext = drawingVisual.RenderOpen();
{
    // your drawing commands go here
    drawingContext.DrawRectangle(
            Brushes.Red, new Pen(),
            new Rect(this.RenderSize));
}
Render(drawingContext);
drawingContext.Close();
backingStore.Render(drawingVisual);

如果您想每一帧都重新绘制这个RenderTargetBitmap,您可以像这样捕获CompositionTarget.Rendering事件:
CompositionTarget.Rendering += MyRenderingHandler;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接