在绘制热图时提高渲染性能

时间:2017-07-20 07:46:27

标签: c# wpf

我试图用每个点的点和幅度的给定输入绘制热图。正常输入大小约为400,000点,但代码每15个 th 点。地图绘制在Canvas

在XAML中:

<Grid x:Name="MainPanel" Width="800" Height="600">
    <Textbox Name="textbox" Grid.Column="0"/>
    <Canvas Name="canvas" Grid.Column="1" HorizontalAlignment="Left" Height="450" Margin="70,75,0,0" VerticalAlignment="Top" Width="706"/>
</Grid>

渲染代码:

public static void DrawPoints(Parser parser, Canvas canvas, double radiusX, double radiusY, double thickness, double minAmplitude, double maxAmplitude)
{
    double cellWidth = canvas.ActualWidth;
    double cellHeight = canvas.ActualHeight;

    var list = parser.Data;

    // Draws every 15th point
    int res = 15;

    for (int i = 0; i < list.Count; i += res)
    {
        var location = list[i].Point;
        var amplitude = list[i].Amplitude;

        // Converts rgb to hsv values for representation
        var color= ColorConverter.hsv2rgb(
            COLOR_START_H * (1.0 - ((amplitude - minAmplitude) / (maxAmplitude - minAmplitude))),
            COLOR_START_S, COLOR_START_V);

        Point center = new Point(location.X, location.Y);
        DrawEllipse(canvas, center, radiusX, radiusY,
            new SolidColorBrush(color), new SolidColorBrush(color), thickness);
    }
    return;
}

DrawEllipse函数中:

public static void DrawEllipse(Canvas canvas, Point center, double radiusX, double radiusY, Brush fill, Brush stroke, double thickness)
{
    var ellipse = new Ellipse();
    ellipse.Width = radiusX * 2;
    ellipse.Height = radiusY * 2;
    ellipse.Fill = fill;
    ellipse.Stroke = stroke;
    ellipse.StrokeThickness = thickness;

    Canvas.SetLeft(ellipse, center.X - radiusX);
    Canvas.SetBottom(ellipse, center.Y - radiusY);

    canvas.Children.Add(ellipse);

    return;
}

在主要代码中:

private void btnDraw_Click(object sender, RoutedEventArgs e)
{
    Painter.DrawPoints(parser, canvas, 3, 3, 1, 0, 100);
}

问题在于,在将所有点渲染到Canvas之后,当我尝试在同一网格中的textbox中输入一些文本时,存在显着的滞后。我该怎么做才能减少滞后?

1 个答案:

答案 0 :(得分:1)

您可以做的是将所有数据绘制到Visual并使用RenderTargetBitmap进行渲染。这需要一段时间,但会解决您的性能问题。

private List<Point> _points;
private Size _targetarea;

private void btnGenerate_Click(object sender, RoutedEventArgs e)
{
    _targetarea = new Size(500, 500);
    _points = GeneratePoints(10000, _targetarea);
    TheImage.Source = DrawPoints(_points, _targetarea, 5, 5, 1);
}

private List<Point> GeneratePoints(int count, Size size)
{
    Random r = new Random();

    return Enumerable.Range(0, count)
        .Select(j => new Point(r.NextDouble() * size.Width, r.NextDouble() * size.Height))
        .ToList();
}

private static ImageSource DrawPoints(IEnumerable<Point> points, Size size, double radiusX, double radiusY, double thickness)
{
    DrawingVisual visual = new DrawingVisual();

    using (DrawingContext context = visual.RenderOpen())
    {
        var fill = new SolidColorBrush(Colors.Yellow);
        var stroke = new SolidColorBrush(Colors.Red);

        foreach(var center in points)
        {
            context.DrawEllipse(fill, new Pen(stroke, thickness), center, radiusX, radiusY);
        }
    }

    RenderTargetBitmap bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
    bitmap.Render(visual);
    bitmap.Freeze();
    return bitmap;
}

要在单击图像时确定最近的数据点,只需使用您用于生成图像的原始数据。

private void TheImage_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Point clickPoint = GetCoordinates(e.GetPosition((IInputElement) sender));

    Point closestPoint = new Point();
    double minSquareDistance = double.MaxValue;

    foreach (Point point in _points)
    {
        double dX = point.X - clickPoint.X;
        double dY = point.Y - clickPoint.Y;
        double squareDistance = dX * dX * dY * dY;
        if (squareDistance < minSquareDistance)
        {
            minSquareDistance = squareDistance;
            closestPoint = point;
        }
    }

    MessageBox.Show($"Clicked near {closestPoint.X:f0}/{closestPoint.Y:f0}");
}

private Point GetCoordinates(Point position)
{
    // Here you need to translate Screen-Coordinates to your internal coordinate system
    return position;
}

要加快整个过程,请安装WriteableBitmapEx(NuGet可用)并改为使用此代码:

private static ImageSource DrawPoints(IEnumerable<Point> points, Size size, double radiusX, double radiusY, double thickness)
{
    WriteableBitmap bitmap = new WriteableBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32, null);

    foreach(var center in points)
    {
        bitmap.FillEllipse((int)(center.X - radiusX), (int)(center.Y - radiusY), (int)(center.X + radiusX), (int)(center.Y + radiusY), Colors.Yellow);
        bitmap.DrawEllipse((int) (center.X - radiusX), (int) (center.Y - radiusY), (int) (center.X + radiusX), (int) (center.Y + radiusY), Colors.Red);
    }

    bitmap.Freeze();
    return bitmap;
}

我无权访问您的其余代码,因此以下代码使用了大量简化和随机数据,例如:我使用的是Image而不是Canvas和简单的Point,而不是您的解析器返回的任何内容。