我们正在构建一个图形应用程序,我们需要在画布背景上绘制点,因为我们有一个像对齐网格
这样的功能当然,用户可以设置捕捉点之间的距离,因此,如果我们有一个尺寸为1024 x 1024的画布,每个点之间有5个像素,我们将有大约41775个点!
在画布上渲染这么多点的推荐方法是什么?我们需要它尽可能快。
答案 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 );
}
}
}