【问题标题】:How do I Render Individual Pixels in Flutter?如何在 Flutter 中渲染单个像素?
【发布时间】:2019-09-14 13:50:27
【问题描述】:

设置

我正在使用自定义RenderBox 进行绘图。
下面代码中的canvas对象来自PaintingContext in the paint method

绘图

我正在尝试使用Canvas.drawRect 单独渲染像素
我应该指出,它们有时比它们实际占据的屏幕像素更大,有时更小。

for (int i = 0; i < width * height; i++) {
  // in this case the rect size is 1
  canvas.drawRect(
      Rect.fromLTWH(index % (width * height), 
          (index / (width * height)).floor(), 1, 1), Paint()..color = colors[i]);
}

存储

我将像素存储为 List&lt;List&lt;Color&gt;&gt;(上面代码中的colors)。我之前尝试过不同的嵌套列表,但它们在性能方面并没有引起任何明显的差异。 当使用999x999 图像填充列表时,我的 Android 模拟器测试设备上的内存增加了282.7MB。请注意,它只是暂时增加282.7MB。大约半分钟后,增幅下降到 153.6MB 并保持在那里(没有任何用户交互)。

渲染

999x999 的分辨率下,上面的代码导致250.1 ms/frameGPU 最大值1835.9 ms/frameUI 最大值,这显然是不可接受的。尝试绘制999x999 图像时,UI 会冻结两秒钟,考虑到 4k 视频在同一设备上流畅运行,这应该是小菜一碟(我猜)。

CPU

我不确定如何使用 Android 分析器正确跟踪此问题,但在填充或更改列表时,即绘制像素(对于以上指标),CPU 使用率从0% 上升到60%。以下是 AVD 性能设置:

原因

我不知道从哪里开始,因为我什至不确定我的代码的哪一部分会导致冻结。是内存使用吗?还是图纸本身?
我一般会怎么做?我究竟做错了什么?我应该如何存储这些像素。

努力

我尝试了很多都没有帮助,所以我将尝试只指出最值得注意的:

  • 我尝试将 List&lt;List&lt;Color&gt;&gt; 转换为 Image from the dart:ui library 希望使用 Canvas.drawImage。为了做到这一点,我尝试编码我自己的 PNG,但 I have not been able to render more than a single row。但是,看起来这不会提高性能。在尝试转换 9999x9999 图像时,我遇到了内存不足异常。现在,我想知道视频是如何渲染的,因为任何 4k 视频都会比 9999x9999 图像占用更多内存,如果它在内存中只有几秒钟。

  • 我尝试实现image 包。然而,我在完成它之前就停下来了,因为我注意到它并不是要在 Flutter 中使用,而是在 HTML 中使用。使用它我不会有任何收获。

  • 这对于我将得出的以下结论非常重要:我尝试仅绘制而不存储像素,即使用Random.nextInt 生成随机颜色.当尝试随机生成999x999 图像时,这导致1824.7 ms/framesGPU 最大值2362.7 ms/frameUI 最大值,这更糟,尤其是在 GPU 部门。

结论

这是我在尝试使用Canvas.drawImage 进行渲染失败之前得出的结论:Canvas.drawRect 不适用于此任务,因为它甚至无法绘制简单的图像。

你如何在 Flutter 中做到这一点?

注意事项

  • 这基本上是what I tried to ask over two months ago(是的,我一直在努力解决这个问题),但我认为我当时没有正确表达自己,我更不知道实际问题是什么。

  • 我可以正确渲染的最高分辨率约为10k 像素。我至少需要1m

  • 我认为放弃 Flutter 并转向原生可能是我唯一的选择。但是,我想相信我完全错误地解决了这个问题。我花了大约三个月的时间试图弄清楚这一点,但我没有找到任何能引导我到任何地方的东西。

【问题讨论】:

  • instantiateImageCodec 表示支持 BMP 格式,这相对容易从原始 ARGB 数据进行编码 - 尽管我不知道这种 ARGB->BMP 编码和 BMP->图像解码的速度有多快成为...
  • 还有:你的Layer 树有多大?
  • [+9 毫秒] ????要在运行时进行热重载更改,请按“r”。要热重启(和重建状态),请按“R”。 [ +2 ms] SM T110 上的 Observatory 调试器和分析器可在以下网址获得:127.0.0.1:41854 [ ] 有关更详细的帮助消息,请按“h”。要分离,请按“d”;要退出,请按“q”。 [+129309 ms] 正在初始化热重载...

标签: android dart flutter


【解决方案1】:

解决方案

dart:ui 具有将像素轻松转换为Image 的功能:decodeImageFromPixels

当我创建这个答案时,我根本没有意识到这一点,这就是我写“Alternative”部分的原因。

另类

感谢@pslink 在我写下我未能对自己的PNG 进行编码之后提醒我BMP
我之前研究过它,但我认为如果没有足够的文档,它看起来很复杂。现在,我发现this nice article 解释了必要的BMP 标头,并通过复制Example 2 from the "BMP file format" Wikipedia article 实现了32 位BGRA(ARGB 但BGRA 是默认掩码的顺序)。我浏览了所有来源,但找不到此示例的原始来源。也许维基百科文章的作者自己写的。

结果

使用Canvas.drawImage 和我的999x999 像素从BMP 字节列表转换为图像,我得到9.9 ms/frameGPU 最大值UI 最大值 7.1 ms/frame 的strong>,太棒了

|  ms/frame |  Before (Canvas.drawRect) | After (Canvas.drawImage) |
|-----------|---------------------------|--------------------------|
|  GPU max  | 1824.7                    | 9.9                      |
|  UI max   | 2362.7                    | 7.1                      |

结论

Canvas.drawRect 之类的画布操作不适合那样使用。

说明

首先,这很简单,但是,您需要正确填充字节列表,否则,您将收到一个错误,即您的数据格式不正确并且看不到任何结果,这可能会非常令人沮丧.

您需要在绘制之前准备好图像,因为您不能在绘图调用中使用async 操作。

在代码中,您需要使用Codec 将字节列表转换为图像。

final list = [
            0x42, 0x4d, // 'B', 'M'
            ...];
// make sure that you either know the file size, data size and data offset beforehand
//        or that you edit these bytes afterwards

final Uint8List bytes = Uint8List.fromList(list);

final Codec codec = await instantiateImageCodec(bytes));

final Image image = (await codec.getNextFrame()).image;

您需要将此图像传递给您的绘图小部件,例如使用FutureBuilder
现在,您可以在绘图调用中使用Canvas.drawImage

【讨论】:

  • aah,我以前用过很多次,但没有看到任何数字(只是图表) - 现在我也看到了数字,非常感谢,顺便说一句,如果你想获得最大的可用性能,你可以查看examples/layers/raw/spinning_square.dart 或该文件夹中的任何其他来源
猜你喜欢
  • 2021-06-02
  • 2022-11-01
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 2020-11-07
  • 1970-01-01
  • 2014-12-05
相关资源
最近更新 更多