simo
simo

Reputation: 24558

What's the optimum way to draw high number of dots?

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

Answers (1)

Uri London
Uri London

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

Related Questions