You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS平台Dart/Flutter中dart:ui.Image的toByteData方法性能问题求助

临时解决方案:iOS上dart:ui.Image转PNG字节数据优化

针对iOS上dart:ui.Image.toByteData(format: ImageByteFormat.png)的性能问题,在官方修复合并前,你可以尝试以下几种临时解决方案:

1. 使用image包进行纯Dart编码

通过先将dart:ui.Image转换为image库的Image对象,再用该库的PNG编码方法替代原生实现,能显著提升速度。

步骤:

  1. 添加依赖到pubspec.yaml:
dependencies:
  image: ^4.0.17 # 或最新版本
  1. 转换与编码代码:
import 'dart:ui' as ui;
import 'package:image/image.dart' as img;

// 将dart:ui.Image转换为image库的Image对象
img.Image _convertUiImageToImage(ui.Image uiImage) {
  final ui.ByteData? rawByteData = uiImage.toByteData(format: ui.ImageByteFormat.rawRgba);
  if (rawByteData == null) throw Exception('Failed to retrieve raw image data');
  
  final Uint8List pixels = rawByteData.buffer.asUint8List();
  return img.Image.fromBytes(
    width: uiImage.width,
    height: uiImage.height,
    bytes: pixels.buffer,
    format: img.Format.rgba,
  );
}

// 编码为PNG字节数据
Uint8List encodeUiImageToPng(ui.Image uiImage) {
  final img.Image image = _convertUiImageToImage(uiImage);
  return img.encodePng(image);
}

说明:

rawRgba格式的转换远快于直接转PNG,再通过image库优化后的PNG编码逻辑,能大幅缩短耗时,且无需编写原生代码。

2. 通过原生通道(MethodChannel)实现iOS端编码

直接调用iOS系统API进行PNG编码,绕过Flutter引擎当前的性能瓶颈。

Dart端代码:

import 'dart:ui' as ui;
import 'package:flutter/services.dart';

final MethodChannel _imageEncoderChannel = MethodChannel('com.example.image_encoder');

Future<Uint8List?> encodeUiImageToPngNative(ui.Image uiImage) async {
  try {
    final ui.ByteData? rawData = uiImage.toByteData(format: ui.ImageByteFormat.rawRgba);
    if (rawData == null) return null;
    
    final Uint8List pixels = rawData.buffer.asUint8List();
    return await _imageEncoderChannel.invokeMethod<Uint8List>(
      'encodeToPng',
      {
        'width': uiImage.width,
        'height': uiImage.height,
        'pixels': pixels,
      },
    );
  } on PlatformException catch (e) {
    print('PNG encoding failed: ${e.message}');
    return null;
  }
}

iOS端Swift代码:

  1. 创建编码器处理类:
import UIKit
import Flutter

class ImageEncoderHandler: NSObject, FlutterPlugin {
    static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "com.example.image_encoder", binaryMessenger: registrar.messenger())
        let instance = ImageEncoderHandler()
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

    func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard call.method == "encodeToPng" else {
            result(FlutterMethodNotImplemented)
            return
        }
        
        guard let args = call.arguments as? [String: Any],
              let width = args["width"] as? Int,
              let height = args["height"] as? Int,
              let pixelData = args["pixels"] as? Data else {
            result(FlutterError(code: "InvalidArgs", message: "Missing or invalid arguments", details: nil))
            return
        }
        
        // 创建CGImage并编码为PNG
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        
        guard let context = CGContext(
            data: nil,
            width: width,
            height: height,
            bitsPerComponent: 8,
            bytesPerRow: width * 4,
            space: colorSpace,
            bitmapInfo: bitmapInfo.rawValue
        ) else {
            result(FlutterError(code: "ContextError", message: "Failed to create CGContext", details: nil))
            return
        }
        
        context.data?.copyMemory(from: pixelData.baseAddress!, byteCount: pixelData.count)
        guard let cgImage = context.makeImage() else {
            result(FlutterError(code: "ImageError", message: "Failed to create CGImage", details: nil))
            return
        }
        
        let uiImage = UIImage(cgImage: cgImage)
        guard let pngData = uiImage.pngData() else {
            result(FlutterError(code: "EncodeError", message: "Failed to encode PNG", details: nil))
            return
        }
        
        result(pngData)
    }
}
  1. 在AppDelegate中注册通道:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    ImageEncoderHandler.register(with: self.registrar(forPlugin: "ImageEncoderHandler"))
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

说明:

直接调用iOS系统的UIImage.pngData()方法,性能优于当前Flutter引擎的实现,但需要维护原生代码。

3. 提前压缩图片

如果业务允许,在获取dart:ui.Image前先压缩图片的尺寸或质量,从根源减少转换耗时:

  • 加载本地/网络图片时,使用支持压缩的库(如image_picker、cached_network_image)设置压缩参数
  • 用image库对图片进行缩放、质量压缩后再转换为dart:ui.Image

内容的提问来源于stack exchange,提问作者Atreidex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 10:55:22