【发布时间】:2012-03-08 05:10:54
【问题描述】:
我发现了几种在 WPF 中优化位图处理的模式。但是,我不明白何时使用每种模式。由于我认为这是一个普遍的问题,我总结了我的理解和我的猜测并请求您的帮助。如果您可以添加模式,说明它们有何不同,说明它们使用CPU还是GPU,并教何时使用每个和如何组合它们,这将是一个巨大的帮助!
上下文——图像“网格”场景:
我的应用程序必须显示许多位图图像。图像以行和列的网格状组织形式显示在屏幕上(不一定是 Grid 或 UniformGrid 类,想想 Window Media Player 的相册视图)。图像可能会在不同的网格单元之间移动。任意单元格处的某些图像可能会被其他图像替换。图片应该是可点击的,应该提供一个上下文菜单,应该是可选择的,可拖动的等等。换句话说,“将小虫子组合成一个大的位图”是不适用的,至少不是幼稚的。
模式 0:黑客攻击
将小虫子组合成一个位图(如何?绘制上下文?),并将其用作背景。用带有空内容的图像覆盖它,这些图像将处理点击、上下文菜单、事件等。
优点是我们在这里只讨论两个位图:当前显示的一个和应该替换它的一个。这应该非常快。然而,我多年的经验提出了危险的危险信号。你的cmets?
模式 1:缩小图片尺寸
如果您事先知道要调整大小的图像大小,并且准备好丢失细节(颜色)以提高性能,那么这很容易:
- 使用 BitmapImage.DecodePixelWidth 减小位图大小
- 使用 FormatConvertedBitmap.DestinationFormat 减少颜色信息
- 将控件的缩放行为设置 Image.Stretch 设置为 Stretch.None
- 将图像的 SetBitmapScalingMode 设置为 LowQuality。
- 冻结臭虫
见代码here。
模式 2:后台预取
当您认为可以利用用户凝视屏幕上的图像并提前准备好下一个要显示的图像时,此模式适用。除了内存开销之外,您的项目的缺点是它必须支持 .Net Framework 4 目标,而不仅仅是客户端配置文件,因此它可能会导致在客户端上进行安装。你自己将不得不忍受异步编程的痛苦。
在此模式中,您可以创建所需数量的图像控件。当需要添加、移动或删除位图时,您只需修改 Image 控件的 BitmapSource(s)。 BackgroundWorker 任务负责预取 BitmapSource(s)(可能使用上面的“减少图像大小”模式)并将它们插入 MemoryCache。
为此,您必须将 BitmapImage 的 CacheOption 设置为 OnLoad,以便将工作卸载到后台工作人员。
模式 3:绘图上下文
这是由 Microsoft 支持部门的 Sheldon Ziao 在 MSDN WPF 论坛 here 上提出的。有关 DrawingContext 的描述,请参见 Adam Nathan 的 WPF 4 Unleashed 中的第 494 页第 15 章“2D 图形”。我不能说我明白。根据here 的答案,我认为这将改善对几何图形的处理,而不是位图。接下来,我不认为这将支持图像的焦点和事件要求(我在论坛上没有更好地解释要求是我的坏事)而且,我担心这本书的总结句:“注意使用DrawingContext不会改变您在保留模式系统中操作的事实。指定的绘图不会立即发生;这些命令由 WPF 持久化,直到需要它们为止。”这意味着一旦我们的偶数处理程序重新启动,我们就不能像“后台预取”那样利用并行性。
模式 4:可写位图
MSDN 文档here 将其描述为双缓冲区系统:您的 UI 线程更新缓冲区; WPF 的渲染线程将其移至显存。
预期用途(请参阅here)用于在视频电影等显示中发生很大变化的位图。我不确定,但这可能会被破解并与后台预取模式结合并用于网格场景。
模式 5:缓存位图
MSDN (here) 上的信息不多。在 WPF 论坛存档 (here) 上解释说:“BitmapCache API 旨在将您的内容(在硬件中渲染时)缓存在视频内存中,这意味着它会驻留在您的 GPU 上。这可以节省您在将内容绘制到屏幕时重新渲染该内容的成本。”这似乎是个好主意。但是,我不确定有哪些陷阱以及如何使用它。
模式 6:RenderTargetBitmap
RenderTargetBitmap 将 Visual 转换为位图。我不确定这里是否相关。见here。
编辑:关于 Paul Hoenecke 的问题:我写过“我的应用程序必须显示许多位图图像”。我没有提到我需要同时显示大约 800 张图像。
可以阅读我的 SO 问题 WPF Bitmap performance 和 How can I make displaying images on WPF more “snappy”? 中涉及的性能问题
我已经修改了模式 1 的描述,以突出显示图像控件不会被创建或删除的概念(除非我们想要显示更大或更小的网格)。只有它们的 Sources 设置为不同的、新的或 null BitmapSources。
编辑:This question as posted on the WPF support forum,附有 MS 人员的一些回答。
【问题讨论】:
-
您预计有多少张图片?我之前制作了一个显示数千张图像网格的应用程序。我们使用了虚拟模式列表框;当用户向下滚动时,图像在后台线程中加载,冻结并设置为图像的源。显然,您对此进行了很多思考……但是,也许更多地了解您想要完成的工作会更好。例如,您遇到过哪些问题使优化成为如此重要的优先事项?
-
@PaulHoenecke 嗨,保罗,谢谢您的回答。当您想要显示大集合中的少量项目时,虚拟集合可以帮助您。在这里,我想同时显示大约 1K 个项目。此外,我不确定是否存在虚拟化网格。最后,模式 1 本质上是虚拟化 - 我将添加 en 编辑。
-
Avi,您的问题看起来不适合所描述的问题。您可能出于某种原因想要提高压缩率,但是您需要确切地知道您正在显示什么样的图像。或者,另一方面,您可能希望给用户所有可用图像的印象,然后您可以定义一些集合并显示缩略图等。但这两件事并不一定相互关联。也许您想要类似相似度指标来定义外观相似的图像集?
-
@artsenay 对不起,我不明白你的 cmets。我想在屏幕上显示很多位图,我想在网格中显示它们,而不是在任意屏幕位置。而且还不够快,所以我希望它看起来更快。
-
@Avi 有什么理由不使用现有算法,例如JPEG 具有低质量设置?当然,这取决于您要压缩的图像,但是如果您有图表之类的图像,则可以尝试使用 PNG 和其中一种编号算法。如果您不知道自己想做什么,那么讨论如何对内存中的二进制数据进行洗牌是没有任何意义的。可能服务器缓存是您最小的问题,因为瓶颈将在客户端或传输中。
标签: wpf performance optimization bitmap design-patterns