在Java中显示数百万图像的最佳方法是什么?

你看到了吗?

在此处输入图像描述

每个房子的每块砖都是16×16像素的图像。

你可以在这里看到一个基于简单JavaFX的版本,一些Imageview在X和Y上移动以产生“构造”的效果。

我只是使用paintComponent改编为Swing

问题: – 使用JavaFX:我的电脑有问题。 您在图片上看到的内容需要2秒才能加载,然后移动速度非常慢且不稳定。 – 使用Swing :我不知道如何根据亮度,阴影等来调整每个块。所以它看起来像这样:

在此处输入图像描述

我应该选择什么方法? 两者都有很大的缺点。 我本来希望保留JavaFX方法,但也想找到比Imageview东西。 这应该不是一个好主意。

有关Swing优化和实现的信息,请参阅其他答案,因为我的答案是JavaFX特定的。

如果你坚持使用JavaFX实现,这里有几件事要尝试:

  1. 使用node.setCache(true)打开节点缓存。
  2. 使用node.setCacheHint(CacheHint.SPEED)启用高速节点转换。
  3. 提供一个没有效果的实现,另一个带有效果的实现,看看没有效果的实现是否明显更好。 (如果是这种情况,您可能需要使用更有效的效果链或完全放弃一些效果)。
  4. 检查JavaFX系统要求 ,确保您的设置满足硬件/操作系统/驱动程序组合方面的硬件加速的最低要求。
  5. 如果您正在使用Regions和基于css的处理,请注意css系统不会导致太多开销(例如,通过使用不使用css并比较其性能的代码版本)。
  6. 如果需要,实现一定程度的细节缩放(例如,当缩小时为整个房屋提供单个图像而不是为每个房屋图块提供单独的图像)。
  7. 确保您只加载一次给定的图像并在多个ImageView中重复使用它。
  8. 使用分析器识别瓶颈。
  9. 改变测试CPU或显卡,看看其中任何一个是否是瓶颈。
  10. 尝试Java 8预览 ,其中包含许多针对JavaFX的内部性能优化 。
  11. 如果要加载大量图像,请使用背景加载,并在加载图像时在前面播放动画或进度条。
  12. 如果要加载大量图像,请在Image构造函数中预先缩放它们,这样它们就不会占用太多内存,或者需要(可能)额外像素的额外处理能力(如果你使用了很多像素,那么这只是一个考虑因素)不同的非常高分辨率的纹理 – 你似乎没有这样做。

例如:

 Image tile = new Image("tile.png"); Group house = new Group(); house.setCache(true); house.setCacheHint(CacheHint.SPEED); Effect lighting = new Lighting(); for (int i = 0; i < houseWidth; i++) { // here is the critical part => don't do new ImageView(new Image("tile.png")) ImageView tileView = new ImageView(tile)); tileView.setEffect(lighting); tileView.setCache(true); tileView.setCacheHint(CacheHint.SPEED); house.add(tileView); } 

曼特里德的建议很有意思。 我相信,使用JavaFX,您不需要自己实现脏矩形算法(因为底层平台能够为您处理脏区域处理)。 可能是因为它是一种通用机制,它不提供特定情况所需的优化级别,在这种情况下,您需要自己处理脏处理(例如,从场景图中删除节点并重新添加)他们酌情)。

此外,预先计算图像上的模糊/亮度等可以通过在JavaFX中定义效果,将效果应用于屏幕外ImageView节点,然后拍摄屏幕外ImageView节点的快照来获得预先计算的图像来完成。 此技术允许您重用现有的JavaFX效果管道,而无需使用ConvolveOp机制重新实现它。 也许您可以通过将缓存设置为true并将cacheHint设置为在ImageView节点上加速来获得相同的性能级别,因为我相信这在场景背后会发生类似的事情(即以增加内存使用量为代价提高速度)。


JavaFX场景图非常高效,可以处理数千个节点。 但是,您的应用程序可能不止这些。 如果上述优化点对您没有帮助,您可能需要计算节点数并将问题(可能还有一些来源)的引用发布到open-jfx邮件列表中 ,其中JavaFX开发人员知道JavaFX应用程序的详细信息优化是。

与JavaFX 2.2中的(通常无用的)3D场景图形相比,JavaFX 8对3D场景图形的支持要好得多。 您的当前应用程序似乎是psuedo-3d,您可以在其中转换2D对象,单独应用光照效果并调整每个图块的亮度以获得3D外观。 如果是这种情况,使用具有统一3D照明模型的全硬件加速3D场景图可能最终表现更好,看起来更好并且更容易使用 – 您必须在应用程序的上下文中评估它以查看它是否是值得将您的应用程序从2D JavaFX场景图切换到3D JavaFX场景图,或从JavaFX切换到Swing或其他一些技术,如libgdx 。

附录

回答一些Mizur的其他问题:

那他们为什么这么耗电呢?

从我的回答中可以看出,有许多方面涉及到性能,因此挑出一个特定的原因,为什么某些东西是耗电的,有时是困难的,往往是不可能的。 有时它是导致性能问题的组合。 最大的胜利通常来自优化内部循环中执行的内容或通过更改用于解决问题的策略。

如果他们需要这么多,请让我移动瓷砖,或者只是显示,不是吗?

就仅消耗移动资源的资源而言,JavaFX场景图对此进行了一些优化,而像cachehints这样的东西可以进一步提高性能。 也许它们不足以满足您的特定用例或您使用系统的方式。 您可能需要更改算法以减少系统负载。

有没有办法用Imageview技术生成每个建筑物的图像,然后在最终场景中整合这些建筑物的图像?

是 – 您可以使用许多图像和节点将建筑物渲染到屏幕外场景, 对屏幕外场景进行快照以创建单个图像,然后将该图像覆盖在最终场景上。

在图像被绘制之前修改模糊/亮度/等

我自己没有使用它,但这里有一个提示 – 使用Swing / AWT Kernel + ConvolveOp机制: http : //www.java-tips.org/java-se-tips/java.awt.image/styling-digital-图像与- convolveop.html

 BufferedImage biSrc = ... BufferedImage biDest = ... float[] data = new float[] { 0.0625f, 0.125f, 0.0625f, 0.125f, 0.25f, 0.125f, 0.0625f, 0.125f, 0.0625f }; Kernel kernel = new Kernel(3, 3, data); ConvolveOp convolve = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); convolve.filter(biSrc, biDest); 

操作通过3×3矩阵(数据)定义。 你可以搜索brigthness,blur等的例子。

这张照片需要2秒才能加载,但之后移动非常缓慢而且生涩

使用脏矩形算法: 脏矩形