How to bitblit from RenderTargetBitmap to WriteableBitmap? - wpf

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

Related

WPF resizing a canvas doesn't work in dispatcher

I am trying to resize a canvas using the following code but in the dispatcher. It doesn't seems to work am I missing something?
Dispatcher.BeginInvoke(new Action(() => {
canvas.RenderTransform = new ScaleTransform(scale,
scale);
canvas.Measure(new Size(scale * w, scale * h));
canvas.Arrange(new Rect(0, 0, scale * w, scale *
h));
canvas.UpdateLayout();
RenderTargetBitmap rtb = new RenderTargetBitmap(scale * w, scale * h, 96, 96, PixelFormats.Pbgra32);
rtb.Render(canvas);
}), DispatcherPriority.Send);
}
In order to create a bitmap from a Canvas with a scaled size you do not need to resize or scale the Canvas.
Just create a VisualBrush from the Canvas and draw an appropriately sized rectangle with it into a DrawingVisual. Using a DrawingVisual also avoids any potential problems with margins and alignments.
var width = canvas.ActualWidth * scale;
var height = canvas.ActualHeight * scale;
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new VisualBrush(canvas), null, new Rect(0, 0, width, height));
}
var rtb = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Default);
rtb.Render(visual);

WPF Memory Leak using RenderTargetBitmap?

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

Workaround for PictureBox Zoom antialiasing bug?

A PictureBox which has SizeMode.Zoom and enlarges the source image adds white at the bottom and right edges. Anyone have a simple proven workaround? I beleive the bug is in GDI+ so the obvious custom repaint will not fix it. Currently I'm using WPF which I want to avoid.
I'll even settle for a kluge that replaces the bad white with black :)
Example: from a source having single central white pixel, instead of this:
it gives this:
The code is:
private void PictureBoxZoomBug()
{
BitmapSource bitmapSource =
BitmapSource.Create(3, 3, 96, 96, System.Windows.Media.PixelFormats.Gray8, null,
new byte[] { 0, 0, 0, 0, 0xFF, 0, 0, 0, 0 }, 3);
TestDiscamImage.Source = bitmapSource; // OK
Bitmap bitmap;
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
enc.Save(stream);
bitmap = new System.Drawing.Bitmap(stream);
}
TestDiscamCapture_pictureBox.Image = bitmap; // Extra white at bottom and right
}
Thanks

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.

Resources