Getting a DrawingContext for a wpf WriteableBitmap
Asked Answered
C

5

23

Is there a way to get a DrawingContext (or something similar) for a WriteableBitmap? I.e. something to allow you to call simple DrawLine/DrawRectangle/etc kinds of methods, rather than manipulate the raw pixels directly.

Cowitch answered 17/9, 2008 at 22:53 Comment(0)
C
4

It appears the word is no.


For future reference, we plan to use a port of the Writeable Bitmap Extensions for WPF.

For a solution using purely existing code, any of the other suggestions mentioned below will work.

Cowitch answered 17/9, 2008 at 22:53 Comment(2)
That's no official word. That's just some guy saying something on a forum.Hirsch
Point taken; updated text. I suppose you could say it meant "official" as in "posted to a forum monitored by officials, and not corrected by them", but that is a bit of a stretch :)Cowitch
R
22

I found sixlettervariables' solution the most workable one. However, there's a "drawingContext.Close()" missing. According to MSDN, "A DrawingContext must be closed before its content can be rendered". The result is the following utility function:

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

This can then easily be used like this:

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));
    });
Route answered 15/5, 2009 at 16:54 Comment(4)
The .Close() is implicit in the .Dispose() - which is the whole point behind the using statement. If you leave only the render(...) command inside the using block, you'll be fine and don't need any .Close().Mauramauralia
There's no WriteableBitmap in this answer, why is it the top one? :) It's completely unrelated to the question.Hirsch
My guess is that it's upvoted because the original question may be akin to asking "How do I turn this screw with this hammer?" and the answer is "Use a screwdriver" instead of how to use the hammer to do it.Obscenity
if you want to make WritableBitmap from it you can simply call: WritaBleBitmap result = new WritableBitmap(image);Tele
V
18

If you don't mind using System.Drawing you could do something like:

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();                    
Vernettaverneuil answered 28/4, 2009 at 12:0 Comment(3)
I haven't had a chance to try it out just yet, but this seems like a very reasonable workaround in WPF. Thanks!Cowitch
Just in case anyone is wondering, I tested the DrawingVisual WPF method mentioned in this topic and this System.Drawing method, and this System.Drawing method is MUCH FASTER. I'm very disappointed in WPF.Cleaver
You can do the same thing with WPF, with similar (or better?) performance by using RenderTargetBitmap and DrawingVisual / DrawingContextShanks
E
5

I'm wondering the same thing, as currently I do something like:

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

I'm trying to use the WriteableBitmap to allow multithreaded access to the pixel buffer, which is currently not allowed with neither a DrawingContext nor a RenderTargetBitmap. Maybe some sort of WritePixels routine based off of what you've retrieved from the RenderTargetBitmap would work?

Emelda answered 17/9, 2008 at 23:2 Comment(3)
Render spend too longHighams
You cant write the bmp.Render in using that cant get the drawingVisual.Highams
You may need to bmp.Freeze() if you're sharing the bitmap between threads.Perrie
C
4

It appears the word is no.


For future reference, we plan to use a port of the Writeable Bitmap Extensions for WPF.

For a solution using purely existing code, any of the other suggestions mentioned below will work.

Cowitch answered 17/9, 2008 at 22:53 Comment(2)
That's no official word. That's just some guy saying something on a forum.Hirsch
Point taken; updated text. I suppose you could say it meant "official" as in "posted to a forum monitored by officials, and not corrected by them", but that is a bit of a stretch :)Cowitch
S
1

A different way to solve this problem is to use a RenderTargetBitmap as a backing store, just like in the WriteableBitmap example. Then you can create and issue WPF drawing commands to it whenever you want. For example:

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

If you want to redraw this RenderTargetBitmap every frame, you can catch the CompositionTarget.Rendering event, like this:

CompositionTarget.Rendering += MyRenderingHandler;
Shanks answered 7/6, 2017 at 23:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.