applyImageBackground method

Future<Uint8List?> applyImageBackground(
  1. Uint8List frameData, {
  2. required Image backgroundImage,
  3. required int width,
  4. required int height,
  5. required Uint8List mask,
})

Apply image background replacement.

Mirrors React's image compositing:

ctx.drawImage(results.segmentationMask, 0, 0, width, height);
ctx.globalCompositeOperation = "source-out";
ctx.fillStyle = ctx.createPattern(virtualImage, 'no-repeat');
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = "destination-atop";
ctx.drawImage(results.image, 0, 0, width, height);

Implementation

Future<Uint8List?> applyImageBackground(
  Uint8List frameData, {
  required ui.Image backgroundImage,
  required int width,
  required int height,
  required Uint8List mask,
}) async {
  try {
    // Decode the frame
    final codec = await ui.instantiateImageCodec(
      frameData,
      targetWidth: width,
      targetHeight: height,
    );
    final frameImage = (await codec.getNextFrame()).image;

    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder);
    final size = Size(width.toDouble(), height.toDouble());

    // Create mask image
    final maskImage =
        await _bytesToImage(mask, width, height, isGrayscale: true);
    if (maskImage == null) {
      frameImage.dispose();
      return frameData;
    }

    // Step 1: Draw background image (scaled to fill)
    final bgRect = Rect.fromLTWH(0, 0, size.width, size.height);
    final srcRect = Rect.fromLTWH(
      0,
      0,
      backgroundImage.width.toDouble(),
      backgroundImage.height.toDouble(),
    );
    canvas.drawImageRect(backgroundImage, srcRect, bgRect, Paint());

    // Step 2: Draw person over background using mask
    canvas.saveLayer(bgRect, Paint());
    canvas.drawImage(frameImage, Offset.zero, Paint());
    canvas.drawImage(
        maskImage, Offset.zero, Paint()..blendMode = BlendMode.dstIn);
    canvas.restore();

    // Finalize
    final picture = recorder.endRecording();
    final resultImage = await picture.toImage(width, height);
    final byteData =
        await resultImage.toByteData(format: ui.ImageByteFormat.rawRgba);

    frameImage.dispose();
    maskImage.dispose();
    resultImage.dispose();

    return byteData?.buffer.asUint8List();
  } catch (e) {
    debugPrint('applyImageBackground error: $e');
    return frameData;
  }
}