Mobile Development 12 min read

PowerImage: High‑Performance Flutter Image Library Using FFI and Texture Integration

PowerImage is a stable, high‑performance Flutter image library that combines native FFI and texture integration to eliminate cache conflicts, enable simulator support, provide custom image channels, preload and animate images, unify memory management, reduce Dart code, achieve extensive test coverage, and invite open‑source contributions for future enhancements.

Xianyu Technology
Xianyu Technology
Xianyu Technology
PowerImage: High‑Performance Flutter Image Library Using FFI and Texture Integration

Background: After a series of gray‑scale releases, bug fixes and code optimizations, the next‑generation image library PowerImage is now fully stable in the Xianyu app. Compared with its predecessor IFImage, PowerImage addresses several pain points such as cache conflicts with native ImageCache, inability to display images on simulators, and the need for a separate image channel outside the gallery.

Introduction: PowerImage fully exploits native image library capabilities and offers high extensibility for Flutter. It cleverly combines external texture with an FFI solution to stay close to native design.

Key Features

Supports loading ui.Image directly, enabling use cases where external texture alone cannot provide an image.

Provides image pre‑loading similar to native precacheImage .

Introduces texture cache that integrates with native image cache, unifying memory management.

Works on simulators (pre‑Flutter‑1.23.0‑18.1.pre).

Custom image‑type channel for business‑specific image retrieval.

Comprehensive exception capture and reporting.

Supports animated images.

Flutter native solution: The native Image widget obtains an ImageStream via an ImageProvider , uses frameBuilder , loadingBuilder , and finally rebuilds a RawImage which draws through RenderImage using ImageInfo and its ui.Image .

New generation solution – FFI:

iOS side (native) obtains image parameters:

_rowBytes = CGImageGetBytesPerRow(cgImage);
    CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage);
    CFDataRef rawDataRef = CGDataProviderCopyData(dataProvider);
    _handle = (long)CFDataGetBytePtr(rawDataRef);
    NSData *data = CFBridgingRelease(rawDataRef);
    self.data = data;
    _length = data.length;

Dart side creates ui.Image from the native memory:

@override
FutureOr
createImageInfo(Map map) {
  Completer
completer = Completer
();
  int handle = map['handle'];
  int length = map['length'];
  int width = map['width'];
  int height = map['height'];
  int rowBytes = map['rowBytes'];
  ui.PixelFormat pixelFormat = ui.PixelFormat.values[map['flutterPixelFormat'] ?? 0];
  Pointer
pointer = Pointer
.fromAddress(handle);
  Uint8List pixels = pointer.asTypedList(length);
  ui.decodeImageFromPixels(pixels, width, height, pixelFormat,
      (ui.Image image) {
    ImageInfo imageInfo = ImageInfo(image: image);
    completer.complete(imageInfo);
    // release native memory
    PowerImageLoader.instance.releaseImageRequest(options);
  }, rowBytes: rowBytes);
  return completer.future;
}

While FFI avoids the lack of ui.Image in texture mode, decodeImageFromPixels incurs an extra memory copy, prompting two optimization directions: let Flutter decode the data to reduce copy peaks, or work with the Flutter team to cut the copy internally.

Texture solution: Custom TextureImage implements ui.Image solely for cache size calculation, avoiding direct ui.Image usage in ImageCache :

import 'dart:typed_data';
import 'dart:ui' as ui show Image;
import 'dart:ui';

class TextureImage implements ui.Image {
  int _width;
  int _height;
  int textureId;
  TextureImage(this.textureId, int width, int height)
      : _width = width,
        _height = height;

  @override
  void dispose() {}

  @override
  int get height => _height;

  @override
  Future
toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) {
    throw UnimplementedError();
  }

  @override
  int get width => _width;
}

Overall architecture: PowerImageProvider abstracts both FFI and texture paths, producing appropriate ImageInfo . PowerImageLoader offers unified loading and releasing. The system also supports custom image types (e.g., album) and custom render types, enabling flexible extensions.

Data comparison (FFI vs Texture) on iPhone 11 Pro with 300 network images shows similar memory fluctuations (≈186 MB vs 194 MB) in Flutter 2.5.3 release mode. UI‑thread latency is best with texture, while raster‑thread latency favors PowerImage over IFImage.

Code reduction: The Dart side shrank considerably thanks to the native‑aligned design, and the FFI approach complements texture limitations while keeping the codebase concise.

Testing: Core code is covered by unit tests with ~95 % line coverage, ensuring stability.

Open source: The project invites community contributions, provides issue templates, and enforces CI with flutter test before merging PRs.

Future: PowerImage will continue evolving, favoring the FFI path as Flutter advances, adding features like loadingBuilder support, animated‑image handling, and Kotlin/Swift extensions.

FlutterperformanceMobileDevelopmentFFIImageProcessing
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.