Skip to content

[Web][Canvaskit] Implement ImageFilter.compose #120123

@olksij

Description

@olksij

Steps to Reproduce

  1. Execute flutter run -d chrome on the code sample provided below
  2. Debug console throws an exception: ImageFilter.compose not implemented for CanvasKit.

Expected results

On non-web devices ImageFilter.compose() composes two ImageFilters and renders the image properly:
Screenshot 2023-02-06 at 20 41 36
Since I passed an unmodified matrix to ImageFilter, the image renders in its expected look withou render library blocking the render. The same behavior is expected on Web platform.

Actual results

The following UnimplementedError was thrown during paint():
ImageFilter.compose not implemented for CanvasKit.

(See Logs section for details)

Possible solutions:

  • Mention the unavailability of ImageFilter.compose on Web platform in the documentation.
  • Implement the ImageFilter.compose in CanvasKit.
Code sample
import 'dart:ui' as UI;
import 'package:http/http.dart' as http;

import 'package:flutter/material.dart';

void main() => runApp(const MainApp());

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  static const url = 'https://docs.flutter.dev/assets/images/dash/Dash.png';

  static Future<UI.Image> loadImage() async {
    final response = await http.get(Uri.parse(url));
    return await decodeImageFromList(response.bodyBytes);
  }

  @override
  Widget build(context) => FutureBuilder(
    future: loadImage(),
    builder: (_, snapchot) => snapchot.hasData
      ? CustomPaint(painter: Painter(snapchot.data!))
      : const SizedBox(),
  );
}

class Painter extends CustomPainter {
  final UI.Image image;

  Painter(this.image);
  
  @override
  void paint(Canvas canvas, Size size) {    
    final sampleFilter1 = UI.ImageFilter.matrix(Matrix4.identity().storage);
    final sampleFilter2 = UI.ImageFilter.matrix(Matrix4.identity().storage);

    final paint = Paint()
      ..imageFilter = UI.ImageFilter.compose(inner: sampleFilter1, outer: sampleFilter2);

    canvas.drawImage(image, Offset.zero, paint);
  }

  @override
  bool shouldRepaint(oldDelegate) => false;
}
Logs
  • Full exception log
    Because of Github Issue character limit and lack of useful information I decided to skip verbose output
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
The following UnimplementedError was thrown during paint():
ImageFilter.compose not implemented for CanvasKit.

The relevant error-causing widget was:
  CustomPaint
  CustomPaint:file:///Users/oleksiibesida/Documents/filtercompose/flutter_application_1/lib/main.dart:22:9

When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49      throw_
lib/_engine/engine/canvaskit/renderer.dart 172:5                                  composeImageFilters
lib/ui/painting.dart 436:21                                                       compose
packages/sample/main.dart 38:38                                                   <fn>
packages/sample/main.dart 38:88                                                   paint
packages/flutter/src/rendering/custom_paint.dart 571:12                           [_paintWithPainter]
packages/flutter/src/rendering/custom_paint.dart 613:7                            paint
packages/flutter/src/rendering/object.dart 2853:7                                 [_paintWithContext]
packages/flutter/src/rendering/object.dart 253:12                                 paintChild
packages/flutter/src/rendering/view.dart 216:14                                   paint
packages/flutter/src/rendering/object.dart 2853:7                                 [_paintWithContext]
packages/flutter/src/rendering/object.dart 169:10                                 _repaintCompositedChild
packages/flutter/src/rendering/object.dart 112:5                                  repaintCompositedChild
packages/flutter/src/rendering/object.dart 1137:31                                flushPaint
packages/flutter/src/rendering/binding.dart 518:19                                drawFrame
packages/flutter/src/widgets/binding.dart 865:13                                  drawFrame
packages/flutter/src/rendering/binding.dart 381:5                                 [_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1289:15                               [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1218:9                                handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1076:5                                [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1168:13                               invoke
lib/_engine/engine/platform_dispatcher.dart 218:5                                 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 190:45                                     <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 367:37  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 372:39  dcall

The following RenderObject was being processed when the exception was fired: RenderCustomPaint#927e0:
  creator: CustomPaint ← FutureBuilder<Image> ← MainApp ← [root]
  parentData: <none>
  constraints: BoxConstraints(w=1325.0, h=736.0)
  size: Size(1325.0, 736.0)
  painter: Painter#bcb22()
This RenderObject has no descendants.
════════════════════════════════════════════════════════════════════════════════════════════════════
  • flutter doctor -v logs
[✓] Flutter (Channel stable, 3.7.0, on macOS 13.2 22D49 darwin-arm64, locale en-SE)
    • Flutter version 3.7.0 on channel stable at /Users/oleksiibesida/Documents/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision b06b8b2710 (2 weeks ago), 2023-01-23 16:55:55 -0800
    • Engine revision b24591ed32
    • Dart version 2.19.0
    • DevTools version 2.20.1

[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14C18
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] VS Code (version 1.75.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.58.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-arm64   • macOS 13.2 22D49 darwin-arm64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 109.0.5414.119

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

Metadata

Metadata

Labels

P2Important issues not at the top of the work listc: parityWorks on one platform but not anotherc: proposalA detailed proposal for a change to Fluttere: web_canvaskitCanvasKit (a.k.a. Skia-on-WebGL) rendering backend for Webengineflutter/engine related. See also e: labels.platform-webWeb applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions