【问题标题】:How to quickly generate images with .NET如何使用 .NET 快速生成图像
【发布时间】:2011-08-15 01:25:20
【问题描述】:

我已经相当熟悉 System.Drawing 命名空间,了解如何生成图像、在其上绘图、在其上书写文本等的基本步骤。但是,对于任何接近打印的东西来说,它都非常缓慢-质量。我已经看到一些建议使用 COM 与本地 Windows GDI 对话以更快地完成此操作,但我想知道是否可以进行任何优化以实现高速、高质量的图像生成。我尝试过使用抗锯齿选项以及图形、位图和图像对象立即可用的选项,但是我可以采用其他技术来实现这种高速吗?

写这篇文章时,我只是想到使用 .Net 4 中的任务库来做更多的工作,即使每个生成任务都不会更快。

无论如何,我的想法和cmets 表示赞赏。

谢谢!

【问题讨论】:

  • 是否有一些您遇到性能问题的特定代码想要展示,以便我们有一个讨论的基础?

标签: c# .net gdi+ gdi


【解决方案1】:

如果您想要原始速度,最好的选择是使用 DirectX。下一个最好的方法可能是使用 C++ 中的 GDI 并提供一个托管接口来调用它。然后可能直接从 C# p/invoke 到 GDI,最后在 C# 中使用 GDI+。但是,根据您的操作,您可能看不到巨大的差异。如果您受到 GDI+ 驱动显卡速度的限制,多线程可能对您没有帮助,但如果您在计算“绘制什么”时受处理器限制,则可能会有所帮助。如果您要按顺序打印许多图像,则可以通过在单独的线程上运行预计算、渲染和打印“阶段”来获得收益。

但是,您可以采取许多措施来优化重绘速度,包括优化和折衷方案,这些方法将适用于您选择的任何渲染系统。事实上,其中大部分都源于优化代码时使用的相同原则。

你怎样才能最大限度地减少你绘制的数量?

首先,消除不必要的工作。想想你画的每一个元素——真的有必要吗?通常一个更简单的设计实际上可以看起来更好,同时节省大量的渲染工作。考虑是否可以将渐变填充替换为平面填充,或者圆角矩形是否看起来像普通矩形一样可以接受(并在丢弃之前测试这样做是否会为您的硬件带来任何速度优势!)

挑战你的假设 - 例如。 “高分辨率”要求 - 通常如果您要在染料打印机(这是一个引入一点颜色渗色的过程)或使用任何形式的抖动混合颜色的 CMYK 打印机(它有实际分辨率比打印机可以解决的点间距低得多),相对低分辨率的抗锯齿图像通常可以产生与超高分辨率图像一样好的结果。如果您要输出到 2400dpi 的黑白打印机,您可能仍然会发现 1200dpi 甚至 600dpi 是可以接受的(随着分辨率的提高,回报会越来越少,而且大多数人不会注意到 600dpi 和 2400dpi 之间的区别) .只需使用不同的源分辨率打印一些典型示例,看看结果如何。如果您可以将分辨率减半,您的渲染速度可能会提高 4 倍。

一般尽量避免过度绘制同一区域 - 如果要在区域周围绘制黑色框,可以在黑色矩形内绘制白色矩形,但这意味着将中间的所有像素填充两次。您可以通过在外部绘制 4 个黑色矩形来精确绘制框架来提高性能。相反,如果你有很多绘图图元,你能减少你绘制的图元的数量吗?例如如果要绘制很多条纹,可以交替绘制不同颜色的矩形(= 2n 个矩形),或者您可以用一种颜色填充整个背景,然后只绘制第二种颜色的矩形(= n+1 个矩形) .减少单独调用 GDI+ 方法的次数通常可以带来显着的收益,尤其是在您拥有快速图形硬件的情况下。

如果您多次绘制图像的任何部分,请考虑将其缓存(将其渲染为位图,然后在需要时将其传送到最终图像)。这个子图像越复杂,缓存它就越有可能得到回报。例如,如果您有像尺子这样的重复图案,不要将每个标尺标记绘制为单独的线 - 渲染标尺的重复部分(例如 10 行或 50 行),然后将其预渲染几次以绘制最后的统治者。

同样,避免做大量不必要的工作(例如许多 MeasureString 调用可能会预先计算一次甚至近似的值。或者,如果您要遍历很多 Y 值,请尝试通过在每个 Y 值上添加偏移量来实现迭代而不是每次都使用复数重新计算绝对位置)。

尝试“批量”绘图,以尽量减少必要的状态更改和/或绘图方法调用的次数 - 例如在继续使用下一种颜色之前,绘制一种颜色/纹理/画笔中的所有元素。使用“批量”渲染调用(例如,绘制折线图元一次,而不是调用 DrawLine 100 次)。

如果您正在执行任何逐像素操作,那么获取原始图像缓冲区并直接操作它通常比调用 GetPixel/SetPixel 方法快得多。

正如您已经提到的,您可以关闭昂贵的操作,例如抗锯齿和字体平滑,这在您的特定情况下不会有任何/太多好处。

当然,看看你正在渲染的代码 - 分析它并应用通常的优化来帮助它有效地流动。

最后,您应该考虑硬件升级是否是一种廉价而有效的解决方案 - 如果您的 PC 速度较慢且显卡低端,那么购买一个可能会获得显着收益带有更好显卡的新 PC。或者,如果图像很大,您可能会发现增加几 GB 的 RAM 可以消除虚拟内存分页开销。这听起来可能很昂贵,但有一点是,新硬件的成本/收益比投入更多资金进行额外优化工作(及其不断减少的回报)要好。

【讨论】:

  • 几个不错的建议。特别是我没有考虑缓存,可以说是重复绘制的区域。通过从磁盘获取图像来处理这项工作很多,因此 I/O 开销非常高。我可以通过“观察”对同一图像的重复调用来减少它,并将其保存在内存中一段时间​​。再次感谢!
【解决方案2】:

我有几个想法:

  1. 查看 Paint.net 中的代码。它是一个用 C# 编写的开源绘图程序。它可以给你一些好主意。您当然可以结合想法 2 和 3 来做到这一点。

  2. 如果需要以“立即”方式完成作业,您可以使用异步方式来创建图像。根据整个应用程序的范围,您甚至可以使用 NServiceBus 之类的东西将图像处理组件与任务排队。任务完成后,发送组件将通过订阅完成后发布的消息收到通知。

  3. 基于任务的解决方案适用于延迟处理。您可以批量创建图像并使用任务方法或称为 Quartz.net (http://quartznet.sourceforge.net) 的方法。这是一个开源作业调度程序,我用于所有基于时间的作业。

【讨论】:

    【解决方案3】:

    您可以创建一个新的位图图像并执行 LockBits(...),指定您想要的像素格式。一旦你有了这些位,如果你想使用非托管代码来绘制它,请引入库,将数据固定在内存中,然后使用该库来对付它。我认为您可以对原始像素数据使用 GDI+,但我的印象是 System.Drawing 已经是 GDI+ 之上的一个薄层。无论如何,无论我是否错了,使用 LockBits,您都可以直接访问像素数据,这可能与您对它的编程一样快或慢。

    完成绘图后,您可以 UnlockBits 和 viola 获得新图像。

    【讨论】:

    • 实际上,如果您正在寻找快速打印质量的图形,也许您可​​以使用矢量图而不是位图。
    • 谢谢,我不知道。我会提供代码,但它显然远远超过这个框提供的 600 个左右的字符。
    猜你喜欢
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 2014-08-13
    • 2015-04-22
    • 2017-10-20
    • 2023-04-03
    相关资源
    最近更新 更多