Currently I have an image on a canvas that can I freely move around in my application on which I have 6 layers of DrawingVisuals rendered, but it seems to be pretty slow. I'm using a RenderTargetBitmap to render the visuals on. Is there a faster way to display the visuals on the image or on any other framework element I can freely move on the canvas?
xaml:
<Canvas>
<Image Height="505" HorizontalAlignment="Left" Margin="0,0,0,0" Name="_MapImage" Stretch="Fill" VerticalAlignment="Top" Width="700" MouseLeftButtonDown="_MapImage_MouseDown" MouseWheel="_MapImage_MouseWheel" MouseLeftButtonUp="_MapImage_MouseUp" MouseMove="_MapImage_MouseMove" />
</Canvas>
code:
_renderRoutesBitmap = new RenderTargetBitmap((int)(_MapImage.Width), (int)(_MapImage.Height), 96, 96, PixelFormats.Default);
for (int i = 6; i < 8; ++i)
{
if((layerCode / (int)Math.Pow(10,i) % 2) == 1)
_renderRoutesBitmap.Render(_layers[i]); //takes too much time
}
_RouteImage.Source = _renderRoutesBitmap;
I had to do a similar thing some time ago where I had to write my own GIS application. I had to draw thousands and thousands of drawing visuals, my findings were that RenderTargetBitmap is not a good choice for bitmap operations as it suffers from not using graphical hardware acceleration.
Silverlight has a more suitable class; WriteableBitmap which allows your application to directly write into the GPU buffer. The only issue with this is that it is only available for Silverlight. If you want to stick to using a bitmap for your operations then you can use the equivalent of WriteableBitmap for WPF which is WriteableBitmapEx available here.
As you have only 6 drawing visuals in total, I suggest you move to using a higher level UI element such as Shapes and such.
Display Visual objects using Render is not a good solution. If you have a Canvas and you want to render your visual you don't need to convert visuals into a bitmap, also if you if you make the conversion your image cannot be scaled by any amount without degrading quality (feature of the vector graphics). There is another possibility: create your own Canvas. I currently use this method, and have no problem to draw thousands of shapes. This is a very simple example:
public class DrawingCanvas : Panel
{
public List<Visual> visuals = new List<Visual>();
public void AddVisual(Visual visual)
{
if (visual == null)
return;
this.visuals.Add(visual);
base.AddVisualChild(visual);
base.AddLogicalChild(visual);
}
public void RemoveVisual(Visual visual)
{
if (visual == null)
return;
this.visuals.Remove(visual);
base.RemoveVisualChild(visual);
base.RemoveLogicalChild(visual);
}
}
Use DrawingCanvas instead of Canvas class.
Related
I'm experimenting with WritableBitmapEx on Windows Phone. I created a simple example, a simple box moving up and down.
There's a draw function which redraws the rectangle each frame:
int y = 0;
int dy = 15;
public void draw()
{
y += dy;
if (y > 500 || y < 0)
dy = -dy;
writeableBmp.Clear(System.Windows.Media.Colors.Black);
writeableBmp.FillRectangle(0, y, 100, y + 100, System.Windows.Media.Colors.Green);
}
and the Loaded event which creates the Writable bitmap and also calls draw() on each frame.
WriteableBitmap writeableBmp;
private async void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
writeableBmp = BitmapFactory.New((int)ContentPanel.ActualWidth, (int)ContentPanel.ActualHeight);
image.Source = writeableBmp;
writeableBmp.GetBitmapContext();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
draw();
}
But this is giving my ~30FPS at most, so the animation is not smooth.
I know that there are some better ways of creating such animations in xaml (creating a rectangle object and animating using xaml animations for example), but there's a game (in another language) that redraws each frame this way, and my ultimate goal is porting that code to Windows Phone. So finding a way to redraw fast enough makes porting really easier.
So, is there a way to improve performance of this? or is there a better way to draw each frame manually, but fast enough (60fps)?
Try out Microsoft Win2D. You can get it using NuGet or their GitHub here: Microsoft Win2D GitHub. It's basically a wrapper over Direct2D and very simple to use.
Features (Copied from : https://github.com/Microsoft/Win2D/wiki/Features)
Easy-to-use Windows Runtime API
•Available from .NET and C++
•Supports Windows 10, Windows 8.1, and Windows Phone 8.1
Immediate mode 2D graphics rendering with GPU acceleration
•Implemented as a layer on top of Direct2D, DirectImage, and DirectWrite
•Interop to and from underlying types, so you can mix & match Win2D with native D2D
Bitmap graphics
•Load, save, and draw bitmap images
•Render to texture
•Use bitmaps as opacity masks
•Sprite batch API for efficiently drawing large numbers of bitmaps
•Use block compressed bitmap formats to save memory
•Load, save, and draw virtual bitmaps, which can be larger than the maximum GPU texture size and are automatically split into tiles
Vector graphics
•Draw primitive shapes (lines, rectangles, circles, etc.) or arbitrarily complex geometry
•Fill shapes using solid colors, image brushes, or linear and radial gradients
•Draw lines of any width with flexible stroke styles (dotted, dashed, etc.)
•High quality antialiasing
•Rich geometry manipulation (union, intersect, compute point on path, tessellate, etc.)
•Clip drawing to arbitrary geometric regions
•Capture drawing operations in command lists for later replay
•Rasterize ink strokes (from a stylus)
Powerful image processing effects
•Blurs
•Blends
•Color adjustments (brightness, contrast, exposure, highlights & shadows, etc.)
•Filters (convolve, edge detection, emboss, sharpen)
•Lighting
•Custom pixel shaders
•And many more...
Text
•Fully internationalized Unicode text rendering
•Text layouts can be drawn, measured, or hit-tested against
•Convert text outlines to geometry
•Enumerate fonts and query their metrics
•Draw or manipulate individual glyph runs to create custom text layouts
UI integration
•XAML CanvasControl make it easy to get up and running
•Can also create advanced things like owner-draw XAML controls
•XAML CanvasAnimatedControl provides Update/Draw game loop programming model
•XAML CanvasVirtualControl for drawing to very large virtualized surfaces
•Draw onto Windows.UI.Composition drawing surfaces and swapchains
•Can also draw directly to a CoreWindow
•Printing
Thanks to Chubosaurus Software who suggested Microsoft Win2D.
I'm gonna explain a little more about what I did in my case.
There's a control called CanvasAnimatedControl in Win2D, which is specially designed for this purpose.
After getting the package from NuGet and adding it to the page, you can use two events Draw and Update.
Update for the logic, and Draw is where you render the frame.
So, this is the code for the moving rectangle which described in the question:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
cvs.Update += Cvs_Update;
cvs.Draw += Cvs_Draw;
}
int y = 0;
int dy = 15;
private void Cvs_Draw(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
args.DrawingSession.Clear(Windows.UI.Colors.Blue);
args.DrawingSession.FillRectangle(new Rect(0, y, 100, 100), Windows.UI.Colors.Green);
}
private void Cvs_Update(Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedUpdateEventArgs args)
{
y += dy;
if (y > 500 || y < 0)
dy = -dy;
}
This code runs great (~60fps) on my phone.
The only downside is, Win2D supports Windows Phone 8.1 and higher. So by using this library, you'll lose Windows Phone 8 compatibility.
I seem to be unable to figure out how to Draw graphics in a Windows phone app in C#.
I want to Draw e.g. a line. In old school Windows forms i add an event handler to the Windows paint event. And then use a GDI+ Graphics object. But there is no paint event in any controls?
So how do i draw a line on a canvas in a Windows phone app?
I think I need to clarify.
I want to create dynamic graphics and I want to use C#.
I want an update frequency arround 30 fps and I only need a few graphics elements approximately 100.
if you need a line, use the Line class:
<Page xmlns="whatever">
<Grid>
<Line X1="0" Y1="0" X2="10" Y2="10" Stroke="Blue" StrokeThickness="2"/>
</Grid>
</Page>
Other than that, refer to MSDN.
Forget whatever procedural paradigms you might have learned in archaic technologies. Modern technologies are declarative.
You start by adding Canvas to the form and then .Add() graphical objects to the canvas children - it makes the object scaled for you by the engine, which is kind of neat. Usually looks like this:
line = new **Line**();
line.Stroke = Brushes.Yellow;
line.X1 = 0;
line.Y1 = 0;
line.X2 = 100;
line.Y2 = 100;
line.StrokeThickness = 2;
yourCanvas.Children.**Add(line)**;
Just drop the **s from the code - they are for attention grabbing.
Actually, as I drew dynamic hypercubes, I have never used the XAML version, but if you need a static structure or even substructure XAML is the way to go. As I understand, Children.Add() dynamically creates node in the parsed XAML tree, that .NET keeps in memory. If you can not take slight performance hit for dynamicly positioned graphics that WPF imposes, you will have to stick with DirectX or OpenGL for better performance.
If you want low level access to draw 2D or 3D like when using DirectX you can take a look to SharpDX
There are some samples for Windows Phone in Github like:
MiniCube: Display a rotating cube in a DrawingSurfaceBackgroundGrid
I am creating a tool which relies heavily on graph-node trees. The current implementation is done in Java and I'm porting it to a generic code-base on C#, so it can be used by various rendering implementations and also because I want to use the power of WPF for a user-friendly interface.
After browsing around for a day, I came across various methods to draw Vector-graphics through WPF.
This guy speaks about different layers within WPF developers can choose from. As I want to use WPF PURELY for his rendering at first, I want to work on the "Visual Layer".
I then came across things like:
DrawingVisual,
GeometryDrawing,
FrameworkElement / UIElement / Shapes
So, I'm a bit overwhelmed by all the different implementations that do eventually the same in totally different ways.
The Graph-Node library has been ported to C# already with all it's logic (including collision detection and dragging with mouse). As it is made with graphic-renderers in mind (like XNA, SlimDX, OpenTK, etc.), what would be the best way in terms of performance to implement a WPF renderer (as in, it will draw whatever the graph library tells it to draw?
Basically, the resulting WPF control acts as a canvas, but it has to be SUPER lightweight and not have any neat WPF features besides providing me a way to draw my circles, lines and other shapes :)
EDIT:
I basically want to know: What is the way to go? Do I extend Canvas as "Host" for my graphics and then add my custom implementation of a UIElement? Or can I have one class which can draw EVERYTHING (as in, one mega super ultra graphic). Much like overriding OnPaint in GDI or Paint-method in Java (which gives a Graphics object to do everything with).
I'd recommend reading Optimizing Performance: 2D Graphics and Imaging.
Basically, Drawing objects will be lighter weight than Shapes, in general. This is probably what you want to use.
Generally, better performance is obtained with lower-level services. In WPF, this means the Drawing family of objects. All you get are: Drawing, DrawingGroup, GeometryDrawing, GlyphRunDrawing, ImageDrawing, and VideoDrawing. However, they are sufficient for all needs. Using these types is very friendly with WPF because Drawing is the conceptual unit that WPF exchanges with your GPU accelerator, possibly retaining and managing it there if possible. This works because the Drawing is expressed in terms of portable vector drawing primitives.
Once you start re-architecting your app around Drawings however, you might need some interop with your higher-level code which is still based on UIElement, FrameworkElement, etc. One thing that I haven't found built-in to WPF is a simple way to wrap a Drawing as a FrameworkElement in the lowest-overhead way possible. DrawingVisual isn't a complete solution, because it only derives from Visual--meaning it still requires a hosting element.
The following class will host any WPF Drawing directly without using an intermediate DrawingVisual. I added support for FrameworkElement's Margin property (with no performance penalty if unused) but little else. Because of WPF's single rendering thread it's safe and easy to cache a single TranslateTransform object to implement the margin. I'd recommend that you supply only drawings which have been Frozen; in fact, in the version that I use, I have an assert to that effect in the constructor.
public class DrawingElement : FrameworkElement
{
static readonly TranslateTransform tt_cache = new TranslateTransform();
public DrawingElement(Drawing drawing)
{
this.drawing = drawing;
}
readonly Drawing drawing;
TranslateTransform get_transform()
{
if (Margin.Left == 0 && Margin.Top == 0)
return null;
tt_cache.X = Margin.Left;
tt_cache.Y = Margin.Top;
return tt_cache;
}
protected override Size MeasureOverride(Size _)
{
var sz = drawing.Bounds.Size;
return new Size
{
Width = sz.Width + Margin.Left + Margin.Right,
Height = sz.Height + Margin.Top + Margin.Bottom,
};
}
protected override void OnRender(DrawingContext dc)
{
var tt = get_transform();
if (tt != null)
dc.PushTransform(tt);
dc.DrawDrawing(drawing);
if (tt != null)
dc.Pop();
}
};
[edit:] This is also useful for inserting a WPF Drawing into the InlineUIContainer.Child property (i.e. using TextBlock.InlinesCollection to format the contents of the TextBlock more richly).
the DrawingVisual seems to be a valid choice:
The DrawingVisual is a lightweight drawing class that is used to
render shapes, images, or text. This class is considered lightweight
because it does not provide layout or event handling, which improves
its performance. For this reason, drawings are ideal for backgrounds
and clip art.
source: Using DrawingVisual Objects
so this seems to be absolutely what you ask, a Canvas SUPER lightweight.
So, I've used winForms .CreateGraphics to draw a variety of different things, from lines to boxes to images. It was very snappy and responsive.
I am trying to learn WPF in C#
I found that WPF allows me to "add" rectangle objects to a canvas which will display them properly. HOWEVER, I am drawing hundreds of thousands of rectangles at times, and the draw rate can become exceedingly slow, and the UI becomes less snappy when I move even 1 of the rectangles.
Painting directly onto an element in winForms was not very fast, but it was consistent regardless of how much I painted.
Is there a similar solution to doing this in WPF?
I tried adding a linq to System.Drawing, which gave me a Graphics object, but none of the wpf elements i tried have the .CreateGraphics() method.
WPF uses a different model for graphics manipulation than WinForms.
With WinForms, you are able to directly edit the pixels on the screen. The concept of your rectangle is lost after the pixels are drawn. Drawing pixels is a very fast operation.
With WPF, you are not controlling the pixels on the screen. DirectDraw is. DirectDraw is a vector-based compositing engine. You do not draw pixels. You define vector shapes (or visuals). The concept of a shape, or a rectangle, is RETAINED, even after the image is rendered to the screen. When you add a new rectangle which overlaps the others, ALL OTHER RECTANGLES NEED TO BE REDRAWN. This is likely where your performance is slowing down. This does not happen when using WinForms.
You can improve the performance of WPF a bit by overriding OnRender. You can cut out the overhead of the Rectangle object and directly provide the visuals. However, you are still not drawing pixels to the screen. You are defining shapes that DirectDraw uses to render the image. In this regard, the OnRender name may be a bit misleading.
I am sure you can find plenty of tricks to improve performance of your application in WPF. There are ways to still paint pixels - but that is kinda defeating the point of WPF.
What are you doing that requires thousands of rectangles?
You would need to create a control that overrides OnRender and do your drawing in there. There isn't a way for you to draw onto another control, but a control can draw itself.
Also, keep in mind that WPF uses retained graphics, so if you change something you need to invalidate the visual as needed.
EDIT:
Something like:
public class MyControl : Control {
public MyControl() {
this.Rects = new ObservableCollection<Rect>();
// TODO: attach to CollectionChanged to know when to invalidate visual
}
public ObservableCollection<Rect> Rects { get; private set; }
protected override void OnRender(DrawingContext dc) {
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Colors.LimeGreen;
Pen myPen = new Pen(Brushes.Blue, 10);
foreach (Rect rect in this.Rects)
dc.DrawRectangle(mySolidColorBrush, myPen, rect);
}
}
As was said, WPF uses a retained graphics methodology so your actually creating 100,000 Rectangle objects in memory and then drawing all of them. The slowdowns are probably due to garbage collection and general memory issues.
Aside from override the OnRender method, here's a couple of things you could look into though.
Drawing the rectangles to an image in a background thread using the GDI methods your familiar and then write the result to a WPF WriteableBitmap
Use the D3DImage and take advantage of hardware acceleration. This requires you to know the DirectX (or Direct2D) libraries. If your interested in this approach, I'd suggest looking into SlimDx.
The problem is most likeley not that WPF can't render 1000s of graphic objects, but that your creating and adding items too far up the WPF object hierachy. It does after all use the GPU for all the graphical grunt work.
You should add objects as close to the "Visual" class as possible, as soon as you start adding objects based on the latter "UIElement" you are asking WPF to track user clicks, hovers and so on for each object, not just draw it.
I am trying to write a small interactive game-like application, where I need to have a Draw method that's gonna draw on screen, but can't figure out how to structure the method for WPF.
If this was Winforms, I could use:
public void Draw (Graphics g)
{
}
But for a WPF Window, what should I have on it in the xaml (currently only have a Grid), and what should this Draw method receive as an argument?
First I want to do it like this to get it working, then I can think about how to make it more WPF, etc. But now I am more interested in getting this to work.
Typically, you "draw" in WPF in a completely different manner.
In Windows Forms/GDI, the graphics API is an immediate mode graphics API. Each time the window is refreshed/invalidated, you explicitly draw the contents using Graphics.
In WPF, however, things work differently. You rarely ever directly draw - instead, it's a retained mode graphics API. You tell WPF where you want the objects, and it takes care of the drawing for you.
The best way to think of it is, in Windows Forms, you'd say "Draw a line from X1 to Y1. Then draw a line from X2 to Y2. Then ...". And you repeat this every time you need to "redraw" since the screen is invalidated.
In WPF, instead, you say "I want a line from X1 to Y1. I want a line from X2 to Y2." WPF then decides when and how to draw it for you.
This is done by placing the shapes on a Canvas, and then letting WPF do all of the hard work.
When there are just too many objects to be drawn very quickly (huge Visual Tree) another option would be to use a WriteableBitmap. Just use the Pixels property to set the pixels and/or use the Render method to draw UIElements.
I preffer to use OnRender method like in this example:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawRectangle(null, new Pen(Brushes.Black, 2), new Rect(0, 0, ActualWidth, Height));
}
To Implement a Draw loop type behavior in WPF you can use the CompositionTarget.Rendering event. This is raised once per frame when the WPF drawing system is painting frames.
As others have pointed out this is not very WPF friendly but it will work and can be used to get more immediate drawing behavior out of a WPF app.
In most cases you would use a single root canvas and update say the Canvas position of an element on the CompositionTarget.Rendering event.
For example to make a ellipse fly all over the screen do this:
In your XAML (For a Window that is 640 by 480 in size):
<Canvas x:Name="theCanvas">
<Ellipse x:Name="theEllipse" Height="10" Width="10" Fill="Black" />
</Canvas>
In your Code behind for the Window that the above XAML is in (Make sure to add a reference to System.Windows.Media in order to see the CompsitionTarget object :
public static Random rand = new Random();
public View()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, System.EventArgs e)
{
double newLeft = rand.Next(0, 640);
double newTop = rand.Next(0, 480);
theEllipse.SetValue(Canvas.LeftProperty,newLeft);
theEllipse.SetValue(Canvas.TopProperty, newTop);
}
Yow should add a Canvas (or change the Grid for a Canvas) and then draw over it. Here is Microsoft tut on drawing over a canvas
Also, I don't know how related is this other question to yours, but you might want to check it out.