绘制大量点数的最佳方法是什么?

时间:2012-03-11 08:25:07

标签: wpf

我们正在构建一个图形应用程序,我们需要在画布背景上绘制点,因为我们有一个像对齐网格

这样的功能

当然,用户可以设置捕捉点之间的距离,因此,如果我们有一个尺寸为1024 x 1024的画布,每个点之间有5个像素,我们将有大约41775个点!

在画布上渲染这么多点的推荐方法是什么?我们需要它尽可能快。

1 个答案:

答案 0 :(得分:4)

WPF没有直接的方法在Canvas上绘制像素。实现它的最佳方式是使用Image和WriteableBitmap源。看看下面的代码。它有两个函数:drawGrid1和drawGrid2。在我的机器上,第一个函数(绘制Ellipse元素)需要6秒。后一种功能需要50毫秒。

以下代码仅供参考。你可以缓存WritebaleBitmap,你应该敏感(如果你的场景需要)宽度或高度的变化(或者,只是创建一个非常大的位图)。如果您需要更高的性能,并且对不安全的代码没问题,可以调用WritebaleBitmap.Lock,然后获取WriteableBitmap.BackBuffer,并手动修改后台缓冲区。最后,调用WriteableBitmap.AddDirtyBuffer使整个矩形无效。如果您的网格只有两种颜色,也可以使用调色板实现更高的性能。

有关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>

代码背后:

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