Java 渲染大位图的小区域非常耗时

Java 渲染大位图的小区域非常耗时,java,android,bitmap,Java,Android,Bitmap,我有以下代码 this.getGame().getGraphics().drawBitmap(Assets.playScreen_LaserBeamAnimation, new Rect(0, 0, 100, 750), new Rect(0, 0, 100, 750), null); this.getGame().getGraphics().drawBitmap(Assets.playScreen_LaserBeamAnimation, new Rect(0, 200, 10, 800), n

我有以下代码

this.getGame().getGraphics().drawBitmap(Assets.playScreen_LaserBeamAnimation, new Rect(0, 0, 100, 750), new Rect(0, 0, 100, 750), null);
this.getGame().getGraphics().drawBitmap(Assets.playScreen_LaserBeamAnimation, new Rect(0, 200, 10, 800), new Rect(0, 0, 200, 600), null);
第一个render语句大约需要
0.6-1秒的时间来渲染。
第二个在
1毫秒左右

位图很大:
968KB
,加载的代码如下:

public Bitmap readAssetsBitmap(String filename) throws IOException {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options(); 
            options.inPurgeable = true;
            Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
            if(bitmap == null) {
                WSLog.d(Game.GAME_ENGINE_TAG, this,"File cannot be opened: It's value is null");
                throw new IOException("File cannot be opened: It's value is null");
            }
            else {
                WSLog.d(Game.GAME_ENGINE_TAG, this,"File successfully read: " + filename);
                return bitmap;
            }
        } 
        catch (IOException e) {
            WSLog.d(Game.GAME_ENGINE_TAG, this,"File cannot be opened: " + e.getMessage());
            throw new IOException("File cannot be opened: " + e.getMessage());
        }

    }
我希望读取图像和设置位图需要一些时间,但是渲染一小部分图像不会花费太长的时间(奇怪的是,这只是第一次渲染调用)

如何避免第一次渲染花费如此长的时间


注意:它与缩放无关(我相信)。

看起来您正试图使用包含所有帧的大型位图绘制动画。如果是这种情况,那么缩小图像的比例可能对您不起作用

但是,如果图像中有n个离散帧,那么为什么不在加载图像时创建n个位图,并在加载应用程序时将其存储在
ArrayList
中呢?这样,您就可以预剪切图像,而不必担心在渲染时对图像进行切片

在渲染时,可以将当前位图替换为要绘制的位图,也可以(比使用缩放绘制要快得多)

这可能需要更多内存,但可能会提供明显的加速


更新: 为什么这么慢。Java图形真的很蠢。我的猜测是,在第一个示例(长示例)中,当它看到区域具有相同的比例时,它会迭代整个大图像,并且每当它在给定区域中找到一个像素时,它就会在目标区域中绘制它

在快速绘制中,我的猜测是,它看到比例不同,然后反向绘制:对于目标区域中的每个像素,它计算出原始图像中要参考的像素,并相应地混合到该正方形中

由于目标区域中的像素要少得多,因此该算法的速度要快得多


图形真的很糟糕。您是否研究过其他第三方图像处理库?

TL;DR加载和渲染图形实际上是一组相对昂贵的(内部)操作,当图形首次渲染到屏幕时,首先加载到内存中,然后加载到ARM CPU的图形模块中。此外,动画GIF被扩展为一组图像,每个动画帧对应一个图像,使其比您想象的要大得多。为了提高速度,可以考虑减少动画尺寸(像素和帧数),或者强迫GIF更早地进入图形存储器以避免第一帧暂停。
Android要在内部渲染游戏图形需要做很多事情,特别是如果你有GIF动画:

  • 从(相对较慢的)RAM加载图像
  • 解压缩文件
  • 将256色模型映射到RGBA空间
  • 从GIF文件中的图像增量生成动画的每一帧
  • 创建一个带有计时器的线程,以便在GIF的不同帧之间切换
现在我们在CPU内存中有一个图像(实际上是一组帧)。请注意,包含大量帧的非常小的GIF可能会成为非常大的内存-Android必须为GIF中的每个帧都有一个完整大小的图像。此外,256位颜色模型在内部扩展为RGBA,因此每个像素有4个字节

例如,一个带有动画小形状(32x32像素)的GIF(128x128像素)。让我们进一步假设动画中有100个步骤-这可能是10公里左右,取决于背景的简单程度。但是,在内存中展开后,动画中的每一帧都是4x128x128=64kB。您有100帧,因此内存大小为6MB

下一步:

  • 如果使用3D,我们必须将图像编译为图形模块的内部纹理格式
  • 将数据流到图形内存中(这需要惊人的长时间-这就是为什么大多数3D游戏在游戏段之间有那些“加载…”页面)
  • 将图像从图形模块的格式更改为实际屏幕的颜色深度(例如RGB格式具有24位颜色,但LCD显示器通常具有18或20位颜色深度。图形卡必须转换图像,并且通常引入抖动以模拟附加颜色)
一旦图像(实际上是一组帧)加载到图形模块中,事情就会变得很快

好的,那你能做些什么呢?以下是一些建议:

  • 减少GIF的像素大小,或减少动画中的帧数
  • 预加载
  • 如果图像较大,则平铺图像
减小GIF的大小可以显著减少内存使用,这将减少Android将帧推入图形模块所需的时间以及编译成纹理的成本。这样做的缺点是,您可能无权修改图形

预加载通过在游戏加载阶段强制渲染,提前将图像推入图形模块。图像可能在屏幕外(将窗口设置为隐藏),或者通过设置低Z顺序位于顶层后面,或者位于1像素区域内


平铺在中讨论)。以谷歌地球为例,这是一个最极端的例子,它在地球上的每一个区域都使用瓷砖(以任何给定的分辨率)。为了只渲染您感兴趣的部分,Google Earth必须只加载要显示的分幅。因为瓷砖很小,所以装载速度很快。然而,在你的情况下,你可能有一个GIF动画,所以这是行不通的。平铺动画可能不实用,或者可能会导致图像部分的渲染出现伪影或延迟。

。在内存中加载缩小的版本。缩放图像更改顺序,您将知道是因为预加载还是缩放差异