【问题标题】:Getting a DrawingContext for a wpf WriteableBitmap获取 wpf WriteableBitmap 的 DrawingContext
【发布时间】:2010-09-10 10:48:17
【问题描述】:

有没有办法为WriteableBitmap 获取DrawingContext(或类似的东西)? IE。可以让你调用简单的DrawLine/DrawRectangle/等方法,而不是直接操作原始像素。

【问题讨论】:

    标签: wpf bitmap drawing


    【解决方案1】:

    我发现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));
        });
    

    【讨论】:

    • .Close() 隐含在.Dispose() 中——这是using 语句背后的全部要点。如果您将 render(...) 命令留在 using 块中,您会没事的,不需要任何 .Close()
    • 这个答案中没有WriteableBitmap,为什么它是第一位的? :) 这与问题完全无关。
    • 我的猜测是它被赞成了,因为最初的问题可能类似于问“我如何用这把锤子转动这个螺丝?”而答案是“使用螺丝刀”而不是如何使用锤子来做。
    • 如果你想从中制作 WritableBitmap,你可以简单地调用:WritaBleBitmap result = new WritableBitmap(image);
    【解决方案2】:

    如果您不介意使用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 中非常合理的解决方法。谢谢!
    • 以防万一有人想知道,我测试了本主题中提到的 DrawingVisual WPF 方法和这个 System.Drawing 方法,这个 System.Drawing 方法要快得多。我对 WPF 非常失望。
    • 你可以用 WPF 做同样的事情,通过使用 RenderTargetBitmap 和 DrawingVisual / DrawingContext 获得相似(或更好?)的性能
    【解决方案3】:

    我想知道同样的事情,因为目前我正在做类似的事情:

    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 例程会起作用?

    【讨论】:

    • 渲染花费太长
    • 你不能写bmp.Render,用那个不能得到drawingVisual。
    • 如果你在线程之间共享位图,你可能需要bmp.Freeze()
    【解决方案4】:

    出现the word is no


    为了将来参考,我们计划为 WPF 使用Writeable Bitmap Extensions 的端口。

    对于使用纯现有代码的解决方案,下面提到的任何其他建议都可以使用。

    【讨论】:

    • 这不是官方说法。这只是某个人在论坛上说的话。
    • 取点;更新的文本。我想你可以说它的意思是“官方”,就像“发布到由官员监控的论坛,而不是被他们纠正”,但这有点牵强 :)
    【解决方案5】:

    解决此问题的另一种方法是使用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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 2023-03-24
      相关资源
      最近更新 更多