Flutter Raw Image Provider

一般情况下,考虑网络传输效率,会采用算法来压缩这个数据,故而你会看到有各种各样的图像压缩算法和文件格式。

你可能会问什么情况下会有需要直接去加载一张图的原始rgba数据?

这里举个简单例子:分块加载图片。将图片解码后,分割成一个个矩形区域,每个矩形就有一个 raw rgba 数据,将其交给Image渲染,这样做可以降低一定的GPU 内存压力,减少出现GPU OOM 或黑屏的概率。

要支持 raw rgba ,其实很简单,在 dart:ui包下有个方法decodeImageFromPixels可以直接使用,前提是需要有原始的二进制数据、宽、高。

import 'dart:ui';

Future decodeRawRgba(ByteData bytes, int width, int height) {

final Completer completer = Completer();

decodeImageFromPixels(

bytes.buffer.asUint8List(),

width,

height,

PixelFormat.rgba8888,

completer.complete,

);

return completer.future;

}

有了这个 Image(dart:ui)对象就可以交给 RawImage Widget 来加载了。但RawImage太过于底层了,能不能只用 Image Widget呢?因为需要复用 LoadingBuilder这些逻辑。

当然可以。查看一下 Image Widget 的构造函数就知道,我们需要一个 ImageProvider,那么问题进一步简化到如何写一个ImageProvider 支持 raw rgba 数据。

实现一个 ImageProvider,我们需要实现 load这个关键方法。以MemoryImage为例:

class MemoryImage extends ImageProvider {

@override

ImageStreamCompleter load(MemoryImage key, DecoderCallback decode) {

return MultiFrameImageStreamCompleter(

codec: _loadAsync(key, decode),

scale: key.scale,

debugLabel: 'MemoryImage(${describeIdentity(key.bytes)})',

);

}

Future<ui.Codec> _loadAsync(MemoryImage key, DecoderCallback decode) {

return decode(bytes);

}

}

很显然,我们需要想一个方法构造出raw rgba 数据的 Codec。

其实秘密就在 decodeImageFromPixels这个方法实现里:

void decodeImageFromPixels(

Uint8List pixels,

int width,

int height,

PixelFormat format,

ImageDecoderCallback callback, {

int? rowBytes,

int? targetWidth,

int? targetHeight,

bool allowUpscaling = true,

}) {

if (targetWidth != null) {

assert(allowUpscaling || targetWidth <= width);

}

if (targetHeight != null) {

assert(allowUpscaling || targetHeight <= height);

}

ImmutableBuffer.fromUint8List(pixels)

.then((ImmutableBuffer buffer) {

final ImageDescriptor descriptor = ImageDescriptor.raw(

buffer,

width: width,

height: height,

rowBytes: rowBytes,

pixelFormat: format,

);

if (!allowUpscaling) {

if (targetWidth != null && targetWidth! > descriptor.width) {

targetWidth = descriptor.width;

}

if (targetHeight != null && targetHeight! > descriptor.height) {

targetHeight = descriptor.height;

}

}

descriptor

.instantiateCodec(

targetWidth: targetWidth,

targetHeight: targetHeight,

)

.then((Codec codec) => codec.getNextFrame())

.then((FrameInfo frameInfo) => callback(frameInfo.image));

});

}

先从数据构造出ImageDescriptor,再把descriptor.instantiateCodec()这一步抽出来就可以获取 raw rgba 数据的 Codec,进而实现一个自己的RawImageProvider了。

如:

相关推荐
好的佩奇9 小时前
Dart 之类型系统
flutter·dart
恋猫de小郭15 小时前
Dart 3.15 新特性:this 终于支持类型提升了
android·前端·flutter
FungLeo2 天前
成为全栈·Flutter App 篇·主题与可访问性:深浅色、大字号和触控体验
flutter·移动开发·深色模式·可访问性·成为全栈·大字号适配
用户95421573334852 天前
只是选个照片,怎么留下了这么多访问记录?Flutter 图片选择器排查与优化实录
flutter
FungLeo3 天前
成为全栈·Flutter App 篇·第四批复盘:复用了什么,移动端又新增了什么
flutter·移动开发·跨端开发·成为全栈·api契约·全栈复盘
jason.zeng@15022073 天前
使用vscode+flutter开发android程序(入门)
android·vscode·flutter
jason.zeng@15022073 天前
Flutter 项目标准分层目录(repository/dto/page,对齐后端思想)
flutter
程序员老刘4 天前
跨平台开发地图 | 2026年10月
flutter·ai编程·客户端
林瞅瞅4 天前
Flutter 开发环境从零搭建与调试记录
flutter
FungLeo5 天前
成为全栈·Flutter App 篇·Markdown 编辑器:编辑、预览与离开保护
flutter·移动开发·markdown编辑器·表单设计·成为全栈·草稿保存