using GDI+ in WPF application - wpf

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.

Related

How to programatically resize a DrawingVisual?

So, I'm new to WPF Drawing. For performance reasons, I've had to switch from regular controls like ContentControl and UserControl to more light-weight elements like DrawingVisual. I am working on a diagramming app which would probably have a max of 1000 elements on the canvas that can be dragged, resized and such. Firstly, is it better to use DrawingVisual instead of Shape?
Secondly, my main question here. I am adding DrawingVisual elements to the Canvas as such:
public class SVisualContainer : UIElement
{
// Create a collection of child visual objects.
private VisualCollection _children;
public SVisualContainer()
{
_children = new VisualCollection(this);
_children.Add(CreateDrawingVisualRectangle());
}
// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, null, rect);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
// Provide a required override for the VisualChildrenCount property.
protected override int VisualChildrenCount
{
get { return _children.Count; }
}
// Provide a required override for the GetVisualChild method.
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count)
{
throw new ArgumentOutOfRangeException();
}
return _children[index];
}
}
And within the canvas:
public void AddStateVisual()
{
var sVisual = new SVisualContainer();
Children.Add(sVisual);
Canvas.SetLeft(sVisual, 10);
Canvas.SetTop(sVisual, 10);
}
How can I increase the size of the Rectangle dynamically through code? I have tried setting the Height and Width of the Rectangle which did not work, played around with the ScaleTransform but that is probably not what I want. Would I need to redraw the Rectangle? Thanks!
I ended up using DrawingVisual within UIElement as shown in the question, and continuously redrawing the DrawingVisual upon resize. The UIElement.RenderSize property, UIElement.MeasureCore method and UIElement.InvalidateMeasure method are central to this. This works quite well and the performance is acceptable.

Unable to present a BitmapSource properly

I have a program which generates a System.Drawing.Bitmap and then uses it to create a BitmapSource. I then assign the CurrentImage property of a WPF Image to the BitmapSource I create.
And here is the problem: no matter what I set the DPI values of the BitmapSource to, I end up with a grainy picture on my 4K display.
The piece of XAML I'm using is:
<Image Source="{Binding Path=CurrentImage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SnapsToDevicePixels="True"/>
and the code is as follows:
private static void SetBitmap(FreeformEditor fe, Bitmap bmp)
{
if (fe.lastBitmap != IntPtr.Zero)
DeleteObject(fe.lastBitmap);
try
{
var data = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly, bmp.PixelFormat);
var dpi = baseDPI * (1 + fe.RenderForRetina.ToInt());
fe.CurrentImage = BitmapSource.Create(
data.Width, data.Height, dpi, dpi, PixelFormats.Bgr32, null,
data.Scan0, data.Stride * data.Height, data.Stride);
bmp.UnlockBits(data);
fe.lastBitmap = bmp.GetHbitmap();
fe.imageSizeRun.Content = string.Format("{0}x{1} #{2}dpi", fe.CurrentImage.Width,
fe.CurrentImage.Height,
fe.CurrentImage.DpiX);
}
catch
{
// this happens due to framework bugs
}
}

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

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

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