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

2022-09-01 22:44:09

你看到了吗?

enter image description here

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

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

我只是将其调整为使用.SwingpaintComponent

问题: - 使用JavaFX:我的计算机有问题。您在图片上看到的内容需要2秒钟才能加载,然后它移动得非常缓慢和生涩。- 与:我不知道如何根据亮度,阴影等调整每个块。所以它看起来像这样:Swing

enter image description here

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


答案 1

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

如果您坚持使用JavaFX实现,可以尝试以下几种方法:

  1. 使用 node.setCache(true) 打开节点缓存。
  2. 使用 node.setCacheHint(CacheHint.SPEED) 启用高速节点转换。
  3. 提供一个没有效果的实现和另一个有效果的实现,看看没有效果的实现是否表现得更好。(如果是这种情况,您可能需要使用更有效的效果链或完全放弃某些效果)。
  4. 检查 JavaFX 系统要求,以确保您的设置在硬件/操作系统/驱动程序组合方面满足硬件加速的最低要求。
  5. 如果您使用的是区域和基于 css 的处理,请注意 css 系统不会造成太多开销(例如,通过拥有不使用 css 的代码版本并比较其性能)。
  6. 如果需要,实现一定程度的细节缩放(例如,当缩小时,为整个房屋提供单个图像,而不是为每个房屋瓷砖提供单独的图像)。
  7. 确保只加载一次给定的图像,并在多个图像视图中重复使用它。
  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 机制重新实现它。也许您可以通过在ImageView节点上将缓存设置为true并将cacheHint设置为速度来获得相同的性能级别,因为我相信这在幕后做了类似的事情(即以增加内存使用为代价来提高速度)。


JavaFX 场景图非常高效,可以处理数千个节点。但是,您的应用程序中可能有多个。如果上述优化点对您没有帮助,您可能需要计算节点数量,并将对您的问题(也许还有一些来源)的引用发布到open-jfx邮件列表中,其中了解JavaFX应用程序优化细节的JavaFX开发人员就在那里。

JavaFX 8对3D场景图的支持远远优于JavaFX 2.2中的(大部分无用的)3D场景图。您当前的应用程序似乎是伪3D,您可以在其中转换2D对象,单独应用照明效果并调整每个图块的亮度以获得3D外观。如果是这种情况,使用具有统一3D照明模型的完全硬件加速的3D场景图最终可能会表现得更好,看起来更好,更易于使用 - 您必须在应用程序的上下文中对其进行评估,以查看是否值得将您的应用程序从2D JavaFX场景图切换到3D JavaFX场景图,或者从JavaFX切换到Swing或其他一些技术,如libgdx

补遗

对Mizur的一些其他问题的回答:

那么,为什么它们如此耗电呢?

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

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

就仅消耗资源来存储您移动的内容而言,JavaFX 场景图对此进行了一些优化,而像缓存之类的东西可以进一步提高性能。也许它们对于您的特定用例或您使用系统的方式还不够。您可能需要更改算法以减少系统负载。

有没有办法使用这种 Imageview 技术生成每个建筑物的图像,然后将这些建筑物的图像集成到最终场景中?

是 - 您可以使用许多图像和节点将建筑物渲染到屏幕外场景,拍摄屏幕外场景的快照以创建单个图像,然后将该图像叠加在最终场景上。


答案 2

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

我自己没有用过它,但这里有一个提示 - 使用Swing / AWT Kernel + ConvolveOp机制:http://www.java-tips.org/java-se-tips/java.awt.image/styling-digital-images-with-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);

操作通过 3x3 矩阵(数据)定义。您可以搜索brithness,blurence等示例。

图片花了2秒钟来加载,但它移动得很慢,之后很生涩

使用脏矩形算法:脏矩形


推荐