绘制太大而无法放入 QImage 的图像
Posted
技术标签:
【中文标题】绘制太大而无法放入 QImage 的图像【英文标题】:Draw an image too big to fit into a QImage 【发布时间】:2014-01-30 16:33:09 【问题描述】:我需要显示一个非常大的图表,例如图像分辨率可能是 100 000 x 1000。但是,我似乎被 QImage 限制为 32768 x 32768。
我无法合理地在每个 paintEvent
处直接重绘图表,因此我需要将其存储到 QImage 中(它可能是不会改变任何内容的 QPixmap)。但是,它不适合。
我的第一个想法是:
创建QImage
列表
在各种QImage
上绘图
使用良好的QImage
s 重绘。
第一点和最后一点已经很容易完成了。但第二点更复杂。我非常有信心我的方法会奏效,但它需要重载基本绘制方法(绘制矩形、圆形等)才能在多个图像上绘制。
所以,在继续之前,我想知道其他选择。
【问题讨论】:
QImage
是每行像素缓冲区对齐的连续 4 字节。这样的数据结构对于(A)不会一次全部显示,(B)通常会“缩小”显示,以及(C)非常大的图像设计得不好。任何库都不需要 400 MB 的连续 RAM。这就是为什么QImage
不起作用的原因。一个平铺图像,其中每个平铺是一个QImage
,甚至还有一个用于缩小的 mip-map 类型的东西,应该可以工作 - 我不知道这是否是你所描述的。
这就是我所描述的。
【参考方案1】:
您可能不想一次显示多个QImage
的数据。很少有屏幕的宽度或高度超过 32k 像素。
所以你想要一个抽象类型,产生QImage
s 请求读取,在偏移量和可能在不同的缩放因子。
下一个问题是修改这个抽象类型。一个易于使用但不是最高性能的版本包括让用户将QImage
s 写入您的内部存储(无论是什么)。
用户仍然必须“平铺”他们的努力,但可以以他们方便的方式平铺他们的努力。
更高性能的版本公开了一些我们尚未提及的底层实现。
大图像的传统实现是平铺图像。您有一个相互邻接的图像图块网格。当有人从您的图像中请求 blit 时,您会生成一个临时的QImage
,并将适当的图块 blit 到它上面。当有人向您发送消息时,您会找出合适的图块,并在部分图块上写下该来源的部分内容QImage
。
更高性能的界面公开了这些图块。
低级界面让外部知道您的图块在哪里,并让他们请求它们。这是一个糟糕的界面。
更好的界面公开了一个子图块迭代器。他们要求一个区域,然后您返回一对描述该区域的迭代器。迭代器中的数据包括一个瓦片和该瓦片中的一个区域以及该区域在“完整图像”中的位置,或一个子瓦片对象(具有线跨度、线长等)和子图块对象。
另一个好的界面是foreach
风格的界面。同样,大图像类的用户传入他们想要使用的区域,但也传递了一个回调。该回调类似于上述迭代器取消引用的结果之一。
与迭代器方法相比,这种方法有两大优势。首先,您可以在大型图像类中实现并行图像处理算法。其次,编写比滚动自己的迭代器要容易得多。
一旦你拥有了其中任何一个,绘图就相对容易了。确定您要绘制的区域(要大方)。迭代生成的图块。在每个图块上,将图块的偏移量应用于绘图后进行绘制。
【讨论】:
感谢您的解释,但我不明白如何实际实施后一种方法。我的意思是,我正在使用 QPainter 方法编写文本。如果该文本位于 2 个不同的图块上怎么办? @Maxime 这取决于。QPainter::drawText
是否让您传递负坐标?如果是这样,请计算(或猜测)哪些图块与您的文本重叠。将全局坐标转换为瓦片局部坐标。在与文本重叠的每个图块上绘制文本(可能在负偏移处)。正如我上面提到的,tile-exposing 版本更难使用:我会先实现“blit a QImage”和“get a QImage”接口,然后担心在你工作后公开tile。【参考方案2】:
您可以使用 Qt 图形视图框架。为它创建一个QGraphicsView
和一个QGraphicsScene
。使用QGraphicsScene::addPixmap
添加项目(返回QGraphicsPixmapItem
,它派生自QGraphicsItem
)并使用QGraphicsItem::setPos
调整它们的位置。 QGraphicsView
将有效地绘制您的场景并在必要时处理滚动和缩放。
【讨论】:
【参考方案3】:您知道 100,000 x 1000 RGBA QImage 是 400MBytes 吗?浪费所有的内存是没有意义的。真的,没有。
只需在paintEvent
中每次请求一个请求即可绘制它。要聪明一点,这样你就只画需要展示的东西。我认为应该专注于优化绘画过程和您的数据结构,以便可以有效地绘画。
在小范围内(缩小),通过对数据进行近似/抽取/插值以使其看起来相同,可以获得很多好处,但您不会浪费时间多次绘制相同的像素。
【讨论】:
以上是关于绘制太大而无法放入 QImage 的图像的主要内容,如果未能解决你的问题,请参考以下文章