Reputation: 24558
We are building a graphical application, we need to draw dots on the canvas background, as we have a feature like Snap To Grid
Of course, user can set the distance between snapping dots, so, if we have a canvas of size 1024 x 1024, with 5 pixels between each dot, we will have around 41775 dot!
What's the recommended way to render this high number of dots on the canvas ? we need it to be as fast as possible.
Upvotes: 1
Views: 2556
Reputation: 10797
WPF doesn't have a direct method to draw pixels on a Canvas. The optimal way to implement it would be with Image and a WriteableBitmap source. Take a look at the code below. It has two functions: drawGrid1 and drawGrid2. On my machine, the first function (draws Ellipse element) takes 6 seconds. The latter function takes 50 milliseconds.
The code below is just for illustration. You could cache the WritebaleBitmap, and you should be sensitive (if your scenario requires) changes in the width or height (or, just create a very big bitmap). If you need even more performance, and you are OK with unsafe code, you can call WritebaleBitmap.Lock, then get WriteableBitmap.BackBuffer, and modify the back buffer manually. At the end, call WriteableBitmap.AddDirtyBuffer to invalidate the entire rectangle. It is also possible that if your Grid has only two colors, you can achieve even more performance, by using a palette.
More about WriteableBitmap: http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap(VS.85).aspx
XAML:
<Window
x:Class="SO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="1000" Width="1000"
Title="SO Sample"
Loaded="Window_Loaded"
>
<Canvas x:Name="x_canvas">
<Border Canvas.Left="4" Canvas.Right="4" Width="120" Height="32" Background="White" >
<TextBlock x:Name="x_txt" VerticalAlignment="Center" />
</Border>
</Canvas>
</Window>
Code Behind:
private void Window_Loaded( object sender, RoutedEventArgs e ) {
DateTime start = DateTime.Now;
//drawGrid1( );
drawGrid2( );
DateTime end = DateTime.Now;
TimeSpan span = end - start;
x_txt.Text = span.ToString( );
}
private void drawGrid2( ) {
// Create a new image
Image img = new Image( );
RenderOptions.SetBitmapScalingMode( img, BitmapScalingMode.NearestNeighbor );
RenderOptions.SetEdgeMode( img, EdgeMode.Aliased );
// Add this image to the canvas
x_canvas.Children.Add( img );
int width = (int)x_canvas.ActualWidth;
int height = (int)x_canvas.ActualHeight;
// Create the bitmap, and set
WriteableBitmap wb = new WriteableBitmap(
width,
height,
96, 96,
PixelFormats.Bgra32,
null
);
img.Source = wb;
img.Stretch = Stretch.None;
img.HorizontalAlignment = HorizontalAlignment.Left;
img.VerticalAlignment = VerticalAlignment.Top;
Canvas.SetZIndex( img, -100 );
// Each "dot" is 2x2 rectangle
Int32Rect rect = new Int32Rect( 0, 0, 2, 2 );
int size = rect.Width * rect.Height * 4;
byte[] pixels = new byte[ size ];
// Setup the pixel array
for( int i=0; i<rect.Height*rect.Width; ++i ) {
pixels[ i*4 + 0 ] = 255; // Blue
pixels[ i*4 + 1 ] = 0; // Green
pixels[ i*4 + 2 ] = 0; // Red
pixels[ i*4 + 3 ] = 255; // Alpha
}
wb.WritePixels( rect, pixels, rect.Width*4, 0 );
int step = 5;
for( int r = 0; r<height; r+=step ) {
for( int c = 0; c<width; c+=step ) {
rect.X = c;
rect.Y = r;
wb.WritePixels( rect, pixels, rect.Width*4, 0 );
}
}
}
private void drawGrid1( ) {
int step = 10;
for( int i=0; i<1024; i+=step ) {
for( int j=0; j<1024; j+=step ) {
Ellipse l = new Ellipse( );
if( i%100==0 && j%100==0 ) {
l.Width = 4;
l.Height = 4;
}
else {
l.Width = 2;
l.Height = 2;
}
l.Fill = new SolidColorBrush( Colors.Black );
Canvas.SetTop( l, i );
Canvas.SetLeft( l, j );
Canvas.SetZIndex( l, -100 );
this.x_canvas.Children.Add( l );
}
}
}
Upvotes: 4