WPF中BitmapSource创建速度非常慢的问题

Oshinac 2014-12-29 11:54:16
我最近在做一个基于WPF的项目,其中有个功能是将一些几何图形数据绘制成为一张图片,然后再显示在界面上。
我尝试了两种方法:
1. 使用GDI+中的Bitmap和Graphics来绘制图片,再Save成Stream,再通过BitmapFrame.Create来将创建BitmapSource,最后通过Image控件呈现图片。
2. 使用WPF中的DrawingVisual绘制图形,并使用RenderTargetBitmap生成BitmapSource,最后通过Image控件呈现图片。

其实,如上两种方法,本质上都是先绘制图形,再构造成WPF的Image控件支持的BitmapSource。只是前一种使用的是GDI+的绘制,后一种使用的是WPF的绘制。

如上两种方法在正常情况下工作都很正常,但是,如果当该应用程序的内存占用达到1.5G以上时(我会把所有数据加载到内存中),如上所述的两个方法中的第二个阶段,也就是构造BitmapSource的过程,会变得非常慢,会耗时1s以上,而正常情况下只耗时20毫秒。
经过反复测试,我发现耗时问题这只跟程序的内存占用有关系,内存一旦达到1.5G左右或以上,BitmapSource的构造就会很慢,内存一减下来,就又恢复正常速度。

为了避免多余的代码干扰,我特地建立一个新的测试项目,发现该问题仍然存在,测试代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SampleGDI
{
class Geo
{
public string GeoText { get; set; }
}

public partial class MainWindow : Window
{
private Random rd = new Random((int)DateTime.Now.Ticks);
private Timer timer = new Timer(1000);

//用于存放大数据
private List<object> data = new List<object>(200000000);


public MainWindow()
{
InitializeComponent();

//模拟大数据量的内存占用
for (int i = 0; i < 40000000; i++)
data.Add(new Geo() { GeoText = "POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))" });

timer.Elapsed += timer_Elapsed;
timer.Start();
}

void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Draw();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
}

private void Draw()
{
timer.Stop();

while (true)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var visual = new DrawingGroup();
using (var context = visual.Append())
for (int i = 0; i < 10; i++)
context.DrawEllipse(Brushes.Black, new Pen(Brushes.Black, 1), new Point(rd.Next(800), rd.Next(480)), 4, 4);
sw.Stop();
var v = new DrawingVisual();
using (var context = v.RenderOpen())
context.DrawDrawing(visual);
sw.Stop();
Console.WriteLine("绘制:{0}", sw.Elapsed);

System.Threading.Thread.Sleep(1000);



sw = System.Diagnostics.Stopwatch.StartNew();
var dImageSource = new RenderTargetBitmap(800, 480, 96, 96, System.Windows.Media.PixelFormats.Pbgra32);
dImageSource.Render(v); // 这一步很慢,会耗时1s左右,正常情况只耗时20毫秒左右。
dImageSource.Freeze();
sw.Stop();
Console.WriteLine("构造:{0}", sw.Elapsed);
System.Threading.Thread.Sleep(1000);


sw = System.Diagnostics.Stopwatch.StartNew();
Dispatcher.Invoke(new Action(() => { image.Source = dImageSource; }));
sw.Stop();
Console.WriteLine("显示:{0}\r\n", sw.Elapsed);
System.Threading.Thread.Sleep(2000);

}
}
}
}


请问,如果在不修改数据读取机制(全部加载到内存这种方式)的情况下,应该怎样解决速度慢的问题?
...全文
1095 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
SlimTracy 2016-04-05
  • 打赏
  • 举报
回复
载入常用的小图用CachedBitmap。 画图用[RenderTargetBitmap].Render效率是很差,尤其是绘制调用多的时候。用VisualBrush会快点。
baidu_31302759 2016-04-01
  • 打赏
  • 举报
回复
我想问一下这里需不需要 obBitmapImage.Freeze(); 把它变成只读的,我现在在做的实时显示也是这么做的,但是我总在想如果如果在别的地方挂了不动地方了,这个地方的内存会不会突增
baidu_31302759 2016-04-01
  • 打赏
  • 举报
回复
我用的是第一个例子的方法
zixingcheng_ 2014-12-30
  • 打赏
  • 举报
回复
你的图片创建之类的速度自己解决,就我的程序没发现图片到bitmapSource速度不慢。我给两个转换代码你参考。 /// <summary>将图片转换为图片源 /// </summary> /// <param name="objBitmap">图片</param> /// <returns></returns> public static BitmapImage Trans_ToSource(Bitmap objBitmap) { MemoryStream objMs = null; BitmapImage obBitmapImage = null; try { //将图片转换为内存 objMs = new MemoryStream(); //该处有些问题,测试发现目前使用Png格式可以兼容无错其他均可能出错 objBitmap.Save(objMs, System.Drawing.Imaging.ImageFormat.Png); //将内存转换为Wpf图像源 obBitmapImage = new BitmapImage(); obBitmapImage.BeginInit(); obBitmapImage.StreamSource = objMs; obBitmapImage.EndInit(); //返回图片源 return obBitmapImage; } catch { throw; } } /// <summary>将图片转换为图片源 /// </summary> /// <param name="strNameSpace">命名空间,格式为"GalaxyErp.Main"</param> /// <param name="strPath">图片路径(有命名空间时为相当路径格式为"Images/btnMinimize.png",否则为绝对路径)</param> /// <returns></returns> public static BitmapImage Trans_ToSource(string strNameSpace, string strPath) { Uri objUri = null; string strRealPath = ""; BitmapImage obBitmapImage = null; try { //路径修正 strPath = strPath.Replace("\\", "/"); while (strPath.Substring(0, 1) == ".") { strPath = strPath.Substring(1); } if (strPath.Substring(0, 1) == "/") { strPath = strPath.Substring(1); } //提取真实路径 if (strNameSpace + "" != "") { strRealPath = string.Format(@"/{0};component/{1}", strNameSpace, strPath); } else { strRealPath = strPath; } //生成路径 objUri = new Uri(strRealPath, UriKind.Relative); obBitmapImage = new System.Windows.Media.Imaging.BitmapImage(); obBitmapImage.BeginInit(); obBitmapImage.UriSource = objUri; obBitmapImage.EndInit(); return obBitmapImage; } catch { throw; } }

8,757

社区成员

发帖
与我相关
我的任务
社区描述
WPF/Silverlight相关讨论
社区管理员
  • WPF/Silverlight社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧