WPF Memory Leak using RenderTargetBitmap? - wpf

I'm a little baffled by a memory leak in my WPF code. I'm rendering some 3D geometry to several RenderTargetBitmaps, then rendering each of those to a large, master RenderTargetBitmap. But when I do this, I get a memory leak that crashes my app after just a minute or two.
I've reproduced the error in the following simplified piece of code.
private void timer1_Tick(object sender, EventArgs e) {
// if first time, create final stitch bitmap and set UI image source
if (stitch == null) {
stitch = new RenderTargetBitmap(1280, 480, 96, 96, PixelFormats.Pbgra32);
myImage.Source = stitch;
}
// create visual and render to img1
Rect rect = new Rect(new Point(160, 100), new Size(320, 80));
DrawingVisual dvis = new DrawingVisual();
using (DrawingContext dc = dvis.RenderOpen()) {
dc.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
}
RenderTargetBitmap img1 = new RenderTargetBitmap(640, 480, 96, 96, PixelFormats.Pbgra32);
img1.Render(dvis);
// create visual and render to final stitch
DrawingVisual vis = new DrawingVisual();
using (DrawingContext dc = vis.RenderOpen()) {
dc.DrawImage(img1, new Rect(0, 0, 640, 480));
}
stitch.Clear();
stitch.Render(vis);
}
Can anyone see anything obvious that is going wrong here? Why would this code have an egregious memory leak?

if you monitor behaviors of the RenderTargetBitmap class using Resource Monitor, you can see each time this class called, you lose 500KB of your memory. my Answer to your Question is: Dont use RenderTargetBitmap class so many times
You cant even release the Used Memory of RenderTargetBitmap.
If you really need using RenderTargetBitmap class, just add these lines at End of your code.
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
This maybe solve your problem:
private void timer1_Tick(object sender, EventArgs e) {
// if first time, create final stitch bitmap and set UI image source
if (stitch == null) {
stitch = new RenderTargetBitmap(1280, 480, 96, 96, PixelFormats.Pbgra32);
myImage.Source = stitch;
}
// create visual and render to img1
Rect rect = new Rect(new Point(160, 100), new Size(320, 80));
DrawingVisual dvis = new DrawingVisual();
using (DrawingContext dc = dvis.RenderOpen()) {
dc.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
}
RenderTargetBitmap img1 = new RenderTargetBitmap(640, 480, 96, 96, PixelFormats.Pbgra32);
img1.Render(dvis);
// create visual and render to final stitch
DrawingVisual vis = new DrawingVisual();
using (DrawingContext dc = vis.RenderOpen()) {
dc.DrawImage(img1, new Rect(0, 0, 640, 480));
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
stitch.Clear();
stitch.Render(vis);
}

Related

How to convert System.Windows.Media.DrawingImage into Stream?

I'am trying convert DrawingImage into MemoryStream. My code looks like this:
public MemoryStream ImageStream(DrawingImage drawingImage)
{
MemoryStream stream = new MemoryStream();
ImageSource imageSource = drawingImage;
if (imageSource != null)
{
BitmapSource bitmap = imageSource as BitmapSource;
if (bitmap != null)
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream);
}
}
return stream;
}
But problem is after casting ImageSource into BitmapSource bitmap is always null. Any sugestion how to fix that?
The reason your bitmap variable is always null is because DrawingImage does not extend BitmapImage or vice-versa, so the cast is guaranteed to fail. A DrawingImage does not contain any pixel data of any kind. It references a Drawing that is used whenever the image needs to be rasterized.
How did you find yourself in a situation where you want to rasterize a DrawingImage and serialize it into a stream? I get the feeling you are going about something in an unusual way if you have need of a function like this.
Nevertheless, you could implement this function by drawing the DrawingImage to a DrawingVisual, rendering it to a RenderTargetBitmap, and then passing the render target to the encoder to serialize the raster data to a stream.
public MemoryStream ImageStream(DrawingImage drawingImage)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawDrawing(drawingImage.Drawing);
dc.Close();
}
RenderTargetBitmap target = new RenderTargetBitmap((int)visual.Drawing.Bounds.Right, (int)visual.Drawing.Bounds.Bottom, 96.0, 96.0, PixelFormats.Pbgra32);
target.Render(visual);
MemoryStream stream = new MemoryStream();
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(target));
encoder.Save(stream);
return stream;
}
If you want something a little more generic, I would split this into two methods and change some of the types.
public BitmapSource Rasterize(Drawing drawing)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawDrawing(drawing);
dc.Close();
}
RenderTargetBitmap target = new RenderTargetBitmap((int)drawing.Bounds.Right, (int)drawing.Bounds.Bottom, 96.0, 96.0, PixelFormats.Pbgra32);
target.Render(visual);
return target;
}
public void SavePng(BitmapSource source, Stream target)
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(target);
}
Then you could use it with any kind of stream. For example, to save the drawing to a file:
using (FileStream file = File.Create("somepath.png"))
{
SavePng(Rasterize(drawingImage.Drawing), file);
}

wpf image repaint in a loop

Good evening residents =)
I'm drawing and then calling sleep in a cycle, but image control displays only the last instance of my painting and only when i exit the loop.
What should be done to fix it? A piece of code:
while (true)
{
...
if (TMax < T || TMin < T)
{
break;
}
UpdatePoints();
...
System.Threading.Thread.Sleep(500);
}
private void UpdatePoints()
{
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
...
dc.Close();
}
RenderTargetBitmap rtb = new RenderTargetBitmap(1000, 1000, 96, 96, PixelFormats.Pbgra32);
rtb.Render(dv);
critPoints.Source = rtb;
}
Create timer and use timer action to draw your picture, it doesn't work because your thread doesn't release when you use Sleep method, it is also interop your self and can't continue execution you program and repaint.

How do I convert from a Brush (e.g. DrawingBrush) to a BitmapSource?

I have a DrawingBrush with some vector graphics. I want to convert it to BitmapSource as an intermediate step to getting it to Bitmap. What's the (best) way to do this?
public static BitmapSource BitmapSourceFromBrush(Brush drawingBrush, int size = 32, int dpi = 96)
{
// RenderTargetBitmap = builds a bitmap rendering of a visual
var pixelFormat = PixelFormats.Pbgra32;
RenderTargetBitmap rtb = new RenderTargetBitmap(size, size, dpi, dpi, pixelFormat);
// Drawing visual allows us to compose graphic drawing parts into a visual to render
var drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
// Declaring drawing a rectangle using the input brush to fill up the visual
context.DrawRectangle(drawingBrush, null, new Rect(0, 0, size, size));
}
// Actually rendering the bitmap
rtb.Render(drawingVisual);
return rtb;
}

using GDI+ in WPF application

I'm writing a program in WPF application that simulates the game of life.
How can I preform GDI+ like graphics operations to create an Image that contains the grid of cells?
(Normally, in WinForms, I would have know how to do this operation).
Edit:
I used this code:
WriteableBitmap wb = new WriteableBitmap(width * 5, height * 5, 100, 100, new PixelFormat(), new BitmapPalette(new List<Color> { Color.FromArgb(255, 255, 0, 0) }));
wb.WritePixels(new Int32Rect(0, 0, 5, 5), new IntPtr(), 3, 3);
Background.Source = wb;
Background is a System.Windows.Controls.Image Control
You could use a WriteableBitmap or use a WPF container such as a Grid or Canvas with a lot of rectangles in it. A lot depends on the size of the gameboard. A WriteableBitmap might be better suited for a huge map and a canvas or grid might be easier for smaller sizes.
Is this what you are looking for?
I think you're making things harder on yourself by using WriteableBitmap.WritePixel. You'll have a much better time drawing with Shapes or using RendterTargetBitmap and a DeviceContext.
Here's some code on how you would draw using this method.
MainForm's XAML:
<Grid>
<Image Name="Background"
Width="200"
Height="200"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Grid>
MainForm's Code-Behind:
private RenderTargetBitmap buffer;
private DrawingVisual drawingVisual = new DrawingVisual();
public MainWindow()
{
InitializeComponent();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
buffer = new RenderTargetBitmap((int)Background.Width, (int)Background.Height, 96, 96, PixelFormats.Pbgra32);
Background.Source = buffer;
DrawStuff();
}
private void DrawStuff()
{
if (buffer == null)
return;
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(new SolidColorBrush(Colors.Red), null, new Rect(0, 0, 10, 10));
}
buffer.Render(drawingVisual);
}
Adjust the Width/Height of the Image to whatever you desire. All of your drawing logic should be inside of the using statement. You'll find the methods on DrawingContext are much more flexible and easier to understand than WritePixel. Call "DrawStuff" whenever you want to trigger a redraw.

How to bitblit from RenderTargetBitmap to WriteableBitmap?

I'm rendering dozens of visuals to the RenderTargetBitmap. Each is rendered in it's own Rect.
What I want to do is to copy one of these Rect areas rendered from RenderTargetBitmap instance into the same area of the WriteableBitmap...Fast copy rect pixels or smth. like that.
So, is there a way to copy rect from RenderTargetBitmap to WriteableBitmap in a fast way?
Solved by copying the whole RenderTargetBitmap to WriteableBitmap like this:
protected override void OnRender(DrawingContext drawingContext)
{
if (ActualWidth == 0 || ActualHeight == 0) return;
// Create destination bitmap
wb = new WriteableBitmap((int) ActualWidth, (int) ActualHeight, 96, 96, PixelFormats.Pbgra32, null);
wb.Lock();
rtb = new RenderTargetBitmap(wb.PixelWidth, wb.PixelHeight, wb.DpiX, wb.DpiY, PixelFormats.Pbgra32);
foreach (MyVisual visual in visuals)
{
visual.Render(rtb);
}
rtb.CopyPixels(new Int32Rect(0,0, rtb.PixelWidth, rtb.PixelHeight),
wb.BackBuffer,
wb.BackBufferStride * wb.PixelHeight, wb.BackBufferStride);
wb.AddDirtyRect(new Int32Rect(0, 0, (int)ActualWidth, (int)ActualHeight));
wb.Unlock();
drawingContext.DrawImage(wb, new Rect(0, 0, ActualWidth, ActualHeight));
}

Resources