3

我目前正在尝试提高地图渲染库的性能。在准时符号的情况下,图书馆真的经常只是在每个位置一次又一次地绘制相同的图像。但是,绘制过程可能非常复杂,因为符号的参数化确实非常丰富。对于每个点,我都有一个树结构来计算要绘制的图像。当参数不依赖于我正在处理的数据时,正如我之前所说,我只是画了几次复杂的符号。

我试图实现一个缓存机制。我把已经绘制好的图片存储起来,如果遇到已经满足的配置,就获取图片再绘制。我所做的第一个测试是针对一个非常简单的符号。这是一个形状和内部都被填充的圆圈。

我知道这个符号在所有位置都是不变的,我缓存它然后从缓存的图像中再次绘制它。这有效......但我面临两个重要问题:

  • 绘制符号的质量几乎没有损坏。
  • 更多问题:使用缓存渲染地图所需的时间确实比没有缓存的时间要长。这对于缓存来说非常令人失望^_^

开启缓存机制时的核心代码如下:

if(pc.isCached(map)){
    BufferedImage bi = pc.getCachedValue(map);
    drawCachedImageOnGeometry(g2, sds, fid, selected, mt, the_geom, bi);
} else {
    BufferedImage bi = g2.getDeviceConfiguration().createCompatibleImage(200, 200);
    Graphics2D tg2 = bi.createGraphics();
    graphic.draw(tg2, map, selected, mt, AffineTransform.getTranslateInstance(100, 100));
    drawCachedImageOnGeometry(g2, sds, fid, selected, mt, the_geom, bi);
    pc.cacheSymbol(map, bi);
}

drawCachedImageOnGeometry 中唯一有趣的调用是

g2.drawRenderedImage(bi, AffineTransform.getTranslateInstance(x-100,y-100));

我已经尝试使用 VolatileImage 实例而不是 BufferedImage ......但这会导致更深层次的问题(我无法确定每次需要时都能正确渲染图像)。

我也做了一些分析,似乎在使用我的缓存时,花费时间最长的操作是在 awt 中进行的渲染操作。

也就是说,我想我的策略是错误的......因此,我的问题是:

  • 有没有什么有效的方法可以实现我解释的目标?
  • 更准确地说,存储用于绘制我的符号并根据需要翻译它们的 AWT 指令会更快吗?我假设有可能检索用于构建符号的“命令”......我没有在万维网上找到很多关于它的信息......如果可能的话,那将节省符号的计算时间(如前所述,这可能非常复杂)和符号的质量。

提前感谢您提供给我的所有信息和资源:-)

年龄。

编辑:这里有一些关于可以渲染的图形的细节。根据我正在实现的符号系统模型,图形可以非常简单(即带有形状的实心正方形)也可以非常复杂(例如,形状和填充都用影线绘制的标签,即使是光环如果我愿意,可以围绕它)。我想使用缓存,因为我确信在大多数配置中我将能够:

  • 区分用于绘制具有相同样式的相同源的两个不同符号的参数。
  • 确保具有相同参数的两个源(位置除外)将为相同的样式生成相同的符号,但在两个不同的位置(只需要翻译)。

由于这两点,缓存似乎是一个不错的策略。此外,可能有数千个重复的符号要绘制在同一图像中。

4

1 回答 1

2

你对你的绘图真正需要什么样的操作非常模糊,所以我只能给你一些非常笼统的指示。

1.) 绘制预渲染图像不一定比使用 Graphics2D 操作绘制相同图像更快。这在很大程度上取决于绘制图像所需的复杂性。作为极端情况,请考虑 fillRect() 与包含预渲染矩形的 Image 的 drawImage() (fillRect 仅写入目标像素,其中 drawImage 还需要从源复制)。

2.) 在大多数情况下,您永远不想直接弄乱 VolatileImage。BufferedImage 会自动利用 VolatileImage,除非你弄乱了 Image DataBuffer。如果您有许多预渲染的图像,您也可能会耗尽加速的视频内存,从而降低图像绘制性能。

3.) 预渲染图像的即时缩放/旋转等可能非常昂贵(取决于平台和当前的图形转换)。

4.) 您创建的“兼容图像”可能与绘图目标并不真正兼容。您获得的图像与默认屏幕设备兼容,这可能与多显示器设置中的实际目标不兼容。使用实际的目标组件 createImage() 可能会得到更好的结果。

编辑:

5.) 平移渲染操作的坐标可能会改变生成的目标像素。一个明显的情况是当坐标是非整数时(在坐标本身中或通过图形上设置的 AffineTransform 间接地)。此外,文本和可能的其他图元的抗锯齿可能会受到坐标的轻微影响(想到亚像素渲染)。


您可以尝试一种方法来区分符号的渲染速度是快还是慢。快的直接渲染,慢的被缓存。这里的主要问题是决定哪些是快/慢的,我希望这不是微不足道的决定。

另外,我想知道您何时说要渲染数千个符号,因为我想它们中的大多数应该被剪掉,因为只有一小部分图形适合窗口/框架?如果是这样的话,不要太在意缓存。完全在当前剪辑范围之外的绘图操作将相对便宜 - 图形目标真正为它们做的所有事情都是检测它们是否完全不可见以及它们什么时候不做任何事情。如果目标是生成要保存到磁盘/打印的图像(无论如何),我不会费心加快渲染速度,因为这是一个相对罕见的操作,实际打印可能远远超过渲染所需的时间无论如何,图表。

如果以上都不适用于您的情况,请注意您的缓存不会使用更多的时间/内存来确定缓存版本是否存在,而不是实际节省的渲染时间。您还需要考虑到,如果从不重用该图像,则构建缓存图像而不是直接渲染到目标确实会花费您一些时间。如果图像至少重复使用一次,最好是多次重复,缓存只能为您带来一些速度。

如果您通过组合原始渲染操作对象(例如有 Rectangle、Halo 和 Text 渲染对象子类)从原始操作构建符号,您可能希望为它们中的每一个分配一个成本指标,并且只缓存那些超过一些的符号(到确定)成本阈值。此外,为每个原始操作实现 hashCode() 并为 fast(er) equals 检测实现符号本身可能是一个好主意。

于 2012-07-13T15:11:04.677 回答