C# 提高绘制热图时的渲染性能
我试图用给定的输入点和每个点的振幅绘制一张热图。正常输入大小约为400000点,但代码每15点绘制一次。地图绘制在C# 提高绘制热图时的渲染性能,c#,wpf,C#,Wpf,我试图用给定的输入点和每个点的振幅绘制一张热图。正常输入大小约为400000点,但代码每15点绘制一次。地图绘制在画布上 在XAML中: 渲染代码: public static void DrawPoints(Parser parser, Canvas canvas, double radiusX, double radiusY, double thickness, double minAmplitude, double maxAmplitude) { double cellWidt
画布上
在XAML中:
渲染代码:
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;
}
在主代码中:
private void btnDraw_Click(object sender, RoutedEventArgs e)
{
Painter.DrawPoints(parser, canvas, 3, 3, 1, 0, 100);
}
问题是,在将所有点渲染到画布
上后,当我尝试在同一网格内的文本框
中输入一些文本时,存在明显的延迟。我能做些什么来减少滞后 您可以做的是,将所有数据绘制到一个可视化
中,并使用渲染目标位图
进行渲染。这需要一段时间,但会解决您的性能问题
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;
}
我无法访问你剩下的代码,因此,下面的代码使用了大量简化和随机数据,例如,我使用的是图像
而不是画布
和简单的点
s,而不是解析器返回的任何内容。您可能需要查看WriteableBitmapEx库。数据是否会更改,或者只需要生成一次?你需要互动(上下文菜单、工具提示、缩放)还是快照/图像(如克莱门斯建议的)就足够了?@ManfredRadlwimmer它只需要生成一次。唯一的交互性是onMouseClick
,它将返回单击点的振幅值。这解决了GUI滞后的问题,但我意识到,使用这种渲染方法,它实际上比直接在画布上绘制要慢得多。有什么技巧可以帮助提高性能吗?@JohnTan您可以在多个线程中预渲染热图的部分(按z顺序排序),然后使用相同的方法将它们合并在一起。因为bitmap.Freeze()
确保生成的图像是线程安全的(并且是锁定的),所以它不应该是一个问题。我该怎么做?这个想法是否类似于将context
传递到不同的线程中,每个线程都运行context.drawerlipse()
?如果是这样的话,我该怎么把它们重新合并在一起呢?哦,不,我不是这个意思。我的意思是将部分数据传递给几个线程,它们都创建一个单独的图像,然后将图像与context.DrawImage
合并(覆盖)到一个新的DrawingVisual上。另一种选择是使用第三方库,它们在绘制图像时速度更快(我不知道任何库,但我确信它们确实存在)。@JohnTanWriteableBitmapEx
(Clemes在评论中提到了这一点)工作非常出色-我已经更新了答案(在底部)。
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;
}
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;
}