Flutter Raw Image Provider

简介: Flutter 中的 Image Widget 内置支持 file、network、memory三种形式的文件。但这几种都只支持常规的经过压缩后的图片文件或二进制数据,如jpg、png、webp文件等。并没有支持原始的rgba 二进制数据。这里说的原始二进制数据是指图像的每个像素的色彩值所组成的字节数组。一张图有宽x高个像素点,一个像素点的色彩值用32bit来存储,分为4个通道,每个通道各占用8bit,分别为红、绿、蓝、透明度(RGBA),这个数组就是每个像素点色彩值的集合,dart 中一般用Uint8List。

Flutter 中的 Image Widget 内置支持 file、network、memory三种形式的文件。

但这几种都只支持常规的经过压缩后的图片文件或二进制数据,如jpg、png、webp文件等。并没有支持原始的rgba 二进制数据。

这里说的原始二进制数据是指图像的每个像素的色彩值所组成的字节数组。一张图有宽x高个像素点,一个像素点的色彩值用32bit来存储,分为4个通道,每个通道各占用8bit,分别为红、绿、蓝、透明度(RGBA),这个数组就是每个像素点色彩值的集合,dart 中一般用Uint8List。


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


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


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


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

import 'dart:ui';
Future<Image> decodeRawRgba(ByteData bytes, int width, int height) {
  final Completer<Image> completer = Completer<Image>();
  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<MemoryImage> {
  @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了。

如:

class RawImageProvider extends ImageProvider<_RawImageKey> {
  final RawImageData image;
  /// see [ui.decodeImageFromPixels]
  Future<ui.Codec> _loadAsync(_RawImageKey key) async {
    var buffer = await ui.ImmutableBuffer.fromUint8List(image.pixels);
    final descriptor = ui.ImageDescriptor.raw(
      buffer,
      width: image.width,
      height: image.height,
      pixelFormat: image.pixelFormat,
    );
    return descriptor.instantiateCodec(
        targetWidth: targetWidth, targetHeight: targetHeight);
  }

如果你恰好也有这个需要,可以直接添加 pub 依赖

dependencies:
    raw_image_provider: ^0.1.0
相关文章
|
3月前
|
开发工具 开发者
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
本文深入探讨了 Flutter 中 Provider 的高级用法,涵盖多 Provider 组合、Selector 优化性能、ChangeNotifierProxyProvider 管理依赖关系以及自定义 Provider。通过这些技巧,开发者可以构建高效、可维护的响应式应用。
123 2
|
4月前
【Flutter】状态管理:Provider状态管理
【Flutter】状态管理:Provider状态管理
34 0
|
4月前
|
开发工具 开发者
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
|
6月前
|
缓存
Flutter Image从网络加载图片刷新、强制重新渲染
Flutter Image从网络加载图片刷新、强制重新渲染
223 1
|
6月前
Flutter 状态管理新境界:多Provider并行驱动UI
Flutter 状态管理新境界:多Provider并行驱动UI
96 0
|
6月前
|
Dart API
状态管理的艺术:探索Flutter的Provider库
状态管理的艺术:探索Flutter的Provider库
67 0
|
9月前
|
存储 JavaScript 前端开发
【Flutter 前端技术开发专栏】Flutter 中的状态管理框架(如 Provider、Redux 等)
【4月更文挑战第30天】本文探讨了 Flutter 开发中的状态管理,重点介绍了 Provider 和 Redux 两种框架。Provider 以其简单易用性适合初学者和小项目,而 Redux 则适用于大型复杂应用,保证状态一致性。此外,还提到了 Riverpod 和 BLoC 等其他框架。选择框架时要考虑项目规模、团队技术水平和个人偏好。文章通过购物车应用示例展示了不同框架的使用,并展望了状态管理框架的未来发展。
216 0
【Flutter 前端技术开发专栏】Flutter 中的状态管理框架(如 Provider、Redux 等)
|
9月前
|
存储 UED 开发者
Flutter的状态管理:setState、Provider、Bloc的使用详解
【4月更文挑战第26天】Flutter状态管理详解:涵盖setState基础,Provider的跨组件共享及Bloc的复杂场景处理。了解这三种方法的优缺点,助力优化应用数据一致性与用户体验。当状态管理需求升级,从简单的setState到Provider的便利,再到Bloc的强大功能,开发者可根据项目规模和复杂度选择合适策略。
|
9月前
|
存储 前端开发
Flutter Provider状态管理---MVVM架构实战
Flutter Provider状态管理—MVVM架构实战 在Flutter中,状态管理是一个非常重要的概念。Flutter Provider是一种状态管理的解决方案,它提供了一种简单,灵活和高效的方法来管理Flutter应用程序中的状态。本文将详细介绍Flutter Provider的使用,以及如何在MVVM架构中使用它。
444 0
|
存储 Android开发
Flutter控件之图片Image封装
Flutter中偏偏原生的控件,少了很多需要又常用的属性,比如宽高,比如内外边距,又比如点击事件,如果不采取封装,视图的结构会一层嵌套一层,徒增很多的冗余代码,所以,为了简洁代码,还有为了拓展原生组件没有的属性,就不得不进行一次简单的封装,使其在调用的时候,可以很方便的实现某些功能。
161 0

热门文章

最新文章

  • 1
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 2
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 6
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 7
    零基础构建即时通讯开源项目OpenIM移动端-Flutter篇
  • 8
    flutter3-dart3-dymall原创仿抖音(直播+短视频+聊天)商城app系统模板
  • 9
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 10
    【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 1
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    13
  • 2
    零基础构建即时通讯开源项目OpenIM移动端-Flutter篇
    58
  • 3
    flutter3-dart3-dymall原创仿抖音(直播+短视频+聊天)商城app系统模板
    38
  • 4
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    141
  • 5
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 6
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 7
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    121
  • 8
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    75
  • 9
    flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
    18
  • 10
    【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    27