Flutter 使用 Image.memory 加载位图数据

news/2025/2/21 8:07:40/

0.前言

Image.memory 可以通过 Uint8List 存储的图像字节数据来构造一个 Image 部件,数据的内容需要包含格式头,裸数据他是没法解析的。

Uint8List? bytes;
int width;
int height;Image.memory(bytes!,width: width.toDouble(),height: height.toDouble(),// 无间隔播放,防止闪烁gaplessPlayback: true,// 保持宽高比例fit: BoxFit.contain,
)

如果是从文件读取的带格式头的数据,可以直接使用;如果是自己生成或者处理过的位图裸数据,可以自己编码生成位图格式的数据。

1.使用 image 工具包进行编码

链接:image | Dart Package (flutter-io.cn)

先用命令安装这个包:

flutter pub add image

主要用到两个接口: Image.fromBytes 和 BmpEncoder.encode

import 'dart:typed_data';
import 'package:flutter/material.dart';// 先安装 image 组件:flutter pub add image
import 'package:image/image.dart' as image_lib;void main() {runApp(const MyApp());
}class MyApp extends StatefulWidget {const MyApp({super.key});@overrideState<MyApp> createState() => _MyApp();
}class _MyApp extends State<MyApp> {Uint8List? _bytes;int _width = 0;int _height = 0;@overrideWidget build(BuildContext context) {// MaterialApp 创建一个基于 Material Design 风格的应用return MaterialApp(title: "Test Flutter",// Scaffold 是一个布局,有 appBar body persistentFooterButtons 等区域home: Scaffold(backgroundColor: Colors.white,// ConstrainedBox 对其子组件添加额外的约束body: ConstrainedBox(// 如果没传宽高参数就填充父组件constraints: const BoxConstraints.expand(),child: _bytes == null? null// memory 从内存加载数据,bytes 参数需要带格式头: Image.memory(_bytes!,width: _width.toDouble(),height: _height.toDouble(),// 无间隔播放,防止闪烁gaplessPlayback: true,// 保持宽高比例fit: BoxFit.contain,)),persistentFooterButtons: [TextButton(child: const Text("Update"),onPressed: () {// 点击按钮生成位图数据并显示createAndUpdate();},),],),);}void createAndUpdate() {// 填充位图数据,单通道int width = 255;int height = 255;Uint8List bytes = Uint8List(width * height);for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {bytes[i * width + j] = (i + j) ~/ 2;}}updateImage(width, height, bytes);}void updateImage(int imgWidth, int imgHeight, Uint8List imgBytes) {// https://pub.dev/documentation/image/latest/image/Image/Image.fromBytes.htmlimage_lib.Image img = image_lib.Image.fromBytes(width: imgWidth,height: imgHeight,bytes: imgBytes.buffer,rowStride: imgWidth,numChannels: 1,format: image_lib.Format.uint8);image_lib.Encoder encoder = image_lib.BmpEncoder();setState(() {_width = imgWidth;_height = imgHeight;_bytes = encoder.encode(img);});}
}

2.自己根据位图格式填充格式头

位图格式相对比较简单,可以自己根据图片信息生成格式头:

import 'dart:typed_data';
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatefulWidget {const MyApp({super.key});@overrideState<MyApp> createState() => _MyApp();
}class _MyApp extends State<MyApp> {Uint8List? _bytes;int _width = 0;int _height = 0;@overrideWidget build(BuildContext context) {// MaterialApp 创建一个基于 Material Design 风格的应用return MaterialApp(title: "Test Flutter",// Scaffold 是一个布局,有 appBar body persistentFooterButtons 等区域home: Scaffold(backgroundColor: Colors.white,// ConstrainedBox 对其子组件添加额外的约束body: ConstrainedBox(// 如果没传宽高参数就填充父组件constraints: const BoxConstraints.expand(),child: _bytes == null? null// memory 从内存加载数据,bytes 参数需要带格式头: Image.memory(_bytes!,width: _width.toDouble(),height: _height.toDouble(),// 无间隔播放,防止闪烁gaplessPlayback: true,// 保持宽高比例fit: BoxFit.contain,)),persistentFooterButtons: [TextButton(child: const Text("Update"),onPressed: () {// 点击按钮生成位图数据并显示createAndUpdate();},),],),);}void createAndUpdate() {// 填充位图数据int width = 255;int height = 255;int channel = 3;Uint8List bytes = Uint8List(width * height * channel);for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {for (int k = 0; k < channel; k++) {bytes[i * width * channel + j * channel + k] = (i + j) ~/ 2;}}}updateImage(width, height, bytes);}void updateImage(int imgWidth, int imgHeight, Uint8List imgBytes) {BMP24 encoder = BMP24(imgWidth, imgHeight);Uint8List bmp = encoder.encode(imgBytes);setState(() {_width = imgWidth;_height = imgHeight;_bytes = bmp;});}
}// 给位图数据添加格式头,并进行字节对齐
class BMP24 {final int _headerSize = 54;// channel 3 = 24 位 RGB888final int _channel = 3;final int _width;final int _height;late Uint8List _bmp;BMP24(this._width, this._height) {// 位图需要四字节对齐// assert((_width * _channel) & 3 == 0);int rowStride = 4 * ((_width * _channel * 8 + 31) ~/ 32);int fileSize = _headerSize + rowStride * _height;debugPrint("stride $rowStride, file size $fileSize .");_bmp = Uint8List(fileSize);ByteData bd = _bmp.buffer.asByteData();// -- 14 字节位图文件头bd.setUint8(0, 0x42); // 固定两字节格式头,字母 Bbd.setUint8(1, 0x4d); // 固定两字节格式头,字母 Mbd.setUint32(2, fileSize, Endian.little); // 文件大小// 2 保留字段// 2 保留字段bd.setUint32(10, _headerSize, Endian.little); // 图片数据开始字节偏移// -- 40 字节位图信息头bd.setUint32(14, 40, Endian.little); // 信息头字节长度bd.setUint32(18, _width, Endian.little); // 图片的像素宽度bd.setUint32(22, -_height, Endian.little); // 图片的像素高度,bmp 的填充是上下颠倒的bd.setUint16(26, 1, Endian.little); // 为目标设备说明颜色平面数,为1bd.setUint16(28, 8 * _channel, Endian.little); // 一个像素点的位数bd.setUint16(30, 0); // 压缩类型,0不压缩bd.setUint32(34, rowStride * _height, Endian.little); // 数据字节大小// 4 水平分辨率,有符号数,单位像素/米// 4 垂直分辨率,有符号数,单位像素/米// 4 颜色索引数// 4 重要颜色索引数}// 图像数据Uint8List encode(Uint8List bytes) {int size = _width * _height * _channel;assert(bytes.length == size);// 如果本来就是四字节对齐的,可以直接 copy// _bmp.setRange(_headerSize, _headerSize + size + 1, bytes);int rowStride = 4 * ((_width * _channel * 8 + 31) ~/ 32);// 逐行填充,四字节对齐for (int row = 0; row < _height; row++) {var subs =bytes.sublist(row * _width * _channel, (row + 1) * _width * _channel);_bmp.setRange(_headerSize + row * rowStride,_headerSize + row * rowStride + _width * _channel, subs);}return _bmp;}
}

 

 


http://www.ppmy.cn/news/916223.html

相关文章

基于JAVA的局域网聊天软件的设计与实现(仿制QQ)

一、系统分析 1.1 问题描述 客户端 实现简易版的局域网聊天器实现富文本内容聊天智能聊天机器人群发消息传送文件等功能 服务器端 实现群发通知管理聊天线程 1.2 系统功能分析 客户端功能 登陆注册发送表情消息发送文本消息截取图片图片处理震动效果发送文件群发消息设置聊天…

局域网限制网速软件_为什么员工反感局域网管理软件

局域网管理软件对于老板来说可以说是十分的喜爱的&#xff0c;但是它在员工的心目中就没有那么高的分量的&#xff0c;身为员工还都很讨厌这款软件&#xff0c;这到底是为什么呢&#xff1f;下面我们就来听听员工到底心里是怎么想的吧&#xff0c;了解一下他们是为什么不喜欢这…

FTP服务器搭建(局域网版+广域网版)

目录 什么是FTP服务器本地FTP服务器搭建&#xff08;简易版&#xff09;FTP服务器发布至外网&#xff08;Serv-u版&#xff09; 什么是FTP服务器 FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机&#xff0c;它们依照FTP协议提供服务。 F…

查询局域网电脑的IP,端口号,MAC地址(黑客技术入门)

原文地址&#xff1a;http://blog.csdn.net/qq_21792169/article/details/51954815 网上看到很多都是使用nmap工具&#xff0c;这个工具我没有使用过&#xff0c;我自己实现nmap工具的功能&#xff0c;首先我们查询局域网内有哪些电脑是alive的&#xff0c;下面我写了一个脚本&…

Socket通信原理和QQ的基本实现

文章目录 前言一、网络中进程之间通信方式二、Socket的基本使用1.Socket函数2.bind()函数3.listen()、connect()函数4.accept()函数5.read()、write()、close()函数 三、QQ的基本实现C语言版本C版本 前言 最近在看Socket网络编程这一部分&#xff0c;本来以为跟教程下来就算了…

使用ARP欺骗, 截取局域网中任意一台机器的网页请求,破解用户名密码等信息

转载自https://www.cnblogs.com/xcj26/articles/6073411.html 阅读目录 ARP欺骗的作用  ARP欺骗  什么是网关  ARP协议是什么  单向ARP欺骗  双向ARP欺骗  开始ARP欺骗  让局域网中的任意一台计算机或者手机断线  zxarps实现双向截取数据&#xff0c;并修改网页…

基于(LinuxC语言)的udp局域网聊天室

【使用说明与相关缺陷】 //1.关于两个文件夹的说明&#xff1a;(这部分是源文件压缩包的说明&#xff0c;这边没发所以可以忽略 ) 文件夹 udp_聊天室 的内容是本次项目的内容. 文件夹 tcp_select 的内容是基于tcp和select写的服务器转发代码. …

Visual Studio 2019离线安装教程(脱机安装、局域网安装)

文章目录 项目场景:问题描述:解决方案:1.下载vs引导程序2.创建文件夹用来存放安装包文件3. 找到自己需要安装的vs工作负载和组件id4. 下载离线安装包参考文献项目场景: 因为公司的业务要求,需要在内网环境进行开发和测试。 问题描述: Visual Studio 需要下载到内网安装…