Flutter图片合并插件image_combine的使用
Flutter图片合并插件image_combine的使用
image_combine
是一个用于通过Rust原生代码垂直合并图像的Flutter FFI插件。
特性
- 使用Rust原生代码快速合并图像
使用方法
import 'package:image_combine/image_combine.dart';
void main() async {
// 初始化插件
await ImageCombine.instance.initialize();
// 垂直合并图像
final result = await ImageCombine.instance.mergeImagesVertically(
imageBuffers: [image1Bytes, image2Bytes, image3Bytes],
maxSizeKb: BigInt.from(2048),
);
}
性能基准测试
测试条件
- 三个分辨率为3024×4032的图像
- 每个图像大小约为2MB
- 未指定最大尺寸
- 在性能模式下进行测试
测试结果
设备 | 处理时间 |
---|---|
Pixel 8 Pro | 约900毫秒 |
iPhone 15 Pro Max | 约500毫秒 |
iPad Pro M4 | 约400毫秒 |
Macbook Pro M1 Max | 约500毫秒 |
已测试平台
- ✅ Android
- ✅ iOS
- ✅ MacOS
完整示例Demo
以下是一个完整的示例代码,展示了如何使用 image_combine
插件来准备和合并图像。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_combine/image_combine.dart';
import 'package:image/image.dart' as img;
import 'package:flutter/foundation.dart';
Future<void> main() async {
await ImageCombine.instance.initialize();
runApp(
const MaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isLoading = false;
Uint8List? result;
int? mergeTime;
List<Uint8List>? preparedImages;
List<img.Image?>? decodedImages;
Future<void> _prepareImages() async {
setState(() => isLoading = true);
try {
final images = await _prepareImagesInBackground();
setState(() {
preparedImages = images;
decodedImages = images.map((image) => img.decodeImage(image)).toList();
isLoading = false;
});
} catch (e) {
_handleError('Error preparing images: $e');
}
}
static Future<List<Uint8List>> _prepareImagesInBackground() async {
final assetPaths = [
'assets/demo/receipt_1.jpeg',
'assets/demo/receipt_1.1.jpeg',
'assets/demo/receipt_2.jpeg',
];
// 加载所有图像
final images = await Future.wait(
assetPaths.map((path) async {
final data = await rootBundle.load(path);
return data.buffer.asUint8List();
}),
);
return images;
}
Future<void> _mergeImages() async {
setState(() => isLoading = true);
try {
final stopwatch = Stopwatch()..start();
final mergedResult =
await compute(_mergeImagesInBackground, preparedImages!);
stopwatch.stop();
if (mergedResult == null) {
throw Exception('Failed to merge images');
}
setState(() {
result = mergedResult;
isLoading = false;
mergeTime = stopwatch.elapsedMilliseconds;
});
} catch (e) {
_handleError('Error merging images: $e');
}
}
static Future<Uint8List?> _mergeImagesInBackground(
List<Uint8List> images) async {
await ImageCombine.instance.initialize();
return ImageCombine.instance.mergeImagesVertically(
imageBuffers: images,
maxSizeKb: BigInt.from(2048),
);
}
void _handleError(String message) {
setState(() => isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Image Merger')),
body: Builder(
builder: (context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildPrepareButton(),
if (preparedImages != null) _buildImagePreview(),
if (preparedImages != null && result == null)
_buildMergeButton(),
if (mergeTime != null) _buildMergeTimeInfo(),
if (result != null) Image.memory(result!),
],
),
);
},
),
);
}
Widget _buildPrepareButton() {
return TextButton(
onPressed: preparedImages != null || isLoading ? null : _prepareImages,
child: isLoading && preparedImages == null
? const CircularProgressIndicator()
: const Text('Prepare Images'),
);
}
Widget _buildMergeButton() {
return TextButton(
onPressed: isLoading ? null : _mergeImages,
child: isLoading
? const CircularProgressIndicator()
: const Text('Merge Images'),
);
}
Widget _buildImagePreview() {
return SizedBox(
height: 200,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var i = 0; i < preparedImages!.length; i++)
_buildImageThumbnail(i),
],
),
),
);
}
Widget _buildImageThumbnail(int index) {
final decodedImage = decodedImages![index];
final fileSizeInKB =
(preparedImages![index].lengthInBytes / (1024)).toStringAsFixed(2);
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SizedBox(
width: 100,
height: 150,
child: Image.memory(
preparedImages![index],
fit: BoxFit.contain,
cacheHeight: 150,
cacheWidth: 100,
// 防止在重建期间闪烁
gaplessPlayback: true,
),
),
Text(
'Size: ${decodedImage?.width}x${decodedImage?.height}\n'
'File: $fileSizeInKB KB',
style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
),
);
}
Widget _buildMergeTimeInfo() {
return Column(
children: [
Text('Merge time: $mergeTime ms, profile mode will be faster'),
if (result != null)
Text('Result size: ${result!.lengthInBytes / (1024)} KB'),
],
);
}
}
更多关于Flutter图片合并插件image_combine的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
1 回复
更多关于Flutter图片合并插件image_combine的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用image_combine
插件来合并图片的示例代码。这个插件允许你将多张图片合并成一张图片。以下是一个简单的示例,展示了如何安装和使用这个插件。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加image_combine
依赖:
dependencies:
flutter:
sdk: flutter
image_combine: ^x.y.z # 请将x.y.z替换为最新版本号
然后运行flutter pub get
来获取依赖。
2. 导入包
在你的Dart文件中导入image_combine
包:
import 'package:image_combine/image_combine.dart';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:flutter/material.dart';
3. 使用ImageCombine合并图片
下面是一个完整的示例,展示了如何使用ImageCombine
将两张图片合并成一张:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Image Combine Example'),
),
body: Center(
child: ImageCombineExample(),
),
),
);
}
}
class ImageCombineExample extends StatefulWidget {
@override
_ImageCombineExampleState createState() => _ImageCombineExampleState();
}
class _ImageCombineExampleState extends State<ImageCombineExample> {
Uint8List? combinedImageBytes;
@override
void initState() {
super.initState();
_combineImages();
}
Future<void> _combineImages() async {
// 加载图片资源,这里使用网络图片作为示例
final image1 = NetworkImage('https://example.com/image1.png');
final image2 = NetworkImage('https://example.com/image2.png');
final completer1 = Completer<ui.Image>();
final completer2 = Completer<ui.Image>();
image1.image.resolve(const ImageConfiguration()).addListener(
ImageStreamListener((ImageInfo imageInfo, bool synchronousCall) {
final ui.Image img = imageInfo.image;
completer1.complete(img);
}),
);
image2.image.resolve(const ImageConfiguration()).addListener(
ImageStreamListener((ImageInfo imageInfo, bool synchronousCall) {
final ui.Image img = imageInfo.image;
completer2.complete(img);
}),
);
await Future.wait([completer1.future, completer2.future]);
final img1 = completer1.future.result!;
final img2 = completer2.future.result!;
// 创建ImageCombine实例
final imageCombine = ImageCombine();
// 将图片转换为ByteData
final byteData1 = await img1.toByteData(format: ui.ImageByteFormat.png);
final byteData2 = await img2.toByteData(format: ui.ImageByteFormat.png);
// 将ByteData转换为Uint8List
final Uint8List imageBytes1 = byteData1!.buffer.asUint8List();
final Uint8List imageBytes2 = byteData2!.buffer.asUint8List();
// 添加图片到ImageCombine
imageCombine.addImage(imageBytes1);
imageCombine.addImage(imageBytes2, position: Offset(img1.width, 0)); // 将第二张图片放在第一张图片的右侧
// 获取合并后的图片
combinedImageBytes = imageCombine.combine();
// 更新UI
setState(() {});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (combinedImageBytes != null)
Image.memory(combinedImageBytes!),
Text('Loading images...'),
],
);
}
}
注意事项
- 图片资源:上面的示例使用了网络图片。如果你使用本地图片资源,可以使用
AssetImage
代替NetworkImage
。 - 图片位置:在
imageCombine.addImage
方法中,你可以通过position
参数来设置图片的位置。 - 图片格式:在将
ui.Image
转换为ByteData
时,你可以根据需要选择图片格式(如PNG或JPEG)。
这个示例展示了如何使用image_combine
插件将多张图片合并成一张图片,并在Flutter应用中显示合并后的图片。希望这对你有所帮助!