From a749820e53865777eef9ffdd396edb626a3bef75 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Wed, 1 Mar 2023 16:01:04 -0800 Subject: [PATCH] Skwasm Renderer - initial implementation (#39072) Skwasm Renderer - initial implementation --- ci/licenses_golden/licenses_flutter | 52 ++++ common/config.gni | 3 - lib/web_ui/dev/build.dart | 50 ++- lib/web_ui/dev/steps/compile_tests_step.dart | 47 ++- lib/web_ui/dev/steps/run_tests_step.dart | 2 +- lib/web_ui/dev/test_dart2wasm.js | 19 +- lib/web_ui/dev/test_platform.dart | 19 +- lib/web_ui/dev/test_runner.dart | 6 +- lib/web_ui/dev/utils.dart | 6 +- lib/web_ui/lib/geometry.dart | 4 + .../lib/src/engine/js_interop/js_promise.dart | 7 +- lib/web_ui/lib/src/engine/renderer.dart | 2 +- .../lib/src/engine/skwasm/skwasm_impl.dart | 17 + .../src/engine/skwasm/skwasm_impl/canvas.dart | 294 ++++++++++++++++++ .../src/engine/skwasm/skwasm_impl/image.dart | 47 +++ .../src/engine/skwasm/skwasm_impl/paint.dart | 93 ++++++ .../engine/skwasm/skwasm_impl/paragraph.dart | 155 +++++++++ .../src/engine/skwasm/skwasm_impl/path.dart | 259 +++++++++++++++ .../skwasm/skwasm_impl/path_metrics.dart | 90 ++++++ .../engine/skwasm/skwasm_impl/picture.dart | 61 ++++ .../skwasm/skwasm_impl/raw/raw_canvas.dart | 139 +++++++++ .../skwasm/skwasm_impl/raw/raw_geometry.dart | 12 + .../skwasm/skwasm_impl/raw/raw_memory.dart | 165 ++++++++++ .../skwasm/skwasm_impl/raw/raw_paint.dart | 63 ++++ .../skwasm/skwasm_impl/raw/raw_path.dart | 203 ++++++++++++ .../skwasm_impl/raw/raw_path_metrics.dart | 51 +++ .../skwasm/skwasm_impl/raw/raw_picture.dart | 47 +++ .../skwasm/skwasm_impl/raw/raw_surface.dart | 36 +++ .../engine/skwasm/skwasm_impl/renderer.dart | 113 ++++--- .../engine/skwasm/skwasm_impl/surface.dart | 26 ++ .../engine/skwasm/skwasm_impl/vertices.dart | 38 +++ lib/web_ui/skwasm/BUILD.gn | 56 ++++ lib/web_ui/skwasm/canvas.cpp | 185 +++++++++++ lib/web_ui/skwasm/contour_measure.cpp | 56 ++++ lib/web_ui/skwasm/export.h | 7 + lib/web_ui/skwasm/helpers.h | 28 ++ lib/web_ui/skwasm/paint.cpp | 80 +++++ lib/web_ui/skwasm/path.cpp | 197 ++++++++++++ lib/web_ui/skwasm/picture.cpp | 38 +++ lib/web_ui/skwasm/surface.cpp | 197 ++++++++++++ lib/web_ui/skwasm/wrappers.h | 34 ++ lib/web_ui/test/canvaskit/canvas_test.dart | 28 -- .../test/{engine => ui}/canvas_test.dart | 8 +- .../{engine => ui}/path_metrics_test.dart | 4 +- .../test/{engine => ui}/picture_test.dart | 4 + third_party/canvaskit/BUILD.gn | 44 ++- third_party/canvaskit/canvaskit.gni | 8 - tools/gn | 33 +- wasm/BUILD.gn | 4 +- web_sdk/BUILD.gn | 76 ++--- web_sdk/sdk_rewriter.dart | 4 + web_sdk/test/sdk_rewriter_test.dart | 1 + 52 files changed, 2985 insertions(+), 233 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart create mode 100644 lib/web_ui/skwasm/BUILD.gn create mode 100644 lib/web_ui/skwasm/canvas.cpp create mode 100644 lib/web_ui/skwasm/contour_measure.cpp create mode 100644 lib/web_ui/skwasm/export.h create mode 100644 lib/web_ui/skwasm/helpers.h create mode 100644 lib/web_ui/skwasm/paint.cpp create mode 100644 lib/web_ui/skwasm/path.cpp create mode 100644 lib/web_ui/skwasm/picture.cpp create mode 100644 lib/web_ui/skwasm/surface.cpp create mode 100644 lib/web_ui/skwasm/wrappers.h delete mode 100644 lib/web_ui/test/canvaskit/canvas_test.dart rename lib/web_ui/test/{engine => ui}/canvas_test.dart (98%) rename lib/web_ui/test/{engine => ui}/path_metrics_test.dart (99%) rename lib/web_ui/test/{engine => ui}/picture_test.dart (98%) delete mode 100644 third_party/canvaskit/canvaskit.gni diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index dda3662bd99e1..e917c5dc9af80 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1937,7 +1937,24 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codecs.dart ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/services/serialization.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/svg.dart + ../../../flutter/LICENSE @@ -1978,6 +1995,15 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/text.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/tile_mode.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/window.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/canvas.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/export.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/helpers.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/paint.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/path.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/picture.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/surface.cpp + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/skwasm/wrappers.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/runtime/dart_isolate.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/runtime/dart_isolate.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/runtime/dart_isolate_group_data.cc + ../../../flutter/LICENSE @@ -4436,7 +4462,24 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codecs.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/serialization.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/svg.dart @@ -4477,6 +4520,15 @@ FILE: ../../../flutter/lib/web_ui/lib/text.dart FILE: ../../../flutter/lib/web_ui/lib/tile_mode.dart FILE: ../../../flutter/lib/web_ui/lib/ui.dart FILE: ../../../flutter/lib/web_ui/lib/window.dart +FILE: ../../../flutter/lib/web_ui/skwasm/canvas.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/export.h +FILE: ../../../flutter/lib/web_ui/skwasm/helpers.h +FILE: ../../../flutter/lib/web_ui/skwasm/paint.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/path.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/picture.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/surface.cpp +FILE: ../../../flutter/lib/web_ui/skwasm/wrappers.h FILE: ../../../flutter/runtime/dart_isolate.cc FILE: ../../../flutter/runtime/dart_isolate.h FILE: ../../../flutter/runtime/dart_isolate_group_data.cc diff --git a/common/config.gni b/common/config.gni index 329f0bcdd0c2f..48f78fbbc17c2 100644 --- a/common/config.gni +++ b/common/config.gni @@ -16,9 +16,6 @@ declare_args() { # Whether to use a prebuilt Dart SDK instead of building one. flutter_prebuilt_dart_sdk = false - - # Whether to build the flutter web sdk outline/DDC artifacts. - flutter_build_web_sdk = false } # feature_defines_list --------------------------------------------------------- diff --git a/lib/web_ui/dev/build.dart b/lib/web_ui/dev/build.dart index 459445eaa1ba3..bf803b7f239f5 100644 --- a/lib/web_ui/dev/build.dart +++ b/lib/web_ui/dev/build.dart @@ -13,6 +13,14 @@ import 'environment.dart'; import 'pipeline.dart'; import 'utils.dart'; +const Map targetAliases = { + 'sdk': 'flutter/web_sdk', + 'web_sdk': 'flutter/web_sdk', + 'canvaskit': 'flutter/third_party/canvaskit:canvaskit_group', + 'canvaskit_chromium': 'flutter/third_party/canvaskit:canvaskit_chromium_group', + 'skwasm': 'flutter/lib/web_ui/skwasm', +}; + class BuildCommand extends Command with ArgUtils { BuildCommand() { argParser.addFlag( @@ -21,16 +29,6 @@ class BuildCommand extends Command with ArgUtils { help: 'Run the build in watch mode so it rebuilds whenever a change is ' 'made. Disabled by default.', ); - argParser.addFlag( - 'build-canvaskit', - help: 'Build CanvasKit locally instead of getting it from CIPD. Enabled ' - 'by default.', - defaultsTo: true - ); - argParser.addFlag( - 'build-canvaskit-chromium', - help: 'Build the Chromium variant of CanvasKit. Disabled by default.', - ); argParser.addFlag( 'host', help: 'Build the host build instead of the wasm build, which is ' @@ -46,22 +44,19 @@ class BuildCommand extends Command with ArgUtils { bool get isWatchMode => boolArg('watch'); - bool get buildCanvasKit => boolArg('build-canvaskit'); - - bool get buildCanvasKitChromium => boolArg('build-canvaskit-chromium'); - bool get host => boolArg('host'); + List get targets => argResults?.rest ?? []; + @override FutureOr run() async { final FilePath libPath = FilePath.fromWebUi('lib'); final List steps = [ - GnPipelineStep( - buildCanvasKit: buildCanvasKit, - buildCanvasKitChromium: buildCanvasKitChromium, - host: host, + GnPipelineStep(host: host), + NinjaPipelineStep( + buildDirectory: host ? environment.hostDebugUnoptDir : environment.wasmReleaseOutDir, + targets: targets.map((String target) => targetAliases[target] ?? target), ), - NinjaPipelineStep(target: host ? environment.hostDebugUnoptDir : environment.wasmReleaseOutDir), ]; final Pipeline buildPipeline = Pipeline(steps: steps); await buildPipeline.run(); @@ -86,13 +81,9 @@ class BuildCommand extends Command with ArgUtils { /// state. GN is pretty quick though, so it's OK to not support interruption. class GnPipelineStep extends ProcessStep { GnPipelineStep({ - required this.buildCanvasKit, - required this.buildCanvasKitChromium, required this.host, }); - final bool buildCanvasKit; - final bool buildCanvasKitChromium; final bool host; @override @@ -111,8 +102,6 @@ class GnPipelineStep extends ProcessStep { return [ '--web', '--runtime-mode=release', - if (buildCanvasKit) '--build-canvaskit', - if (buildCanvasKitChromium) '--build-canvaskit-chromium', ]; } } @@ -131,7 +120,7 @@ class GnPipelineStep extends ProcessStep { /// /// Can be safely interrupted. class NinjaPipelineStep extends ProcessStep { - NinjaPipelineStep({required this.target}); + NinjaPipelineStep({required this.buildDirectory, required this.targets}); @override String get description => 'ninja'; @@ -139,8 +128,10 @@ class NinjaPipelineStep extends ProcessStep { @override bool get isSafeToInterrupt => true; - /// The target directory to build. - final Directory target; + /// The directory to build. + final Directory buildDirectory; + + final Iterable targets; @override Future createProcess() { @@ -149,7 +140,8 @@ class NinjaPipelineStep extends ProcessStep { 'autoninja', [ '-C', - target.path, + buildDirectory.path, + ...targets, ], ); } diff --git a/lib/web_ui/dev/steps/compile_tests_step.dart b/lib/web_ui/dev/steps/compile_tests_step.dart index 381bdfcff3cd9..19b9759e785c0 100644 --- a/lib/web_ui/dev/steps/compile_tests_step.dart +++ b/lib/web_ui/dev/steps/compile_tests_step.dart @@ -23,7 +23,11 @@ import '../utils.dart'; /// * test/ - compiled test code /// * test_images/ - test images copied from Skis sources. class CompileTestsStep implements PipelineStep { - CompileTestsStep({this.testFiles, this.useLocalCanvasKit = false, this.isWasm = false}); + CompileTestsStep({ + this.testFiles, + this.useLocalCanvasKit = false, + this.isWasm = false + }); final List? testFiles; final bool isWasm; @@ -46,6 +50,7 @@ class CompileTestsStep implements PipelineStep { await environment.webUiBuildDir.create(); if (isWasm) { await copyDart2WasmTestScript(); + await copySkwasm(); } await copyCanvasKitFiles(useLocalCanvasKit: useLocalCanvasKit); await buildHostPage(); @@ -133,11 +138,36 @@ Future copyDart2WasmTestScript() async { environment.webUiDevDir.path, 'test_dart2wasm.js', )); - final io.Directory targetDir = io.Directory(pathlib.join( + final io.File targetFile = io.File(pathlib.join( environment.webUiBuildDir.path, 'test_dart2wasm.js', )); - await sourceFile.copy(targetDir.path); + await sourceFile.copy(targetFile.path); +} + +Future copySkwasm() async { + final io.Directory targetDir = io.Directory(pathlib.join( + environment.webUiBuildDir.path, + 'skwasm', + )); + + await targetDir.create(recursive: true); + + for (final String fileName in [ + 'skwasm.wasm', + 'skwasm.js', + 'skwasm.worker.js', + ]) { + final io.File sourceFile = io.File(pathlib.join( + environment.wasmReleaseOutDir.path, + fileName, + )); + final io.File targetFile = io.File(pathlib.join( + targetDir.path, + fileName, + )); + await sourceFile.copy(targetFile.path); + } } final io.Directory _localCanvasKitDir = io.Directory(pathlib.join( @@ -207,7 +237,7 @@ Future copyCanvasKitFiles({bool useLocalCanvasKit = false}) async { Future compileTests(List testFiles, bool isWasm) async { final Stopwatch stopwatch = Stopwatch()..start(); - final TestsByRenderer sortedTests = sortTestsByRenderer(testFiles); + final TestsByRenderer sortedTests = sortTestsByRenderer(testFiles, isWasm); await Future.wait(>[ if (sortedTests.htmlTests.isNotEmpty) @@ -327,11 +357,13 @@ Future compileUnitTestToJS(FilePath input, {required Renderer renderer}) a Future compileUnitTestToWasm(FilePath input, {required Renderer renderer}) async { final String targetFileName = pathlib.join( environment.webUiBuildDir.path, + getBuildDirForRenderer(renderer), '${input.relativeToWebUi}.browser_test.dart.wasm', ); final io.Directory directoryToTarget = io.Directory(pathlib.join( environment.webUiBuildDir.path, + getBuildDirForRenderer(renderer), pathlib.dirname(input.relativeToWebUi))); if (!directoryToTarget.existsSync()) { @@ -350,6 +382,13 @@ Future compileUnitTestToWasm(FilePath input, {required Renderer renderer}) '-DFLUTTER_WEB_AUTO_DETECT=false', '-DFLUTTER_WEB_USE_SKIA=${renderer == Renderer.canvasKit}', '-DFLUTTER_WEB_USE_SKWASM=${renderer == Renderer.skwasm}', + + if (renderer == Renderer.skwasm) + ...[ + '--import-shared-memory', + '--shared-memory-max-pages=32768', + ], + input.relativeToWebUi, // current path. targetFileName, // target path. ]; diff --git a/lib/web_ui/dev/steps/run_tests_step.dart b/lib/web_ui/dev/steps/run_tests_step.dart index a30f1fce51ab2..196a5617d2a97 100644 --- a/lib/web_ui/dev/steps/run_tests_step.dart +++ b/lib/web_ui/dev/steps/run_tests_step.dart @@ -70,7 +70,7 @@ class RunTestsStep implements PipelineStep { final SkiaGoldClient? skiaClient = await _createSkiaClient(); final List testFiles = this.testFiles ?? findAllTests(); - final TestsByRenderer sortedTests = sortTestsByRenderer(testFiles); + final TestsByRenderer sortedTests = sortTestsByRenderer(testFiles, isWasm); bool testsPassed = true; diff --git a/lib/web_ui/dev/test_dart2wasm.js b/lib/web_ui/dev/test_dart2wasm.js index 42ac14650d348..884fbc81fdecc 100644 --- a/lib/web_ui/dev/test_dart2wasm.js +++ b/lib/web_ui/dev/test_dart2wasm.js @@ -54,10 +54,27 @@ window.onload = async function () { let dart2wasm_runtime; let moduleInstance; try { + const isSkwasm = link.hasAttribute('skwasm'); + const imports = isSkwasm ? new Promise((resolve) => { + const skwasmScript = document.createElement('script'); + skwasmScript.src = '/skwasm/skwasm.js'; + + document.body.appendChild(skwasmScript); + skwasmScript.addEventListener('load', async () => { + const skwasmInstance = await skwasm(); + resolve({ + "skwasm": skwasmInstance.asm, + "ffi": { + "memory": skwasmInstance.wasmMemory, + } + }); + }); + }) : {}; + let baseName = link.href + '.browser_test.dart'; dart2wasm_runtime = await import(baseName + '.mjs'); const dartModulePromise = WebAssembly.compileStreaming(fetch(baseName + '.wasm')); - moduleInstance = await dart2wasm_runtime.instantiate(dartModulePromise, {}); + moduleInstance = await dart2wasm_runtime.instantiate(dartModulePromise, imports); } catch (exception) { const message = `Failed to fetch and instantiate wasm module: ${exception}`; sendLoadException(message); diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index d4d4e79235da5..16e161e9229ad 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -40,6 +40,11 @@ import 'browser.dart'; import 'environment.dart' as env; import 'utils.dart'; +const Map coopCoepHeaders = { + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', +}; + /// Custom test platform that serves web engine unit tests. class BrowserPlatform extends PlatformPlugin { BrowserPlatform._({ @@ -472,10 +477,16 @@ class BrowserPlatform extends PlatformPlugin { return shelf.Response.internalServerError(body: error); } + final bool needsCoopCoep = + extension == '.js' || + extension == '.mjs' || + extension == '.html'; return shelf.Response.ok( fileInBuild.readAsBytesSync(), headers: { HttpHeaders.contentTypeHeader: contentType, + if (needsCoopCoep && isWasm && renderer == Renderer.skwasm) + ...coopCoepHeaders, }, ); } @@ -489,7 +500,7 @@ class BrowserPlatform extends PlatformPlugin { // Link to the Dart wrapper. final String scriptBase = htmlEscape.convert(p.basename(test)); - final String link = ''; + final String link = ''; final String testRunner = isWasm ? '/test_dart2wasm.js' : 'packages/test/dart.js'; @@ -508,7 +519,11 @@ class BrowserPlatform extends PlatformPlugin { - ''', headers: {'Content-Type': 'text/html'}); + ''', headers: { + 'Content-Type': 'text/html', + if (isWasm && renderer == Renderer.skwasm) + ...coopCoepHeaders + }); } return shelf.Response.notFound('Not found.'); diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 6d552fd75819c..f3756a82ba480 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -137,7 +137,11 @@ class TestCommand extends Command with ArgUtils { final Pipeline testPipeline = Pipeline(steps: [ if (isWatchMode) ClearTerminalScreenStep(), - CompileTestsStep(testFiles: testFiles, useLocalCanvasKit: useLocalCanvasKit, isWasm: isWasm), + CompileTestsStep( + testFiles: testFiles, + useLocalCanvasKit: useLocalCanvasKit, + isWasm: isWasm + ), RunTestsStep( browserName: browserName, testFiles: testFiles, diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index 775da7f3b9802..2fe7244c20d98 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -407,7 +407,7 @@ class TestsByRenderer { } /// Given a list of test files, organizes them by which renderer should run them. -TestsByRenderer sortTestsByRenderer(List testFiles) { +TestsByRenderer sortTestsByRenderer(List testFiles, bool forWasm) { final List htmlTargets = []; final List canvasKitTargets = []; final List skwasmTargets = []; @@ -425,6 +425,10 @@ TestsByRenderer sortTestsByRenderer(List testFiles) { } else if (path.isWithin(uiTestDirectory, testFile.absolute)) { htmlTargets.add(testFile); canvasKitTargets.add(testFile); + if (forWasm) { + // Only add these tests in wasm mode, since JS mode has a stub renderer. + skwasmTargets.add(testFile); + } } else { htmlTargets.add(testFile); } diff --git a/lib/web_ui/lib/geometry.dart b/lib/web_ui/lib/geometry.dart index 029fc5038a4be..ff5abf31bf51d 100644 --- a/lib/web_ui/lib/geometry.dart +++ b/lib/web_ui/lib/geometry.dart @@ -6,6 +6,10 @@ // documentation of APIs. part of ui; +double toDegrees(double radians) { + return radians * 180 / math.pi; +} + abstract class OffsetBase { const OffsetBase(this._dx, this._dy); diff --git a/lib/web_ui/lib/src/engine/js_interop/js_promise.dart b/lib/web_ui/lib/src/engine/js_interop/js_promise.dart index 9a7f9ca6951b4..bb36d8732a94a 100644 --- a/lib/web_ui/lib/src/engine/js_interop/js_promise.dart +++ b/lib/web_ui/lib/src/engine/js_interop/js_promise.dart @@ -8,6 +8,8 @@ library js_promise; import 'package:js/js.dart'; import 'package:js/js_util.dart' as js_util; +import '../util.dart'; + @JS() @staticInterop class PromiseResolver {} @@ -39,6 +41,9 @@ Promise futureToPromise(Future future) { return Promise(allowInterop((PromiseResolver resolver, PromiseRejecter rejecter) { future.then( (T value) => resolver.resolve(value), - onError: (Object? error) => rejecter.reject(error)); + onError: (Object? error) { + printWarning('Rejecting promise with error: $error'); + rejecter.reject(error); + }); })); } diff --git a/lib/web_ui/lib/src/engine/renderer.dart b/lib/web_ui/lib/src/engine/renderer.dart index cc715bb510386..41f01aeb31f4d 100644 --- a/lib/web_ui/lib/src/engine/renderer.dart +++ b/lib/web_ui/lib/src/engine/renderer.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math' as math; import 'dart:typed_data'; +import 'package:ui/src/engine/skwasm/skwasm_stub.dart' if (dart.library.ffi) 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; import 'browser_detection.dart'; @@ -15,7 +16,6 @@ import 'embedder.dart'; import 'fonts.dart'; import 'html/renderer.dart'; import 'html_image_codec.dart'; -import 'skwasm/skwasm_stub/renderer.dart' if (dart.library.ffi) 'skwasm/skwasm_impl/renderer.dart'; final Renderer _renderer = Renderer._internal(); Renderer get renderer => _renderer; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart index 554302e24e849..d048c10959166 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart @@ -4,4 +4,21 @@ library skwasm_impl; +export 'skwasm_impl/canvas.dart'; +export 'skwasm_impl/image.dart'; +export 'skwasm_impl/paint.dart'; +export 'skwasm_impl/paragraph.dart'; +export 'skwasm_impl/path.dart'; +export 'skwasm_impl/path_metrics.dart'; +export 'skwasm_impl/picture.dart'; +export 'skwasm_impl/raw/raw_canvas.dart'; +export 'skwasm_impl/raw/raw_geometry.dart'; +export 'skwasm_impl/raw/raw_memory.dart'; +export 'skwasm_impl/raw/raw_paint.dart'; +export 'skwasm_impl/raw/raw_path.dart'; +export 'skwasm_impl/raw/raw_path_metrics.dart'; +export 'skwasm_impl/raw/raw_picture.dart'; +export 'skwasm_impl/raw/raw_surface.dart'; export 'skwasm_impl/renderer.dart'; +export 'skwasm_impl/surface.dart'; +export 'skwasm_impl/vertices.dart'; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart new file mode 100644 index 0000000000000..5ddefa82eebe3 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart @@ -0,0 +1,294 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; +import 'package:ui/ui.dart' as ui; + +class SkwasmCanvas implements ui.Canvas { + factory SkwasmCanvas(SkwasmPictureRecorder recorder, ui.Rect cullRect) => + SkwasmCanvas.fromHandle(withStackScope((StackScope s) => + pictureRecorderBeginRecording( + recorder.handle, s.convertRectToNative(cullRect)))); + + SkwasmCanvas.fromHandle(this._handle); + CanvasHandle _handle; + + void delete() { + canvasDestroy(_handle); + } + + @override + void save() { + canvasSave(_handle); + } + + @override + void saveLayer(ui.Rect? bounds, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + if (bounds != null) { + withStackScope((StackScope s) { + canvasSaveLayer(_handle, s.convertRectToNative(bounds), paint.handle); + }); + } else { + canvasSaveLayer(_handle, nullptr, paint.handle); + } + } + + @override + void restore() { + canvasRestore(_handle); + } + + @override + void restoreToCount(int count) { + canvasRestoreToCount(_handle, count); + } + + @override + int getSaveCount() => canvasGetSaveCount(_handle); + + @override + void translate(double dx, double dy) => canvasTranslate(_handle, dx, dy); + + @override + void scale(double sx, [double? sy]) => canvasScale(_handle, sx, sy ?? sx); + + @override + void rotate(double radians) => canvasRotate(_handle, ui.toDegrees(radians)); + + @override + void skew(double sx, double sy) => canvasSkew(_handle, sx, sy); + + @override + void transform(Float64List matrix4) { + withStackScope((StackScope s) { + canvasTransform(_handle, s.convertMatrix44toNative(matrix4)); + }); + } + + @override + void clipRect(ui.Rect rect, + {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { + withStackScope((StackScope s) { + canvasClipRect(_handle, s.convertRectToNative(rect), clipOp.index, doAntiAlias); + }); + } + + @override + void clipRRect(ui.RRect rrect, {bool doAntiAlias = true}) { + withStackScope((StackScope s) { + canvasClipRRect(_handle, s.convertRRectToNative(rrect), doAntiAlias); + }); + } + + @override + void clipPath(ui.Path uiPath, {bool doAntiAlias = true}) { + final SkwasmPath path = uiPath as SkwasmPath; + canvasClipPath(_handle, path.handle, doAntiAlias); + } + + @override + void drawColor(ui.Color color, ui.BlendMode blendMode) => + canvasDrawColor(_handle, color.value, blendMode.index); + + @override + void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + canvasDrawLine(_handle, p1.dx, p1.dy, p2.dx, p2.dy, paint.handle); + } + + @override + void drawPaint(ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + canvasDrawPaint(_handle, paint.handle); + } + + @override + void drawRect(ui.Rect rect, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + withStackScope((StackScope s) { + canvasDrawRect( + _handle, + s.convertRectToNative(rect), + paint.handle + ); + }); + } + + @override + void drawRRect(ui.RRect rrect, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + withStackScope((StackScope s) { + canvasDrawRRect( + _handle, + s.convertRRectToNative(rrect), + paint.handle + ); + }); + } + + @override + void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + withStackScope((StackScope s) { + canvasDrawDRRect( + _handle, + s.convertRRectToNative(outer), + s.convertRRectToNative(inner), + paint.handle + ); + }); + } + + @override + void drawOval(ui.Rect rect, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + withStackScope((StackScope s) { + canvasDrawOval(_handle, s.convertRectToNative(rect), paint.handle); + }); + } + + @override + void drawCircle(ui.Offset center, double radius, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + canvasDrawCircle(_handle, center.dx, center.dy, radius, paint.handle); + } + + @override + void drawArc(ui.Rect rect, double startAngle, double sweepAngle, + bool useCenter, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + withStackScope((StackScope s) { + canvasDrawArc( + _handle, + s.convertRectToNative(rect), + ui.toDegrees(startAngle), + ui.toDegrees(sweepAngle), + useCenter, + paint.handle + ); + }); + } + + @override + void drawPath(ui.Path uiPath, ui.Paint uiPaint) { + final SkwasmPaint paint = uiPaint as SkwasmPaint; + final SkwasmPath path = uiPath as SkwasmPath; + canvasDrawPath(_handle, path.handle, paint.handle); + } + + @override + void drawImage(ui.Image uiImage, ui.Offset offset, ui.Paint uiPaint) { + throw UnimplementedError(); + } + + @override + void drawImageRect( + ui.Image uiImage, ui.Rect src, ui.Rect dst, ui.Paint uiPaint) { + throw UnimplementedError(); + } + + @override + void drawImageNine( + ui.Image uiImage, ui.Rect center, ui.Rect dst, ui.Paint uiPaint) { + throw UnimplementedError(); + } + + @override + void drawPicture(ui.Picture picture) { + canvasDrawPicture(_handle, (picture as SkwasmPicture).handle); + } + + @override + void drawParagraph(ui.Paragraph uiParagraph, ui.Offset offset) { + throw UnimplementedError(); + } + + @override + void drawPoints( + ui.PointMode pointMode, List points, ui.Paint paint) { + throw UnimplementedError(); + } + + @override + void drawRawPoints( + ui.PointMode pointMode, Float32List points, ui.Paint paint) { + throw UnimplementedError(); + } + + @override + void drawVertices( + ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { + throw UnimplementedError(); + } + + @override + void drawAtlas( + ui.Image atlas, + List transforms, + List rects, + List? colors, + ui.BlendMode? blendMode, + ui.Rect? cullRect, + ui.Paint paint, + ) { + throw UnimplementedError(); + } + + @override + void drawRawAtlas( + ui.Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + ui.BlendMode? blendMode, + ui.Rect? cullRect, + ui.Paint paint, + ) { + throw UnimplementedError(); + } + + @override + void drawShadow( + ui.Path path, + ui.Color color, + double elevation, + bool transparentOccluder, + ) { + throw UnimplementedError(); + } + + @override + ui.Rect getDestinationClipBounds() { + return withStackScope((StackScope scope) { + final Pointer outRect = scope.allocInt32Array(4); + canvasGetDeviceClipBounds(_handle, outRect); + return scope.convertIRectFromNative(outRect); + }); + } + + @override + ui.Rect getLocalClipBounds() { + final Float64List transform = getTransform(); + final Matrix4 matrix = Matrix4.fromFloat32List(Float32List.fromList(transform)); + if (matrix.invert() == 0) { + // non-invertible transforms collapse space to a line or point + return ui.Rect.zero; + } + return transformRect(matrix, getDestinationClipBounds()); + } + + @override + Float64List getTransform() { + return withStackScope((StackScope scope) { + final Pointer outMatrix = scope.allocFloatArray(16); + canvasGetTransform(_handle, outMatrix); + return scope.convertMatrix44FromNative(outMatrix); + }); + } +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart new file mode 100644 index 0000000000000..3ca0b738b14e8 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +class SkwasmImage implements ui.Image { + @override + int get width { + throw UnimplementedError(); + } + + @override + int get height { + throw UnimplementedError(); + } + + @override + Future toByteData( + {ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { + throw UnimplementedError(); + } + + @override + void dispose() { + throw UnimplementedError(); + } + + @override + bool get debugDisposed { + throw UnimplementedError(); + } + + @override + SkwasmImage clone() => this; + + @override + bool isCloneOf(ui.Image other) => identical(this, other); + + @override + List? debugGetOpenHandleStackTraces() => null; + + @override + String toString() => '[$width\u00D7$height]'; +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart new file mode 100644 index 0000000000000..daabca6a03ce8 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; +import 'package:ui/ui.dart' as ui; + +class SkwasmPaint implements ui.Paint { + factory SkwasmPaint() { + return SkwasmPaint._fromHandle(paintCreate()); + } + + SkwasmPaint._fromHandle(this._handle); + PaintHandle _handle; + + PaintHandle get handle => _handle; + + ui.BlendMode _cachedBlendMode = ui.BlendMode.srcOver; + + @override + ui.BlendMode get blendMode { + return _cachedBlendMode; + } + + @override + set blendMode(ui.BlendMode blendMode) { + if (_cachedBlendMode != blendMode) { + _cachedBlendMode = blendMode; + paintSetBlendMode(_handle, blendMode.index); + } + } + + @override + ui.PaintingStyle get style => ui.PaintingStyle.values[paintGetStyle(_handle)]; + + @override + set style(ui.PaintingStyle style) => paintSetStyle(_handle, style.index); + + @override + double get strokeWidth => paintGetStrokeWidth(_handle); + + @override + set strokeWidth(double width) => paintSetStrokeWidth(_handle, width); + + @override + ui.StrokeCap get strokeCap => ui.StrokeCap.values[paintGetStrokeCap(_handle)]; + + @override + set strokeCap(ui.StrokeCap cap) => paintSetStrokeCap(_handle, cap.index); + + @override + ui.StrokeJoin get strokeJoin => ui.StrokeJoin.values[paintGetStrokeJoin(_handle)]; + + @override + set strokeJoin(ui.StrokeJoin join) => paintSetStrokeJoin(_handle, join.index); + + @override + bool get isAntiAlias => paintGetAntiAlias(_handle); + + @override + set isAntiAlias(bool value) => paintSetAntiAlias(_handle, value); + + @override + ui.Color get color => ui.Color(paintGetColorInt(_handle)); + + @override + set color(ui.Color color) => paintSetColorInt(_handle, color.value); + + @override + double get strokeMiterLimit => paintGetMiterLimit(_handle); + + @override + set strokeMiterLimit(double limit) => paintSetMiterLimit(_handle, limit); + + // Unimplemented stuff below + @override + ui.ColorFilter? colorFilter; + + @override + ui.FilterQuality filterQuality = ui.FilterQuality.none; + + @override + ui.ImageFilter? imageFilter; + + @override + bool invertColors = false; + + @override + ui.MaskFilter? maskFilter; + + @override + ui.Shader? shader; +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart new file mode 100644 index 0000000000000..0ae57a16f2242 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart @@ -0,0 +1,155 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: avoid_unused_constructor_parameters + +import 'package:ui/ui.dart' as ui; + +// TODO(jacksongardner): implement this +class SkwasmLineMetrics implements ui.LineMetrics { + factory SkwasmLineMetrics({ + required bool hardBreak, + required double ascent, + required double descent, + required double unscaledAscent, + required double height, + required double width, + required double left, + required double baseline, + required int lineNumber, + }) { + throw UnimplementedError(); + } + + @override + bool get hardBreak { + throw UnimplementedError(); + } + + @override + double get ascent { + throw UnimplementedError(); + } + + @override + double get descent { + throw UnimplementedError(); + } + + @override + double get unscaledAscent { + throw UnimplementedError(); + } + + @override + double get height { + throw UnimplementedError(); + } + + @override + double get width { + throw UnimplementedError(); + } + + @override + double get left { + throw UnimplementedError(); + } + + @override + double get baseline { + throw UnimplementedError(); + } + + @override + int get lineNumber { + throw UnimplementedError(); + } +} + +class SkwasmParagraph implements ui.Paragraph { + @override + double get width { + throw UnimplementedError(); + } + + @override + double get height { + throw UnimplementedError(); + } + + @override + double get longestLine { + throw UnimplementedError(); + } + + @override + double get minIntrinsicWidth { + throw UnimplementedError(); + } + + @override + double get maxIntrinsicWidth { + throw UnimplementedError(); + } + + @override + double get alphabeticBaseline { + throw UnimplementedError(); + } + + @override + double get ideographicBaseline { + throw UnimplementedError(); + } + + @override + bool get didExceedMaxLines { + throw UnimplementedError(); + } + + @override + void layout(ui.ParagraphConstraints constraints) { + throw UnimplementedError(); + } + + @override + List getBoxesForRange(int start, int end, + {ui.BoxHeightStyle boxHeightStyle = ui.BoxHeightStyle.tight, + ui.BoxWidthStyle boxWidthStyle = ui.BoxWidthStyle.tight}) { + throw UnimplementedError(); + } + + @override + ui.TextPosition getPositionForOffset(ui.Offset offset) { + throw UnimplementedError(); + } + + @override + ui.TextRange getWordBoundary(ui.TextPosition position) { + throw UnimplementedError(); + } + + @override + ui.TextRange getLineBoundary(ui.TextPosition position) { + throw UnimplementedError(); + } + + @override + List getBoxesForPlaceholders() { + throw UnimplementedError(); + } + + @override + List computeLineMetrics() { + throw UnimplementedError(); + } + + @override + bool get debugDisposed => throw UnimplementedError(); + + @override + void dispose() { + } +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart new file mode 100644 index 0000000000000..78a2e9f0044f6 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart @@ -0,0 +1,259 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; +import 'package:ui/ui.dart' as ui; + +enum PathDirection { + clockwise, + counterClockwise, +} + +enum PathArcSize { + small, + large, +} + +class SkwasmPath implements ui.Path { + factory SkwasmPath() { + return SkwasmPath.fromHandle(pathCreate()); + } + + factory SkwasmPath.from(SkwasmPath source) { + return SkwasmPath.fromHandle(pathCopy(source._handle)); + } + + SkwasmPath.fromHandle(this._handle); + final PathHandle _handle; + + PathHandle get handle => _handle; + + void delete() { + pathDestroy(_handle); + } + + @override + ui.PathFillType get fillType => ui.PathFillType.values[pathGetFillType(_handle)]; + + @override + set fillType(ui.PathFillType fillType) => pathSetFillType(_handle, fillType.index); + + @override + void moveTo(double x, double y) => pathMoveTo(_handle, x, y); + + @override + void relativeMoveTo(double x, double y) => pathRelativeMoveTo(_handle, x, y); + + @override + void lineTo(double x, double y) => pathLineTo(_handle, x, y); + + @override + void relativeLineTo(double x, double y) => pathRelativeMoveTo(_handle, x, y); + + @override + void quadraticBezierTo(double x1, double y1, double x2, double y2) => + pathQuadraticBezierTo(_handle, x1, y1, x2, y2); + + @override + void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) => + pathRelativeQuadraticBezierTo(_handle, x1, y1, x2, y2); + + @override + void cubicTo( + double x1, + double y1, + double x2, + double y2, + double x3, + double y3) => + pathCubicTo(_handle, x1, y1, x2, y2, x3, y3); + + @override + void relativeCubicTo( + double x1, + double y1, + double x2, + double y2, + double x3, + double y3) => + pathRelativeCubicTo(_handle, x1, y1, x2, y2, x3, y3); + + @override + void conicTo(double x1, double y1, double x2, double y2, double w) => + pathConicTo(_handle, x1, y1, x2, y2, w); + + @override + void relativeConicTo(double x1, double y1, double x2, double y2, double w) => + pathRelativeConicTo(_handle, x1, y1, x2, y2, w); + + @override + void arcTo( + ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { + withStackScope((StackScope s) { + pathArcToOval( + _handle, + s.convertRectToNative(rect), + ui.toDegrees(startAngle), + ui.toDegrees(sweepAngle), + forceMoveTo + ); + }); + } + + @override + void arcToPoint( + ui.Offset arcEnd, { + ui.Radius radius = ui.Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { + final PathArcSize arcSize = + largeArc ? PathArcSize.large : PathArcSize.small; + final PathDirection pathDirection = + clockwise ? PathDirection.clockwise : PathDirection.counterClockwise; + pathArcToRotated( + _handle, + radius.x, + radius.y, + ui.toDegrees(rotation), + arcSize.index, + pathDirection.index, + arcEnd.dx, + arcEnd.dy + ); + } + + @override + void relativeArcToPoint( + ui.Offset arcEndDelta, { + ui.Radius radius = ui.Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { + final PathArcSize arcSize = + largeArc ? PathArcSize.large : PathArcSize.small; + final PathDirection pathDirection = + clockwise ? PathDirection.clockwise : PathDirection.counterClockwise; + pathRelativeArcToRotated( + _handle, + radius.x, + radius.y, + ui.toDegrees(rotation), + arcSize.index, + pathDirection.index, + arcEndDelta.dx, + arcEndDelta.dy + ); + } + + @override + void addRect(ui.Rect rect) { + withStackScope((StackScope s) { + pathAddRect(_handle, s.convertRectToNative(rect)); + }); + } + + @override + void addOval(ui.Rect rect) { + withStackScope((StackScope s) { + pathAddOval(_handle, s.convertRectToNative(rect)); + }); + } + + @override + void addArc(ui.Rect rect, double startAngle, double sweepAngle) { + withStackScope((StackScope s) { + pathAddArc( + _handle, + s.convertRectToNative(rect), + ui.toDegrees(startAngle), + ui.toDegrees(sweepAngle) + ); + }); + } + + @override + void addPolygon(List points, bool close) { + withStackScope((StackScope s) { + pathAddPolygon(_handle, s.convertPointArrayToNative(points), points.length, close); + }); + } + + @override + void addRRect(ui.RRect rrect) { + withStackScope((StackScope s) { + pathAddRRect(_handle, s.convertRRectToNative(rrect)); + }); + } + + @override + void addPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) { + _addPath(path, offset, false, matrix4: matrix4); + } + + @override + void extendWithPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) { + _addPath(path, offset, true, matrix4: matrix4); + } + + void _addPath(ui.Path path, ui.Offset offset, bool extend, {Float64List? matrix4}) { + assert(path is SkwasmPath); + withStackScope((StackScope s) { + final Pointer convertedMatrix = + s.convertMatrix4toSkMatrix(matrix4 ?? Matrix4.identity().toFloat64()); + convertedMatrix[2] += offset.dx; + convertedMatrix[5] += offset.dy; + pathAddPath(_handle, (path as SkwasmPath)._handle, convertedMatrix, extend); + }); + } + + @override + void close() => pathClose(_handle); + + @override + void reset() => pathReset(_handle); + + @override + bool contains(ui.Offset point) => pathContains(_handle, point.dx, point.dy); + + @override + ui.Path shift(ui.Offset offset) => + transform(Matrix4.translationValues(offset.dx, offset.dy, 0.0).toFloat64()); + + @override + ui.Path transform(Float64List matrix4) { + return withStackScope((StackScope s) { + final PathHandle newPathHandle = pathCopy(_handle); + pathTransform(newPathHandle, s.convertMatrix4toSkMatrix(matrix4)); + return SkwasmPath.fromHandle(newPathHandle); + }); + } + + @override + ui.Rect getBounds() { + return withStackScope((StackScope s) { + final Pointer rectBuffer = s.allocFloatArray(4); + pathGetBounds(_handle, rectBuffer); + return s.convertRectFromNative(rectBuffer); + }); + } + + static SkwasmPath combine( + ui.PathOperation operation, + SkwasmPath path1, + SkwasmPath path2) => + SkwasmPath.fromHandle(pathCombine( + operation.index, path1._handle, path2._handle)); + + @override + ui.PathMetrics computeMetrics({bool forceClosed = false}) { + return SkwasmPathMetrics(path: this, forceClosed: forceClosed); + } +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart new file mode 100644 index 0000000000000..da753adbc9cda --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart @@ -0,0 +1,90 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:collection'; +import 'dart:ffi'; + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; +import 'package:ui/ui.dart' as ui; + +class SkwasmPathMetrics extends IterableBase + implements ui.PathMetrics { + SkwasmPathMetrics({required this.path, required this.forceClosed}); + + SkwasmPath path; + bool forceClosed; + + @override + late Iterator iterator = SkwasmPathMetricIterator(path, forceClosed); +} + +class SkwasmPathMetricIterator implements Iterator { + SkwasmPathMetricIterator(SkwasmPath path, bool forceClosed) + : _handle = contourMeasureIterCreate(path.handle, forceClosed, 1.0); + + final ContourMeasureIterHandle _handle; + SkwasmPathMetric? _current; + int _nextIndex = 0; + + @override + ui.PathMetric get current { + if (_current == null) { + throw RangeError( + 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' + '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' + '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".'); + } + return _current!; + } + + @override + bool moveNext() { + final ContourMeasureHandle measureHandle = contourMeasureIterNext(_handle); + if (measureHandle == nullptr) { + _current = null; + return false; + } else { + _current = SkwasmPathMetric(measureHandle, _nextIndex); + _nextIndex++; + return true; + } + } +} + +class SkwasmPathMetric implements ui.PathMetric { + SkwasmPathMetric(this._handle, this.contourIndex); + + final ContourMeasureHandle _handle; + + @override + final int contourIndex; + + @override + ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { + return SkwasmPath.fromHandle( + contourMeasureGetSegment(_handle, start, end, startWithMoveTo)); + } + + @override + ui.Tangent? getTangentForOffset(double distance) { + return withStackScope((StackScope scope) { + final Pointer outPosition = scope.allocFloatArray(4); + final Pointer outTangent = + Pointer.fromAddress(outPosition.address + sizeOf() * 2); + final bool result = + contourMeasureGetPosTan(_handle, distance, outPosition, outTangent); + assert(result); + return ui.Tangent( + ui.Offset(outPosition[0], outPosition[1]), + ui.Offset(outTangent[0], outTangent[1]) + ); + }); + } + + @override + bool get isClosed => contourMeasureIsClosed(_handle); + + @override + double get length => contourMeasureLength(_handle); +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart new file mode 100644 index 0000000000000..7d5abdb56dce7 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; +import 'package:ui/ui.dart' as ui; + +class SkwasmPicture implements ui.Picture { + SkwasmPicture.fromHandle(this._handle); + final PictureHandle _handle; + + PictureHandle get handle => _handle; + + @override + Future toImage(int width, int height) { + throw UnimplementedError(); + } + + @override + void dispose() { + ui.Picture.onDispose?.call(this); + pictureDispose(_handle); + debugDisposed = true; + } + + @override + int get approximateBytesUsed => pictureApproximateBytesUsed(_handle); + + @override + bool debugDisposed = false; + + @override + ui.Image toImageSync(int width, int height) { + // TODO(jacksongardner): implement toImageSync + throw UnimplementedError(); + } +} + +class SkwasmPictureRecorder implements ui.PictureRecorder { + factory SkwasmPictureRecorder() => + SkwasmPictureRecorder._fromHandle(pictureRecorderCreate()); + + SkwasmPictureRecorder._fromHandle(this._handle); + final PictureRecorderHandle _handle; + + PictureRecorderHandle get handle => _handle; + + void delete() => pictureRecorderDestroy(_handle); + + @override + SkwasmPicture endRecording() { + isRecording = false; + + final SkwasmPicture picture = SkwasmPicture.fromHandle(pictureRecorderEndRecording(_handle)); + ui.Picture.onCreate?.call(picture); + return picture; + } + + @override + bool isRecording = true; +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart new file mode 100644 index 0000000000000..97f1c89fd42c5 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart @@ -0,0 +1,139 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:ffi'; + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + +class CanvasWrapper extends Opaque {} + +typedef CanvasHandle = Pointer; + +@Native(symbol: 'canvas_destroy', isLeaf: true) +external void canvasDestroy(CanvasHandle canvas); + +@Native(symbol: 'canvas_save', isLeaf: true) +external void canvasSave(CanvasHandle canvas); + +@Native( + symbol: 'canvas_saveLayer', isLeaf: true) +external void canvasSaveLayer( + CanvasHandle canvas, RawRect rect, PaintHandle paint); + +@Native(symbol: 'canvas_restore', isLeaf: true) +external void canvasRestore(CanvasHandle canvas); + +@Native( + symbol: 'canvas_restoreToCount', isLeaf: true) +external void canvasRestoreToCount(CanvasHandle canvas, int count); + +@Native(symbol: 'canvas_getSaveCount', isLeaf: true) +external int canvasGetSaveCount(CanvasHandle canvas); + +@Native( + symbol: 'canvas_translate', isLeaf: true) +external void canvasTranslate(CanvasHandle canvas, double dx, double dy); + +@Native( + symbol: 'canvas_scale', isLeaf: true) +external void canvasScale(CanvasHandle canvas, double sx, double sy); + +@Native( + symbol: 'canvas_rotate', isLeaf: true) +external void canvasRotate(CanvasHandle canvas, double degrees); + +@Native( + symbol: 'canvas_skew', isLeaf: true) +external void canvasSkew(CanvasHandle canvas, double sx, double sy); + +@Native( + symbol: 'canvas_transform', isLeaf: true) +external void canvasTransform(CanvasHandle canvas, RawMatrix44 matrix); + +@Native( + symbol: 'canvas_clipRect', isLeaf: true) +external void canvasClipRect( + CanvasHandle canvas, RawRect rect, int op, bool antialias); + +@Native( + symbol: 'canvas_clipRRect', isLeaf: true) +external void canvasClipRRect( + CanvasHandle canvas, RawRRect rrect, bool antialias); + +@Native( + symbol: 'canvas_clipPath', isLeaf: true) +external void canvasClipPath( + CanvasHandle canvas, PathHandle path, bool antialias); + +@Native( + symbol: 'canvas_drawColor', isLeaf: true) +external void canvasDrawColor(CanvasHandle canvas, int color, int blendMode); + +@Native( + symbol: 'canvas_drawLine', isLeaf: true) +external void canvasDrawLine(CanvasHandle canvas, double x1, double y1, + double x2, double y2, PaintHandle paint); + +@Native( + symbol: 'canvas_drawPaint', isLeaf: true) +external void canvasDrawPaint(CanvasHandle canvas, PaintHandle paint); + +@Native( + symbol: 'canvas_drawRect', isLeaf: true) +external void canvasDrawRect( + CanvasHandle canvas, RawRect rect, PaintHandle paint); + +@Native( + symbol: 'canvas_drawRRect', isLeaf: true) +external void canvasDrawRRect( + CanvasHandle canvas, RawRRect rrect, PaintHandle paint); + +@Native( + symbol: 'canvas_drawDRRect', isLeaf: true) +external void canvasDrawDRRect( + CanvasHandle canvas, RawRRect outer, RawRRect inner, PaintHandle paint); + +@Native( + symbol: 'canvas_drawOval', isLeaf: true) +external void canvasDrawOval( + CanvasHandle canvas, RawRect oval, PaintHandle paint); + +@Native( + symbol: 'canvas_drawCircle', isLeaf: true) +external void canvasDrawCircle( + CanvasHandle canvas, double x, double y, double radius, PaintHandle paint); + +@Native( + symbol: 'canvas_drawCircle', isLeaf: true) +external void canvasDrawArc( + CanvasHandle canvas, + RawRect rect, + double startAngleDegrees, + double sweepAngleDegrees, + bool useCenter, + PaintHandle paint); + +@Native( + symbol: 'canvas_drawPath', isLeaf: true) +external void canvasDrawPath( + CanvasHandle canvas, PathHandle path, PaintHandle paint); + +@Native( + symbol: 'canvas_drawPicture', isLeaf: true) +external void canvasDrawPicture(CanvasHandle canvas, PictureHandle picture); + +@Native( + symbol: 'canvas_getTransform', isLeaf: true) +external void canvasGetTransform(CanvasHandle canvas, RawMatrix44 outMatrix); + +@Native( + symbol: 'canvas_getLocalClipBounds', isLeaf: true) +external void canvasGetLocalClipBounds(CanvasHandle canvas, RawRect outRect); + +@Native( + symbol: 'canvas_getDeviceClipBounds', isLeaf: true) +external void canvasGetDeviceClipBounds(CanvasHandle canvas, RawIRect outRect); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart new file mode 100644 index 0000000000000..74fc835bb920b --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; + +typedef RawRect = Pointer; +typedef RawIRect = Pointer; +typedef RawRRect = Pointer; +typedef RawPointArray = Pointer; +typedef RawMatrix33 = Pointer; +typedef RawMatrix44 = Pointer; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart new file mode 100644 index 0000000000000..22ac87f07d7f3 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart @@ -0,0 +1,165 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:convert'; +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +class Stack extends Opaque {} +typedef StackPointer = Pointer; + +/// Generic linear memory allocation +@Native(symbol: 'stackAlloc', isLeaf: true) +external StackPointer stackAlloc(int length); + +@Native(symbol: 'stackSave', isLeaf: true) +external StackPointer stackSave(); + +@Native(symbol: 'stackRestore', isLeaf: true) +external void stackRestore(StackPointer pointer); + +class StackScope { + Pointer convertStringToNative(String string) { + final Utf8Encoder utf8Encoder = utf8.encoder; + final Uint8List encoded = utf8Encoder.convert(string); + final Pointer pointer = allocInt8Array(encoded.length + 1); + for (int i = 0; i < encoded.length; i++) { + pointer[i] = encoded[i]; + } + pointer[encoded.length] = 0; + return pointer; + } + + Pointer convertMatrix4toSkMatrix(Float64List matrix4) { + final Pointer pointer = allocFloatArray(9); + final int matrixLength = matrix4.length; + + double getVal(int index) { + return (index < matrixLength) ? matrix4[index] : 0.0; + } + + pointer[0] = getVal(0); + pointer[1] = getVal(4); + pointer[2] = getVal(12); + + pointer[3] = getVal(1); + pointer[4] = getVal(5); + pointer[5] = getVal(13); + + pointer[6] = getVal(3); + pointer[7] = getVal(7); + pointer[8] = getVal(15); + + return pointer; + } + + Pointer convertMatrix44toNative(Float64List matrix4) { + assert(matrix4.length == 16); + final Pointer pointer = allocFloatArray(16); + for (int i = 0; i < 16; i++) { + pointer[i] = matrix4[i]; + } + return pointer; + } + + Float64List convertMatrix44FromNative(Pointer buffer) { + final Float64List matrix = Float64List(16); + for (int i = 0; i < 16; i++) { + matrix[i] = buffer[i]; + } + return matrix; + } + + Pointer convertRectToNative(ui.Rect rect) { + final Pointer pointer = allocFloatArray(4); + pointer[0] = rect.left; + pointer[1] = rect.top; + pointer[2] = rect.right; + pointer[3] = rect.bottom; + return pointer; + } + + ui.Rect convertRectFromNative(Pointer buffer) { + return ui.Rect.fromLTRB( + buffer[0], + buffer[1], + buffer[2], + buffer[3], + ); + } + + Pointer convertIRectToNative(ui.Rect rect) { + final Pointer pointer = allocInt32Array(4); + pointer[0] = rect.left.floor(); + pointer[1] = rect.top.floor(); + pointer[2] = rect.right.ceil(); + pointer[3] = rect.bottom.ceil(); + return pointer; + } + + ui.Rect convertIRectFromNative(Pointer buffer) { + return ui.Rect.fromLTRB( + buffer[0].toDouble(), + buffer[1].toDouble(), + buffer[2].toDouble(), + buffer[3].toDouble(), + ); + } + + Pointer convertRRectToNative(ui.RRect rect) { + final Pointer pointer = allocFloatArray(12); + pointer[0] = rect.left; + pointer[1] = rect.top; + pointer[2] = rect.right; + pointer[3] = rect.bottom; + + pointer[4] = rect.tlRadiusX; + pointer[5] = rect.tlRadiusY; + pointer[6] = rect.trRadiusX; + pointer[7] = rect.trRadiusY; + + pointer[8] = rect.brRadiusX; + pointer[9] = rect.brRadiusY; + pointer[10] = rect.blRadiusX; + pointer[11] = rect.blRadiusY; + + return pointer; + } + + Pointer convertPointArrayToNative(List points) { + final Pointer pointer = allocFloatArray(points.length * 2); + for (int i = 0; i < points.length; i++) { + pointer[i * 2] = points[i].dx; + pointer[i * 2 + 1] = points[i].dy; + } + return pointer; + } + + Pointer allocInt8Array(int count) { + final int length = count * sizeOf(); + return stackAlloc(length).cast(); + } + + Pointer allocInt32Array(int count) { + final int length = count * sizeOf(); + return stackAlloc(length).cast(); + } + + Pointer allocFloatArray(int count) { + final int length = count * sizeOf(); + return stackAlloc(length).cast(); + } +} + +T withStackScope(T Function(StackScope scope) f) { + final StackPointer stack = stackSave(); + final T result = f(StackScope()); + stackRestore(stack); + return result; +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart new file mode 100644 index 0000000000000..b796d460bbcd5 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart @@ -0,0 +1,63 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:ffi'; + +class RawPaint extends Opaque {} + +typedef PaintHandle = Pointer; + +@Native(symbol: 'paint_create', isLeaf: true) +external PaintHandle paintCreate(); + +@Native(symbol: 'paint_destroy', isLeaf: true) +external void paintDestroy(PaintHandle paint); + +@Native(symbol: 'paint_setBlendMode', isLeaf: true) +external void paintSetBlendMode(PaintHandle paint, int blendMode); + +@Native(symbol: 'paint_setStyle', isLeaf: true) +external void paintSetStyle(PaintHandle paint, int paintStyle); + +@Native(symbol: 'paint_getStyle', isLeaf: true) +external int paintGetStyle(PaintHandle paint); + +@Native(symbol: 'paint_setStrokeWidth', isLeaf: true) +external void paintSetStrokeWidth(PaintHandle paint, double strokeWidth); + +@Native(symbol: 'paint_getStrokeWidth', isLeaf: true) +external double paintGetStrokeWidth(PaintHandle paint); + +@Native(symbol: 'paint_setStrokeCap', isLeaf: true) +external void paintSetStrokeCap(PaintHandle paint, int cap); + +@Native(symbol: 'paint_getStrokeCap', isLeaf: true) +external int paintGetStrokeCap(PaintHandle paint); + +@Native(symbol: 'paint_setStrokeJoin', isLeaf: true) +external void paintSetStrokeJoin(PaintHandle paint, int join); + +@Native(symbol: 'paint_getStrokeJoin', isLeaf: true) +external int paintGetStrokeJoin(PaintHandle paint); + +@Native(symbol: 'paint_setAntiAlias', isLeaf: true) +external void paintSetAntiAlias(PaintHandle paint, bool antiAlias); + +@Native(symbol: 'paint_getAntiAlias', isLeaf: true) +external bool paintGetAntiAlias(PaintHandle paint); + +@Native(symbol: 'paint_setColorInt', isLeaf: true) +external void paintSetColorInt(PaintHandle paint, int color); + +@Native(symbol: 'paint_getColorInt', isLeaf: true) +external int paintGetColorInt(PaintHandle paint); + +@Native(symbol: 'paint_setMiterLimit', isLeaf: true) +external void paintSetMiterLimit(PaintHandle paint, double miterLimit); + +@Native(symbol: 'paint_getMiterLimit', isLeaf: true) +external double paintGetMiterLimit(PaintHandle paint); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart new file mode 100644 index 0000000000000..6062b44d2b670 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart @@ -0,0 +1,203 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:ffi'; + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + +class RawPath extends Opaque {} + +typedef PathHandle = Pointer; + +@Native(symbol: 'path_create', isLeaf: true) +external PathHandle pathCreate(); + +@Native(symbol: 'path_destroy', isLeaf: true) +external void pathDestroy(PathHandle path); + +@Native(symbol: 'path_copy', isLeaf: true) +external PathHandle pathCopy(PathHandle path); + +@Native(symbol: 'path_setFillType', isLeaf: true) +external void pathSetFillType(PathHandle path, int fillType); + +@Native(symbol: 'path_getFillType', isLeaf: true) +external int pathGetFillType(PathHandle path); + +@Native(symbol: 'path_moveTo', isLeaf: true) +external void pathMoveTo(PathHandle path, double x, double y); + +@Native(symbol: 'path_relativeMoveTo', isLeaf: true) +external void pathRelativeMoveTo(PathHandle path, double x, double y); + +@Native(symbol: 'path_lineTo', isLeaf: true) +external void pathLineTo(PathHandle path, double x, double y); + +@Native( + symbol: 'path_relativeLineTo', + isLeaf: true) +external void pathRelativeLineTo(PathHandle path, double x, double y); + +@Native( + symbol: 'path_quadraticBezierTo', + isLeaf: true) +external void pathQuadraticBezierTo( + PathHandle path, double x1, double y1, double x2, double y2); + +@Native( + symbol: 'path_relativeQuadraticBezierTo', + isLeaf: true) +external void pathRelativeQuadraticBezierTo( + PathHandle path, double x1, double y1, double x2, double y2); + +@Native( + symbol: 'path_cubicTo', + isLeaf: true) +external void pathCubicTo( + PathHandle path, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3 +); + +@Native( + symbol: 'path_relativeCubicTo', + isLeaf: true) +external void pathRelativeCubicTo( + PathHandle path, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3 +); + +@Native( + symbol: 'path_conicTo', + isLeaf: true) +external void pathConicTo( + PathHandle path, + double x1, + double y1, + double x2, + double y2, + double w +); + +@Native( + symbol: 'path_relativeConicTo', + isLeaf: true) +external void pathRelativeConicTo( + PathHandle path, + double x1, + double y1, + double x2, + double y2, + double w +); + +@Native( + symbol: 'path_arcToOval', + isLeaf: true) +external void pathArcToOval( + PathHandle path, + RawRect rect, + double startAngle, + double sweepAngle, + bool forceMoveto +); + +@Native( + symbol: 'path_arcToRotated', + isLeaf: true) +external void pathArcToRotated( + PathHandle path, + double rx, + double ry, + double xAxisRotate, + int arcSize, + int pathDirection, + double x, + double y +); + +@Native( + symbol: 'path_relativeArcToRotated', + isLeaf: true) +external void pathRelativeArcToRotated( + PathHandle path, + double rx, + double ry, + double xAxisRotate, + int arcSize, + int pathDirection, + double x, + double y +); + +@Native(symbol: 'path_addRect', isLeaf: true) +external void pathAddRect(PathHandle path, RawRect oval); + +@Native(symbol: 'path_addOval', isLeaf: true) +external void pathAddOval(PathHandle path, RawRect oval); + +@Native( + symbol: 'path_addArc', + isLeaf: true) +external void pathAddArc( + PathHandle path, + RawRect ovalRect, + double startAngleDegrees, + double sweepAngleDegrees +); + +@Native( + symbol: 'path_addPolygon', + isLeaf: true) +external void pathAddPolygon( + PathHandle path, + RawPointArray points, + int pointCount, + bool close +); + +@Native(symbol: 'path_addRRect', isLeaf: true) +external void pathAddRRect(PathHandle path, RawRRect rrectValues); + +@Native( + symbol: 'path_addPath', + isLeaf: true) +external void pathAddPath( + PathHandle path, + PathHandle other, + RawMatrix33 matrix33, + bool extendPath +); + +@Native(symbol: 'path_close', isLeaf: true) +external void pathClose(PathHandle path); + +@Native(symbol: 'path_reset', isLeaf: true) +external void pathReset(PathHandle path); + +@Native(symbol: 'path_contains', isLeaf: true) +external bool pathContains(PathHandle path, double x, double y); + +@Native(symbol: 'path_transform', isLeaf: true) +external void pathTransform(PathHandle path, RawMatrix33 matrix33); + +@Native(symbol: 'path_getBounds', isLeaf: true) +external void pathGetBounds(PathHandle path, RawRect outRect); + +@Native( + symbol: 'path_combine', + isLeaf: true) +external PathHandle pathCombine(int operation, PathHandle path1, PathHandle path2); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart new file mode 100644 index 0000000000000..0202b49e39c9e --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart @@ -0,0 +1,51 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:ffi'; + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + +class RawContourMeasure extends Opaque {} + +class RawContourMeasureIter extends Opaque {} + +typedef ContourMeasureHandle = Pointer; +typedef ContourMeasureIterHandle = Pointer; + +@Native( + symbol: 'contourMeasureIter_create') +external ContourMeasureIterHandle contourMeasureIterCreate( + PathHandle path, bool forceClosed, double resScale); + +@Native( + symbol: 'contourMeasureIter_next') +external ContourMeasureHandle contourMeasureIterNext( + ContourMeasureIterHandle handle); + +@Native( + symbol: 'contourMeasureIter_dispose') +external void contourMeasureIterDispose(ContourMeasureIterHandle handle); + +@Native(symbol: 'contourMesaure_dispose') +external void contourMeasureDispose(ContourMeasureHandle handle); + +@Native(symbol: 'contourMeasure_length') +external double contourMeasureLength(ContourMeasureHandle handle); + +@Native(symbol: 'contourMeasure_isClosed') +external bool contourMeasureIsClosed(ContourMeasureHandle handle); + +@Native< + Bool Function(ContourMeasureHandle, Float, RawPointArray, + RawPointArray)>(symbol: 'contourMeasure_getPosTan') +external bool contourMeasureGetPosTan(ContourMeasureHandle handle, + double distance, RawPointArray outPosition, RawPointArray outTangent); + +@Native( + symbol: 'contourMeasure_getSegment') +external PathHandle contourMeasureGetSegment(ContourMeasureHandle handle, + double start, double stop, bool startWithMoveTo); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart new file mode 100644 index 0000000000000..ed2c7184c57e6 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:ffi'; + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + +class RawPictureRecorder extends Opaque {} +typedef PictureRecorderHandle = Pointer; + +class RawPicture extends Opaque {} +typedef PictureHandle = Pointer; + +@Native( + symbol: 'pictureRecorder_create', + isLeaf: true) +external PictureRecorderHandle pictureRecorderCreate(); + +@Native( + symbol: 'pictureRecorder_destroy', + isLeaf: true) +external void pictureRecorderDestroy(PictureRecorderHandle picture); + +@Native( + symbol: 'pictureRecorder_beginRecording', + isLeaf: true) +external CanvasHandle pictureRecorderBeginRecording( + PictureRecorderHandle picture, RawRect cullRect); + +@Native( + symbol: 'pictureRecorder_endRecording', + isLeaf: true) +external PictureHandle pictureRecorderEndRecording(PictureRecorderHandle picture); + +@Native( + symbol: 'picture_dispose', + isLeaf: true) +external void pictureDispose(PictureHandle handle); + +@Native( + symbol: 'picture_approximateBytesUsed', + isLeaf: true) +external int pictureApproximateBytesUsed(PictureHandle handle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart new file mode 100644 index 0000000000000..7bbb02c76151e --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@DefaultAsset('skwasm') +library skwasm_impl; + +import 'dart:ffi'; +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + +class RawSurface extends Opaque {} +typedef SurfaceHandle = Pointer; + +@Native)>( + symbol: 'surface_createFromCanvas', + isLeaf: true) +external SurfaceHandle surfaceCreateFromCanvas(Pointer querySelector); + +@Native( + symbol: 'surface_destroy', + isLeaf: true) +external void surfaceDestroy(SurfaceHandle surface); + +@Native( + symbol: 'surface_setCanvasSize', + isLeaf: true) +external void surfaceSetCanvasSize( + SurfaceHandle surface, + int width, + int height +); + +@Native( + symbol: 'surface_renderPicture', + isLeaf: true) +external void surfaceRenderPicture(SurfaceHandle surface, PictureHandle picture); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index 00f3108af1b24..11fff7a89efbf 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math' as math; import 'dart:typed_data'; +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; import '../../embedder.dart'; @@ -17,153 +18,171 @@ import '../../renderer.dart'; class SkwasmRenderer implements Renderer { @override ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2) { - throw UnimplementedError('Not yet implemented'); + return SkwasmPath.combine(op, path1 as SkwasmPath, path2 as SkwasmPath); } @override ui.ImageFilter composeImageFilters({required ui.ImageFilter outer, required ui.ImageFilter inner}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('composeImageFilters not yet implemented'); } @override ui.Path copyPath(ui.Path src) { - throw UnimplementedError('Not yet implemented'); + return SkwasmPath.from(src as SkwasmPath); } @override ui.ImageFilter createBlurImageFilter({double sigmaX = 0.0, double sigmaY = 0.0, ui.TileMode tileMode = ui.TileMode.clamp}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createBlurImageFilter not yet implemented'); } @override ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) { - throw UnimplementedError('Not yet implemented'); + return SkwasmCanvas(recorder as SkwasmPictureRecorder, cullRect ?? ui.Rect.largest); } @override ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, ui.Offset center, double radius, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix]) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createConicalGradient not yet implemented'); } @override ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createDilateImageFilter not yet implemented'); } @override ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createErodeImageFilter not yet implemented'); } @override ui.ImageShader createImageShader(ui.Image image, ui.TileMode tmx, ui.TileMode tmy, Float64List matrix4, ui.FilterQuality? filterQuality) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createImageShader not yet implemented'); } @override ui.Gradient createLinearGradient(ui.Offset from, ui.Offset to, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createLinearGradientn ot yet implemented'); } @override ui.ImageFilter createMatrixImageFilter(Float64List matrix4, {ui.FilterQuality filterQuality = ui.FilterQuality.low}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createMatrixImageFilter not yet implemented'); } @override - ui.Paint createPaint() { - throw UnimplementedError('Not yet implemented'); - } + ui.Paint createPaint() => SkwasmPaint(); @override ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createParagraphBuilder not yet implemented'); } @override ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createParagraphStyle not yet implemented'); } @override - ui.Path createPath() { - throw UnimplementedError('Not yet implemented'); - } + ui.Path createPath() => SkwasmPath(); @override - ui.PictureRecorder createPictureRecorder() { - throw UnimplementedError('Not yet implemented'); - } + ui.PictureRecorder createPictureRecorder() => SkwasmPictureRecorder(); @override ui.Gradient createRadialGradient(ui.Offset center, double radius, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createRadialGradient not yet implemented'); } @override ui.SceneBuilder createSceneBuilder() { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createSceneBuilder not yet implemented'); } @override ui.StrutStyle createStrutStyle({String? fontFamily, List? fontFamilyFallback, double? fontSize, double? height, ui.TextLeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, bool? forceStrutHeight}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createStrutStyle not yet implemented'); } @override ui.Gradient createSweepGradient(ui.Offset center, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, double startAngle = 0.0, double endAngle = math.pi * 2, Float32List? matrix4]) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createSweepGradient not yet implemented'); } @override ui.TextStyle createTextStyle({ui.Color? color, ui.TextDecoration? decoration, ui.Color? decorationColor, ui.TextDecorationStyle? decorationStyle, double? decorationThickness, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.TextBaseline? textBaseline, String? fontFamily, List? fontFamilyFallback, double? fontSize, double? letterSpacing, double? wordSpacing, double? height, ui.TextLeadingDistribution? leadingDistribution, ui.Locale? locale, ui.Paint? background, ui.Paint? foreground, List? shadows, List? fontFeatures, List? fontVariations}) { - throw UnimplementedError('Not yet implemented'); - } - - @override - ui.Vertices createVertices(ui.VertexMode mode, List positions, {List? textureCoordinates, List? colors, List? indices}) { - throw UnimplementedError('Not yet implemented'); - } - - @override - ui.Vertices createVerticesRaw(ui.VertexMode mode, Float32List positions, {Float32List? textureCoordinates, Int32List? colors, Uint16List? indices}) { - throw UnimplementedError('Not yet implemented'); - } + throw UnimplementedError('createTextStyle not yet implemented'); + } + + @override + ui.Vertices createVertices( + ui.VertexMode mode, + List positions, + { + List? textureCoordinates, + List? colors, + List? indices + }) => + SkwasmVertices( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices + ); + + @override + ui.Vertices createVerticesRaw( + ui.VertexMode mode, + Float32List positions, + { + Float32List? textureCoordinates, + Int32List? colors, + Uint16List? indices + }) => + SkwasmVertices.raw( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices + ); @override void decodeImageFromPixels(Uint8List pixels, int width, int height, ui.PixelFormat format, ui.ImageDecoderCallback callback, {int? rowBytes, int? targetWidth, int? targetHeight, bool allowUpscaling = true}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('decodeImageFromPixels not yet implemented'); } @override - FontCollection get fontCollection => throw UnimplementedError('Not yet implemented'); + FontCollection get fontCollection => throw UnimplementedError('fontCollection not yet implemented'); @override FutureOr initialize() { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('initialize not yet implemented'); } @override Future instantiateImageCodec(Uint8List list, {int? targetWidth, int? targetHeight, bool allowUpscaling = true}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('instantiateImageCodec not yet implemented'); } @override Future instantiateImageCodecFromUrl(Uri uri, {WebOnlyImageCodecChunkCallback? chunkCallback}) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('instantiateImageCodecFromUrl not yet implemented'); } @override void renderScene(ui.Scene scene) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('renderScene not yet implemented'); } @override - String get rendererTag => throw UnimplementedError('Not yet implemented'); + String get rendererTag => 'skwasm'; @override void reset(FlutterViewEmbedder embedder) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('reset not yet implemented'); } @override @@ -171,6 +190,6 @@ class SkwasmRenderer implements Renderer { @override Future createFragmentProgram(String assetKey) { - throw UnimplementedError('Not yet implemented'); + throw UnimplementedError('createFragmentProgram not yet implemented'); } } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart new file mode 100644 index 0000000000000..961924777740d --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; + +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + +class Surface { + factory Surface(String canvasQuerySelector) { + final SurfaceHandle surfaceHandle = withStackScope((StackScope scope) { + final Pointer pointer = scope.convertStringToNative(canvasQuerySelector); + return surfaceCreateFromCanvas(pointer); + }); + return Surface._fromHandle(surfaceHandle); + } + + Surface._fromHandle(this._handle); + final SurfaceHandle _handle; + + void setSize(int width, int height) => + surfaceSetCanvasSize(_handle, width, height); + + void renderPicture(SkwasmPicture picture) => + surfaceRenderPicture(_handle, picture.handle); +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart new file mode 100644 index 0000000000000..977c68f9705c8 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: avoid_unused_constructor_parameters + +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +class SkwasmVertices implements ui.Vertices { + factory SkwasmVertices( + ui.VertexMode mode, + List positions, { + List? textureCoordinates, + List? colors, + List? indices, + }) { + throw UnimplementedError(); + } + + factory SkwasmVertices.raw( + ui.VertexMode mode, + Float32List positions, { + Float32List? textureCoordinates, + Int32List? colors, + Uint16List? indices, + }) { + throw UnimplementedError(); + } + + @override + bool get debugDisposed => throw UnimplementedError(); + + @override + void dispose() { + } +} diff --git a/lib/web_ui/skwasm/BUILD.gn b/lib/web_ui/skwasm/BUILD.gn new file mode 100644 index 0000000000000..8745b6a064d62 --- /dev/null +++ b/lib/web_ui/skwasm/BUILD.gn @@ -0,0 +1,56 @@ +# Copyright 2019 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/toolchain/wasm.gni") + +wasm_lib("skwasm") { + sources = [ + "canvas.cpp", + "contour_measure.cpp", + "export.h", + "helpers.h", + "paint.cpp", + "path.cpp", + "picture.cpp", + "surface.cpp", + "wrappers.h", + ] + + ldflags = [ + "-std=c++20", + "-lGL", + "-sUSE_WEBGL2=1", + "-sMAX_WEBGL_VERSION=2", + "-sOFFSCREENCANVAS_SUPPORT", + "-sPTHREAD_POOL_SIZE=1", + "-sALLOW_MEMORY_GROWTH", + "-sUSE_PTHREADS=1", + ] + + cflags = [ "-pthread" ] + + if (is_debug) { + ldflags += [ + "-O0", + "-sDEMANGLE_SUPPORT=1", + "-sASSERTIONS=1", + "-sGL_ASSERTIONS=1", + "-g3", + ] + } else { + ldflags += [ + "-O1", + "--closure=0", + "-flto", + "-sEXPORTED_FUNCTIONS=[stackAlloc]", + ] + + cflags += [ + "-flto", + "-fvisibility=hidden", + ] + } + + deps = [ "//third_party/skia" ] +} diff --git a/lib/web_ui/skwasm/canvas.cpp b/lib/web_ui/skwasm/canvas.cpp new file mode 100644 index 0000000000000..750897d1060f7 --- /dev/null +++ b/lib/web_ui/skwasm/canvas.cpp @@ -0,0 +1,185 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "export.h" +#include "helpers.h" +#include "wrappers.h" + +using namespace Skwasm; + +SKWASM_EXPORT void canvas_destroy(CanvasWrapper* wrapper) { + delete wrapper; +} + +SKWASM_EXPORT void canvas_saveLayer(CanvasWrapper* wrapper, + SkRect* rect, + SkPaint* paint) { + wrapper->canvas->saveLayer(SkCanvas::SaveLayerRec(rect, paint, 0)); +} + +SKWASM_EXPORT void canvas_save(CanvasWrapper* wrapper) { + wrapper->canvas->save(); +} + +SKWASM_EXPORT void canvas_restore(CanvasWrapper* wrapper) { + wrapper->canvas->restore(); +} + +SKWASM_EXPORT void canvas_restoreToCount(CanvasWrapper* wrapper, int count) { + wrapper->canvas->restoreToCount(count); +} + +SKWASM_EXPORT int canvas_getSaveCount(CanvasWrapper* wrapper) { + return wrapper->canvas->getSaveCount(); +} + +SKWASM_EXPORT void canvas_translate(CanvasWrapper* wrapper, + SkScalar dx, + SkScalar dy) { + wrapper->canvas->translate(dx, dy); +} + +SKWASM_EXPORT void canvas_scale(CanvasWrapper* wrapper, + SkScalar sx, + SkScalar sy) { + wrapper->canvas->scale(sx, sy); +} + +SKWASM_EXPORT void canvas_rotate(CanvasWrapper* wrapper, SkScalar degrees) { + wrapper->canvas->rotate(degrees); +} + +SKWASM_EXPORT void canvas_skew(CanvasWrapper* wrapper, + SkScalar sx, + SkScalar sy) { + wrapper->canvas->skew(sx, sy); +} + +SKWASM_EXPORT void canvas_transform(CanvasWrapper* wrapper, + const SkM44* matrix44) { + wrapper->canvas->concat(*matrix44); +} + +SKWASM_EXPORT void canvas_clipRect(CanvasWrapper* wrapper, + const SkRect* rect, + SkClipOp op, + bool antialias) { + wrapper->canvas->clipRect(*rect, op, antialias); +} + +SKWASM_EXPORT void canvas_clipRRect(CanvasWrapper* wrapper, + const SkScalar* rrectValues, + bool antialias) { + wrapper->canvas->clipRRect(createRRect(rrectValues), antialias); +} + +SKWASM_EXPORT void canvas_clipPath(CanvasWrapper* wrapper, + SkPath* path, + bool antialias) { + wrapper->canvas->clipPath(*path, antialias); +} + +SKWASM_EXPORT void canvas_drawColor(CanvasWrapper* wrapper, + SkColor color, + SkBlendMode blendMode) { + makeCurrent(wrapper->context); + wrapper->canvas->drawColor(color, blendMode); +} + +SKWASM_EXPORT void canvas_drawLine(CanvasWrapper* wrapper, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2, + SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawLine(x1, y1, x2, y2, *paint); +} + +SKWASM_EXPORT void canvas_drawPaint(CanvasWrapper* wrapper, SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawPaint(*paint); +} + +SKWASM_EXPORT void canvas_drawRect(CanvasWrapper* wrapper, + SkRect* rect, + SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawRect(*rect, *paint); +} + +SKWASM_EXPORT void canvas_drawRRect(CanvasWrapper* wrapper, + const SkScalar* rrectValues, + SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawRRect(createRRect(rrectValues), *paint); +} + +SKWASM_EXPORT void canvas_drawDRRect(CanvasWrapper* wrapper, + const SkScalar* outerRrectValues, + const SkScalar* innerRrectValues, + SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawDRRect(createRRect(outerRrectValues), + createRRect(innerRrectValues), *paint); +} + +SKWASM_EXPORT void canvas_drawOval(CanvasWrapper* wrapper, + const SkRect* rect, + SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawOval(*rect, *paint); +} + +SKWASM_EXPORT void canvas_drawCircle(CanvasWrapper* wrapper, + SkScalar x, + SkScalar y, + SkScalar radius, + SkPaint* paint) { + makeCurrent(wrapper->context); + + wrapper->canvas->drawCircle(x, y, radius, *paint); +} + +SKWASM_EXPORT void canvas_drawArc(CanvasWrapper* wrapper, + const SkRect* rect, + SkScalar startAngleDegrees, + SkScalar sweepAngleDegrees, + bool useCenter, + SkPaint* paint) { + makeCurrent(wrapper->context); + wrapper->canvas->drawArc(*rect, startAngleDegrees, sweepAngleDegrees, + useCenter, *paint); +} + +SKWASM_EXPORT void canvas_drawPath(CanvasWrapper* wrapper, + SkPath* path, + SkPaint* paint) { + makeCurrent(wrapper->context); + + wrapper->canvas->drawPath(*path, *paint); +} + +SKWASM_EXPORT void canvas_drawPicture(CanvasWrapper* wrapper, + SkPicture* picture) { + makeCurrent(wrapper->context); + + wrapper->canvas->drawPicture(picture); +} + +SKWASM_EXPORT void canvas_getTransform(CanvasWrapper* wrapper, + SkM44* outTransform) { + *outTransform = wrapper->canvas->getLocalToDevice(); +} + +SKWASM_EXPORT void canvas_getLocalClipBounds(CanvasWrapper* wrapper, + SkRect* outRect) { + *outRect = wrapper->canvas->getLocalClipBounds(); +} + +SKWASM_EXPORT void canvas_getDeviceClipBounds(CanvasWrapper* wrapper, + SkIRect* outRect) { + *outRect = wrapper->canvas->getDeviceClipBounds(); +} diff --git a/lib/web_ui/skwasm/contour_measure.cpp b/lib/web_ui/skwasm/contour_measure.cpp new file mode 100644 index 0000000000000..fcc30ad407967 --- /dev/null +++ b/lib/web_ui/skwasm/contour_measure.cpp @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "export.h" +#include "helpers.h" + +#include "third_party/skia/include/core/SkContourMeasure.h" + +using namespace Skwasm; + +SKWASM_EXPORT SkContourMeasureIter* +contourMeasureIter_create(SkPath* path, bool forceClosed, SkScalar resScale) { + return new SkContourMeasureIter(*path, forceClosed, resScale); +} + +SKWASM_EXPORT SkContourMeasure* contourMeasureIter_next( + SkContourMeasureIter* iter) { + auto next = iter->next(); + if (next) { + next->ref(); + } + return next.get(); +} + +SKWASM_EXPORT void contourMeasure_dispose(SkContourMeasure* measure) { + measure->unref(); +} + +SKWASM_EXPORT SkScalar contourMeasure_length(SkContourMeasure* measure) { + return measure->length(); +} + +SKWASM_EXPORT bool contourMeasure_isClosed(SkContourMeasure* measure) { + return measure->isClosed(); +} + +SKWASM_EXPORT bool contourMeasure_getPosTan(SkContourMeasure* measure, + SkScalar distance, + SkPoint* outPosition, + SkVector* outTangent) { + return measure->getPosTan(distance, outPosition, outTangent); +} + +SKWASM_EXPORT SkPath* contourMeasure_getSegment(SkContourMeasure* measure, + SkScalar startD, + SkScalar stopD, + bool startWithMoveTo) { + SkPath* outPath = new SkPath(); + if (!measure->getSegment(startD, stopD, outPath, startWithMoveTo)) { + delete outPath; + return nullptr; + } + return outPath; +} diff --git a/lib/web_ui/skwasm/export.h b/lib/web_ui/skwasm/export.h new file mode 100644 index 0000000000000..cb0172e0bf621 --- /dev/null +++ b/lib/web_ui/skwasm/export.h @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#define SKWASM_EXPORT extern "C" EMSCRIPTEN_KEEPALIVE diff --git a/lib/web_ui/skwasm/helpers.h b/lib/web_ui/skwasm/helpers.h new file mode 100644 index 0000000000000..89b00beefa2af --- /dev/null +++ b/lib/web_ui/skwasm/helpers.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkRRect.h" + +namespace Skwasm { + +inline SkMatrix createMatrix(const SkScalar* f) { + return SkMatrix::MakeAll(f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], + f[8]); +} + +inline SkRRect createRRect(const SkScalar* f) { + const SkScalar* twelveFloats = reinterpret_cast(f); + const SkRect* rect = reinterpret_cast(twelveFloats); + const SkVector* radiiValues = + reinterpret_cast(twelveFloats + 4); + + SkRRect rr; + rr.setRectRadii(*rect, radiiValues); + return rr; +} + +} // namespace Skwasm diff --git a/lib/web_ui/skwasm/paint.cpp b/lib/web_ui/skwasm/paint.cpp new file mode 100644 index 0000000000000..17f89ca2e3d54 --- /dev/null +++ b/lib/web_ui/skwasm/paint.cpp @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "export.h" +#include "helpers.h" +#include "third_party/skia/include/core/SkPaint.h" + +using namespace Skwasm; + +SKWASM_EXPORT SkPaint* paint_create() { + return new SkPaint(); +} + +SKWASM_EXPORT void paint_destroy(SkPaint* paint) { + delete paint; +} + +SKWASM_EXPORT void paint_setBlendMode(SkPaint* paint, SkBlendMode mode) { + paint->setBlendMode(mode); +} + +// No getter for blend mode, as it's non trivial. Cache on the dart side. + +SKWASM_EXPORT void paint_setStyle(SkPaint* paint, SkPaint::Style style) { + paint->setStyle(style); +} + +SKWASM_EXPORT SkPaint::Style paint_getStyle(SkPaint* paint) { + return paint->getStyle(); +} + +SKWASM_EXPORT void paint_setStrokeWidth(SkPaint* paint, SkScalar width) { + paint->setStrokeWidth(width); +} + +SKWASM_EXPORT SkScalar paint_getStrokeWidth(SkPaint* paint) { + return paint->getStrokeWidth(); +} + +SKWASM_EXPORT void paint_setStrokeCap(SkPaint* paint, SkPaint::Cap cap) { + paint->setStrokeCap(cap); +} + +SKWASM_EXPORT SkPaint::Cap paint_getStrokeCap(SkPaint* paint) { + return paint->getStrokeCap(); +} + +SKWASM_EXPORT void paint_setStrokeJoin(SkPaint* paint, SkPaint::Join join) { + paint->setStrokeJoin(join); +} + +SKWASM_EXPORT SkPaint::Join paint_getStrokeJoin(SkPaint* paint) { + return paint->getStrokeJoin(); +} + +SKWASM_EXPORT void paint_setAntiAlias(SkPaint* paint, bool antiAlias) { + paint->setAntiAlias(antiAlias); +} + +SKWASM_EXPORT bool paint_getAntiAlias(SkPaint* paint) { + return paint->isAntiAlias(); +} + +SKWASM_EXPORT void paint_setColorInt(SkPaint* paint, SkColor colorInt) { + paint->setColor(colorInt); +} + +SKWASM_EXPORT SkColor paint_getColorInt(SkPaint* paint) { + return paint->getColor(); +} + +SKWASM_EXPORT void paint_setMiterLimit(SkPaint* paint, SkScalar miterLimit) { + paint->setStrokeMiter(miterLimit); +} + +SKWASM_EXPORT SkScalar paint_getMiterLImit(SkPaint* paint) { + return paint->getStrokeMiter(); +} diff --git a/lib/web_ui/skwasm/path.cpp b/lib/web_ui/skwasm/path.cpp new file mode 100644 index 0000000000000..f8fe6e1452e30 --- /dev/null +++ b/lib/web_ui/skwasm/path.cpp @@ -0,0 +1,197 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "export.h" +#include "helpers.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/pathops/SkPathOps.h" + +using namespace Skwasm; + +SKWASM_EXPORT SkPath* path_create() { + return new SkPath(); +} + +SKWASM_EXPORT void path_destroy(SkPath* path) { + delete path; +} + +SKWASM_EXPORT SkPath* path_copy(SkPath* path) { + return new SkPath(*path); +} + +SKWASM_EXPORT void path_setFillType(SkPath* path, SkPathFillType fillType) { + path->setFillType(fillType); +} + +SKWASM_EXPORT SkPathFillType path_getFillType(SkPath* path) { + return path->getFillType(); +} + +SKWASM_EXPORT void path_moveTo(SkPath* path, SkScalar x, SkScalar y) { + path->moveTo(x, y); +} + +SKWASM_EXPORT void path_relativeMoveTo(SkPath* path, SkScalar x, SkScalar y) { + path->rMoveTo(x, y); +} + +SKWASM_EXPORT void path_lineTo(SkPath* path, SkScalar x, SkScalar y) { + path->lineTo(x, y); +} + +SKWASM_EXPORT void path_relativeLineTo(SkPath* path, SkScalar x, SkScalar y) { + path->rLineTo(x, y); +} + +SKWASM_EXPORT void path_quadraticBezierTo(SkPath* path, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2) { + path->quadTo(x1, y1, x2, y2); +} + +SKWASM_EXPORT void path_relativeQuadraticBezierTo(SkPath* path, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2) { + path->rQuadTo(x1, y1, x2, y2); +} + +SKWASM_EXPORT void path_cubicTo(SkPath* path, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2, + SkScalar x3, + SkScalar y3) { + path->cubicTo(x1, y1, x2, y2, x3, y3); +} + +SKWASM_EXPORT void path_relativeCubicTo(SkPath* path, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2, + SkScalar x3, + SkScalar y3) { + path->rCubicTo(x1, y1, x2, y2, x3, y3); +} + +SKWASM_EXPORT void path_conicTo(SkPath* path, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2, + SkScalar w) { + path->conicTo(x1, y1, x2, y2, w); +} + +SKWASM_EXPORT void path_relativeConicTo(SkPath* path, + SkScalar x1, + SkScalar y1, + SkScalar x2, + SkScalar y2, + SkScalar w) { + path->rConicTo(x1, y1, x2, y2, w); +} + +SKWASM_EXPORT void path_arcToOval(SkPath* path, + const SkRect* rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool forceMoveTo) { + path->arcTo(*rect, startAngle, sweepAngle, forceMoveTo); +} + +SKWASM_EXPORT void path_arcToRotated(SkPath* path, + SkScalar rx, + SkScalar ry, + SkScalar xAxisRotate, + SkPath::ArcSize arcSize, + SkPathDirection pathDirection, + SkScalar x, + SkScalar y) { + path->arcTo(rx, ry, xAxisRotate, arcSize, pathDirection, x, y); +} + +SKWASM_EXPORT void path_relativeArcToRotated(SkPath* path, + SkScalar rx, + SkScalar ry, + SkScalar xAxisRotate, + SkPath::ArcSize arcSize, + SkPathDirection pathDirection, + SkScalar x, + SkScalar y) { + path->rArcTo(rx, ry, xAxisRotate, arcSize, pathDirection, x, y); +} + +SKWASM_EXPORT void path_addRect(SkPath* path, const SkRect* rect) { + path->addRect(*rect); +} + +SKWASM_EXPORT void path_addOval(SkPath* path, const SkRect* oval) { + path->addOval(*oval, SkPathDirection::kCW, 1); +} + +SKWASM_EXPORT void path_addArc(SkPath* path, + const SkRect* oval, + SkScalar startAngle, + SkScalar sweepAngle) { + path->addArc(*oval, startAngle, sweepAngle); +} + +SKWASM_EXPORT void path_addPolygon(SkPath* path, + const SkPoint* points, + int count, + bool close) { + path->addPoly(points, count, close); +} + +SKWASM_EXPORT void path_addRRect(SkPath* path, const SkScalar* rrectValues) { + path->addRRect(createRRect(rrectValues), SkPathDirection::kCW); +} + +SKWASM_EXPORT void path_addPath(SkPath* path, + const SkPath* other, + const SkScalar* matrix33, + SkPath::AddPathMode extendPath) { + path->addPath(*other, createMatrix(matrix33), extendPath); +} + +SKWASM_EXPORT void path_close(SkPath* path) { + path->close(); +} + +SKWASM_EXPORT void path_reset(SkPath* path) { + path->reset(); +} + +SKWASM_EXPORT bool path_contains(SkPath* path, SkScalar x, SkScalar y) { + return path->contains(x, y); +} + +SKWASM_EXPORT void path_transform(SkPath* path, const SkScalar* matrix33) { + path->transform(createMatrix(matrix33)); +} + +SKWASM_EXPORT void path_getBounds(SkPath* path, SkRect* rect) { + *rect = path->getBounds(); +} + +SKWASM_EXPORT SkPath* path_combine(SkPathOp operation, + const SkPath* path1, + const SkPath* path2) { + SkPath* output = new SkPath(); + if (Op(*path1, *path2, operation, output)) { + output->setFillType(path1->getFillType()); + return output; + } else { + delete output; + return nullptr; + } +} diff --git a/lib/web_ui/skwasm/picture.cpp b/lib/web_ui/skwasm/picture.cpp new file mode 100644 index 0000000000000..849f3833124c6 --- /dev/null +++ b/lib/web_ui/skwasm/picture.cpp @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "export.h" +#include "helpers.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "wrappers.h" + +using namespace Skwasm; + +SKWASM_EXPORT SkPictureRecorder* pictureRecorder_create() { + return new SkPictureRecorder(); +} + +SKWASM_EXPORT void pictureRecorder_dispose(SkPictureRecorder* recorder) { + delete recorder; +} + +SKWASM_EXPORT CanvasWrapper* pictureRecorder_beginRecording( + SkPictureRecorder* recorder, + const SkRect* cullRect) { + return new CanvasWrapper{0, recorder->beginRecording(*cullRect)}; +} + +SKWASM_EXPORT SkPicture* pictureRecorder_endRecording( + SkPictureRecorder* recorder) { + return recorder->finishRecordingAsPicture().release(); +} + +SKWASM_EXPORT void picture_dispose(SkPicture* picture) { + picture->unref(); +} + +SKWASM_EXPORT uint32_t picture_approximateBytesUsed(SkPicture* picture) { + return static_cast(picture->approximateBytesUsed()); +} diff --git a/lib/web_ui/skwasm/surface.cpp b/lib/web_ui/skwasm/surface.cpp new file mode 100644 index 0000000000000..f23e757419bc7 --- /dev/null +++ b/lib/web_ui/skwasm/surface.cpp @@ -0,0 +1,197 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include "export.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" +#include "third_party/skia/include/gpu/gl/GrGLTypes.h" +#include "wrappers.h" + +using namespace Skwasm; + +namespace { + +class Surface; +void fDispose(Surface* surface); +void fSetCanvasSize(Surface* surface, int width, int height); +void fRenderPicture(Surface* surface, SkPicture* picture); + +class Surface { + public: + Surface(const char* canvasID) : _canvasID(canvasID) { + pthread_attr_t attr; + pthread_attr_init(&attr); + emscripten_pthread_attr_settransferredcanvases(&attr, _canvasID.c_str()); + + pthread_create( + &_thread, &attr, + [](void* context) -> void* { + static_cast(context)->_runWorker(); + return nullptr; + }, + this); + pthread_detach(_thread); + } + + void dispose() { + emscripten_dispatch_to_thread(_thread, EM_FUNC_SIG_VI, + reinterpret_cast(fDispose), nullptr, + this); + } + + void setCanvasSize(int width, int height) { + emscripten_dispatch_to_thread(_thread, EM_FUNC_SIG_VIII, + reinterpret_cast(fSetCanvasSize), + nullptr, this, width, height); + } + + void renderPicture(SkPicture* picture) { + picture->ref(); + emscripten_dispatch_to_thread(_thread, EM_FUNC_SIG_VII, + reinterpret_cast(fRenderPicture), + nullptr, this, picture); + } + + private: + void _runWorker() { + _init(); + emscripten_unwind_to_js_event_loop(); + } + + void _init() { + EmscriptenWebGLContextAttributes attributes; + emscripten_webgl_init_context_attributes(&attributes); + + attributes.alpha = true; + attributes.depth = true; + attributes.stencil = true; + attributes.antialias = false; + attributes.premultipliedAlpha = true; + attributes.preserveDrawingBuffer = 0; + attributes.powerPreference = EM_WEBGL_POWER_PREFERENCE_DEFAULT; + attributes.failIfMajorPerformanceCaveat = false; + attributes.enableExtensionsByDefault = true; + attributes.explicitSwapControl = false; + attributes.renderViaOffscreenBackBuffer = true; + attributes.majorVersion = 2; + + _glContext = + emscripten_webgl_create_context(_canvasID.c_str(), &attributes); + if (!_glContext) { + printf("Failed to create context!\n"); + return; + } + + makeCurrent(_glContext); + + _grContext = GrDirectContext::MakeGL(GrGLMakeNativeInterface()); + + // WebGL should already be clearing the color and stencil buffers, but do it + // again here to ensure Skia receives them in the expected state. + emscripten_glBindFramebuffer(GL_FRAMEBUFFER, 0); + emscripten_glClearColor(0, 0, 0, 0); + emscripten_glClearStencil(0); + emscripten_glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + _grContext->resetContext(kRenderTarget_GrGLBackendState | + kMisc_GrGLBackendState); + + // The on-screen canvas is FBO 0. Wrap it in a Skia render target so Skia + // can render to it. + _fbInfo.fFBOID = 0; + _fbInfo.fFormat = GL_RGBA8_OES; + + emscripten_glGetIntegerv(GL_SAMPLES, &_sampleCount); + emscripten_glGetIntegerv(GL_STENCIL_BITS, &_stencil); + } + + void _dispose() { delete this; } + + void _setCanvasSize(int width, int height) { + if (_canvasWidth != width || _canvasHeight != height) { + _canvasWidth = width; + _canvasHeight = height; + _recreateSurface(); + } + } + + void _recreateSurface() { + GrBackendRenderTarget target(_canvasWidth, _canvasHeight, _sampleCount, + _stencil, _fbInfo); + _surface = SkSurface::MakeFromBackendRenderTarget( + _grContext.get(), target, kBottomLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB(), nullptr); + } + + void _renderPicture(const SkPicture* picture) { + if (!_surface) { + printf("Can't render picture with no surface.\n"); + return; + } + + auto canvas = _surface->getCanvas(); + canvas->drawPicture(picture); + _surface->flush(); + } + + std::string _canvasID; + + int _canvasWidth = 0; + int _canvasHeight = 0; + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE _glContext = 0; + sk_sp _grContext = nullptr; + sk_sp _surface = nullptr; + GrGLFramebufferInfo _fbInfo; + GrGLint _sampleCount; + GrGLint _stencil; + + pthread_t _thread; + + friend void fDispose(Surface* surface); + friend void fSetCanvasSize(Surface* surface, int width, int height); + friend void fRenderPicture(Surface* surface, SkPicture* picture); +}; + +void fDispose(Surface* surface) { + surface->_dispose(); +} + +void fSetCanvasSize(Surface* surface, int width, int height) { + surface->_setCanvasSize(width, height); +} + +void fRenderPicture(Surface* surface, SkPicture* picture) { + surface->_renderPicture(picture); + picture->unref(); +} + +} // namespace + +SKWASM_EXPORT Surface* surface_createFromCanvas(const char* canvasID) { + return new Surface(canvasID); +} + +SKWASM_EXPORT void surface_destroy(Surface* surface) { + surface->dispose(); +} + +SKWASM_EXPORT void surface_setCanvasSize(Surface* surface, + int width, + int height) { + surface->setCanvasSize(width, height); +} + +SKWASM_EXPORT void surface_renderPicture(Surface* surface, SkPicture* picture) { + surface->renderPicture(picture); +} diff --git a/lib/web_ui/skwasm/wrappers.h b/lib/web_ui/skwasm/wrappers.h new file mode 100644 index 0000000000000..2acc209015d36 --- /dev/null +++ b/lib/web_ui/skwasm/wrappers.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSurface.h" + +namespace Skwasm { + +struct SurfaceWrapper { + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context; + sk_sp grContext; + sk_sp surface; +}; + +struct CanvasWrapper { + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context; + SkCanvas* canvas; +}; + +inline void makeCurrent(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE handle) { + if (!handle) + return; + + int result = emscripten_webgl_make_context_current(handle); + if (result != EMSCRIPTEN_RESULT_SUCCESS) { + printf("make_context failed: %d", result); + } +} + +} // namespace Skwasm diff --git a/lib/web_ui/test/canvaskit/canvas_test.dart b/lib/web_ui/test/canvaskit/canvas_test.dart deleted file mode 100644 index cacb5ed4c6c3f..0000000000000 --- a/lib/web_ui/test/canvaskit/canvas_test.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@TestOn('chrome || safari || firefox') - -import 'dart:async'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; - -import '../engine/canvas_test.dart'; -import 'common.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -// Run the same semantics tests in CanvasKit mode because as of today we do not -// yet share platform view logic with the HTML renderer, which affects -// semantics. -Future testMain() async { - group('CanvasKit semantics', () { - setUpCanvasKitTest(); - - runCanvasTests(deviceClipRoundsOut: true); - }); -} diff --git a/lib/web_ui/test/engine/canvas_test.dart b/lib/web_ui/test/ui/canvas_test.dart similarity index 98% rename from lib/web_ui/test/engine/canvas_test.dart rename to lib/web_ui/test/ui/canvas_test.dart index 926ac1b3fe339..4c294778e040c 100644 --- a/lib/web_ui/test/engine/canvas_test.dart +++ b/lib/web_ui/test/ui/canvas_test.dart @@ -8,18 +8,20 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; - import 'package:ui/ui.dart' as ui; import '../matchers.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - await ui.webOnlyInitializePlatform(); - runCanvasTests(deviceClipRoundsOut: false); + setUpUiTest(); + + final bool deviceClipRoundsOut = renderer is! HtmlRenderer; + runCanvasTests(deviceClipRoundsOut: deviceClipRoundsOut); } void runCanvasTests({required bool deviceClipRoundsOut}) { diff --git a/lib/web_ui/test/engine/path_metrics_test.dart b/lib/web_ui/test/ui/path_metrics_test.dart similarity index 99% rename from lib/web_ui/test/engine/path_metrics_test.dart rename to lib/web_ui/test/ui/path_metrics_test.dart index d237d696fb9db..006c59df2450e 100644 --- a/lib/web_ui/test/engine/path_metrics_test.dart +++ b/lib/web_ui/test/ui/path_metrics_test.dart @@ -9,14 +9,16 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; import '../matchers.dart'; +import 'utils.dart'; -const double kTolerance = 0.001; +const double kTolerance = 0.1; void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { + setUpUiTest(); group('PathMetric length', () { test('empty path', () { final Path path = Path(); diff --git a/lib/web_ui/test/engine/picture_test.dart b/lib/web_ui/test/ui/picture_test.dart similarity index 98% rename from lib/web_ui/test/engine/picture_test.dart rename to lib/web_ui/test/ui/picture_test.dart index d4c5928d38fdb..daa9f9d8075c9 100644 --- a/lib/web_ui/test/engine/picture_test.dart +++ b/lib/web_ui/test/ui/picture_test.dart @@ -6,11 +6,15 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart' as ui; +import 'utils.dart'; + void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { + setUpUiTest(); + test('Picture construction invokes onCreate once', () async { int onCreateInvokedCount = 0; ui.Picture? createdPicture; diff --git a/third_party/canvaskit/BUILD.gn b/third_party/canvaskit/BUILD.gn index 429af65e42c24..63776f68d8585 100644 --- a/third_party/canvaskit/BUILD.gn +++ b/third_party/canvaskit/BUILD.gn @@ -3,36 +3,30 @@ # found in the LICENSE file. import("//build/toolchain/wasm.gni") -import("canvaskit.gni") -if (build_canvaskit) { - # This toolchain is only to be used by canvaskit_group below. - wasm_toolchain("canvaskit") { - extra_toolchain_args = { - skia_use_icu = true - skia_use_client_icu = false - } +# This toolchain is only to be used by the canvaskit target below. +wasm_toolchain("canvaskit") { + extra_toolchain_args = { + skia_use_icu = true + skia_use_client_icu = false } +} - group("canvaskit_group") { - visibility = [ "//flutter/web_sdk:*" ] - public_deps = [ "//third_party/skia/modules/canvaskit(:canvaskit)" ] - } +group("canvaskit_group") { + visibility = [ "//flutter/web_sdk:*" ] + public_deps = [ "//third_party/skia/modules/canvaskit(:canvaskit)" ] } -if (build_canvaskit_chromium) { - # This toolchain is only to be used by canvaskit_chromium_group below. - wasm_toolchain("canvaskit_chromium") { - extra_toolchain_args = { - skia_use_icu = false - skia_use_client_icu = true - skia_icu_bidi_third_party_dir = "//flutter/third_party/canvaskit/icu_bidi" - } +# This toolchain is only to be used by canvaskit_chromium_group below. +wasm_toolchain("canvaskit_chromium") { + extra_toolchain_args = { + skia_use_icu = false + skia_use_client_icu = true + skia_icu_bidi_third_party_dir = "//flutter/third_party/canvaskit/icu_bidi" } +} - group("canvaskit_chromium_group") { - visibility = [ "//flutter/web_sdk:*" ] - public_deps = - [ "//third_party/skia/modules/canvaskit(:canvaskit_chromium)" ] - } +group("canvaskit_chromium_group") { + visibility = [ "//flutter/web_sdk:*" ] + public_deps = [ "//third_party/skia/modules/canvaskit(:canvaskit_chromium)" ] } diff --git a/third_party/canvaskit/canvaskit.gni b/third_party/canvaskit/canvaskit.gni deleted file mode 100644 index fdf99e98a2088..0000000000000 --- a/third_party/canvaskit/canvaskit.gni +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -declare_args() { - build_canvaskit = false - build_canvaskit_chromium = false -} diff --git a/tools/gn b/tools/gn index 6205ce0450717..4aa78f229cc4d 100755 --- a/tools/gn +++ b/tools/gn @@ -261,10 +261,7 @@ def to_gn_args(args): to_gn_wasm_args(args, gn_args) return gn_args - gn_args['flutter_build_web_sdk'] = args.build_web_sdk gn_args['full_dart_sdk'] = args.full_dart_sdk - if args.build_web_sdk or args.full_dart_sdk: - gn_args['build_canvaskit'] = args.build_canvaskit if args.enable_unittests: gn_args['enable_unittests'] = args.enable_unittests @@ -652,13 +649,6 @@ def to_gn_wasm_args(args, gn_args): gn_args['skia_canvaskit_profile_build'] = is_profile_build gn_args['flutter_prebuilt_dart_sdk'] = True - # TODO(jacksongardner): Make this based off of the input argument rather - # than forced to true, once the recipes are updated. - # https://github.com/flutter/flutter/issues/113303 - gn_args['build_canvaskit'] = True - gn_args['build_canvaskit_chromium'] = args.build_canvaskit_chromium - gn_args['flutter_build_web_sdk'] = True - def parse_args(args): args = args[1:] @@ -856,32 +846,11 @@ def parse_args(args): '--no-full-dart-sdk', dest='full_dart_sdk', action='store_false' ) - parser.add_argument( - '--build-web-sdk', - default=False, - action='store_true', - help='build the flutter web sdk' - ) - parser.add_argument( - '--no-build-web-sdk', dest='build_web_sdk', action='store_false' - ) - parser.add_argument( '--build-canvaskit', default=False, action='store_true', - help='build canvaskit from source' - ) - parser.add_argument( - '--no-build-canvaskit', dest='build_canvaskit', action='store_false' - ) - - parser.add_argument( - '--build-canvaskit-chromium', - default=False, - dest='build_canvaskit_chromium', - action='store_true', - help='build the Chromium variant of CanvasKit from sources' + help='build canvaskit from source (DEPRECATED: use ninja targets to select what to build)' ) parser.add_argument( diff --git a/wasm/BUILD.gn b/wasm/BUILD.gn index f4d6a54ee5e76..ccd1ef8450b7f 100644 --- a/wasm/BUILD.gn +++ b/wasm/BUILD.gn @@ -10,7 +10,5 @@ import("//flutter/common/config.gni") # This is the default target when building when the target CPU is WASM. group("wasm") { - if (flutter_build_web_sdk) { - deps = [ "//flutter/web_sdk" ] - } + deps = [ "//flutter/web_sdk:flutter_web_sdk_archive" ] } diff --git a/web_sdk/BUILD.gn b/web_sdk/BUILD.gn index 987b16a529211..c09e5ca0dba02 100644 --- a/web_sdk/BUILD.gn +++ b/web_sdk/BUILD.gn @@ -4,13 +4,8 @@ import("//flutter/build/zip_bundle.gni") import("//flutter/common/config.gni") -import("//flutter/third_party/canvaskit/canvaskit.gni") import("//third_party/dart/build/dart/dart_action.gni") -declare_args() { - archive_flutter_web_sdk = true -} - dart_sdk_package_config = "//third_party/dart/.dart_tool/package_config.json" web_ui_sources = exec_script("//third_party/dart/tools/list_dart_files.py", @@ -35,10 +30,6 @@ group("web_sdk") { ":flutter_ddc_modules", ":flutter_platform_dills", ] - - if (archive_flutter_web_sdk && !is_fuchsia) { - deps += [ ":flutter_web_sdk_archive" ] - } } template("sdk_rewriter") { @@ -558,15 +549,11 @@ if (!is_fuchsia) { deps = [ ":flutter_ddc_modules", ":flutter_platform_dills", + "//flutter/third_party/canvaskit:canvaskit_group", + "//flutter/third_party/canvaskit:canvaskit_chromium_group", + "//flutter/lib/web_ui/skwasm", ] + web_engine_libraries - if (build_canvaskit) { - deps += [ "//flutter/third_party/canvaskit:canvaskit_group" ] - } - if (build_canvaskit_chromium) { - deps += [ "//flutter/third_party/canvaskit:canvaskit_chromium_group" ] - } - # flutter_ddc_modules sources = get_target_outputs(":flutter_dartdevc_kernel_sdk") sources += get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk") @@ -600,35 +587,40 @@ if (!is_fuchsia) { tmp_files += [ { source = rebase_path(source) - destination = rebase_path(source, "$root_build_dir") - }, - ] - } - if (build_canvaskit) { - tmp_files += [ - { - source = rebase_path("$root_out_dir/canvaskit/canvaskit.js") - destination = "flutter_web_sdk/canvaskit/canvaskit.js" - }, - { - source = rebase_path("$root_out_dir/canvaskit/canvaskit.wasm") - destination = "flutter_web_sdk/canvaskit/canvaskit.wasm" - }, - ] - } - if (build_canvaskit_chromium) { - tmp_files += [ - { - source = rebase_path("$root_out_dir/canvaskit_chromium/canvaskit.js") - destination = "flutter_web_sdk/canvaskit_chromium/canvaskit.js" - }, - { - source = - rebase_path("$root_out_dir/canvaskit_chromium/canvaskit.wasm") - destination = "flutter_web_sdk/canvaskit_chromium/canvaskit.wasm" + destination = rebase_path(source, "$root_build_dir/flutter_web_sdk") }, ] } + tmp_files += [ + { + source = rebase_path("$root_out_dir/canvaskit/canvaskit.js") + destination = "canvaskit/canvaskit.js" + }, + { + source = rebase_path("$root_out_dir/canvaskit/canvaskit.wasm") + destination = "canvaskit/canvaskit.wasm" + }, + { + source = rebase_path("$root_out_dir/canvaskit_chromium/canvaskit.js") + destination = "canvaskit/chromium/canvaskit.js" + }, + { + source = rebase_path("$root_out_dir/canvaskit_chromium/canvaskit.wasm") + destination = "canvaskit/chromium/canvaskit.wasm" + }, + { + source = rebase_path("$root_out_dir/skwasm.js") + destination = "canvaskit/skwasm.js" + }, + { + source = rebase_path("$root_out_dir/skwasm.worker.js") + destination = "canvaskit/skwasm.worker.js" + }, + { + source = rebase_path("$root_out_dir/skwasm.wasm") + destination = "canvaskit/skwasm.wasm" + }, + ] files = tmp_files } } diff --git a/web_sdk/sdk_rewriter.dart b/web_sdk/sdk_rewriter.dart index 4a11ed1940eb6..dd25ce9652cfb 100644 --- a/web_sdk/sdk_rewriter.dart +++ b/web_sdk/sdk_rewriter.dart @@ -166,6 +166,10 @@ List getExtraImportsForLibrary(String libraryName) { extraImports.add(entry.value); } } + + if (libraryName == 'skwasm_impl') { + extraImports.add("import 'dart:ffi';"); + } return extraImports; } diff --git a/web_sdk/test/sdk_rewriter_test.dart b/web_sdk/test/sdk_rewriter_test.dart index 54902cfc14e1e..0c6a6f6ac8aee 100644 --- a/web_sdk/test/sdk_rewriter_test.dart +++ b/web_sdk/test/sdk_rewriter_test.dart @@ -141,6 +141,7 @@ void printSomething() { "import 'dart:_engine';", "import 'dart:_web_unicode';", "import 'dart:_web_locale_keymap' as locale_keymap;", + "import 'dart:ffi';", ]); // Other libraries (should not have extra imports).