From 2ff14d733daaa340d8228a96d79fca504310a0c1 Mon Sep 17 00:00:00 2001 From: drwez Date: Thu, 13 Apr 2023 22:30:06 +0200 Subject: [PATCH 1/4] [fuchsia] Remove implementations & clients of deprecated CreateView (#41154) Prepare Flutter's Fuchsia port for removal of the fuchsia.ui.app.ViewProvider.CreateView() API. The Flutter engine's implementation of the API, which has long been deprecated, is removed. Calls to the deprecated CreateView() in tests are replaced with CreateViewWithViewRef(). Bug fxbug.dev/81285 --- shell/platform/fuchsia/flutter/component_v2.cc | 10 ---------- shell/platform/fuchsia/flutter/component_v2.h | 6 ------ .../embedder/parent-view/lib/parent_view.dart | 8 ++++++-- .../lib/embedding-flutter-view.dart | 8 ++++++-- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/shell/platform/fuchsia/flutter/component_v2.cc b/shell/platform/fuchsia/flutter/component_v2.cc index 9893de69e1765..3ad4169869ad4 100644 --- a/shell/platform/fuchsia/flutter/component_v2.cc +++ b/shell/platform/fuchsia/flutter/component_v2.cc @@ -628,16 +628,6 @@ void ComponentV2::OnEngineTerminate(const Engine* shell_holder) { } } -void ComponentV2::CreateView( - zx::eventpair token, - fidl::InterfaceRequest /*incoming_services*/, - fidl::InterfaceHandle< - fuchsia::sys::ServiceProvider> /*outgoing_services*/) { - auto view_ref_pair = scenic::ViewRefPair::New(); - CreateViewWithViewRef(std::move(token), std::move(view_ref_pair.control_ref), - std::move(view_ref_pair.view_ref)); -} - void ComponentV2::CreateViewWithViewRef( zx::eventpair view_token, fuchsia::ui::views::ViewRefControl control_ref, diff --git a/shell/platform/fuchsia/flutter/component_v2.h b/shell/platform/fuchsia/flutter/component_v2.h index c23f4406ed7fa..57a0aa62cdfe5 100644 --- a/shell/platform/fuchsia/flutter/component_v2.h +++ b/shell/platform/fuchsia/flutter/component_v2.h @@ -110,12 +110,6 @@ class ComponentV2 final // |fuchsia::component::runner::ComponentController| void Stop() override; - // |fuchsia::ui::app::ViewProvider| - void CreateView( - zx::eventpair token, - fidl::InterfaceRequest incoming_services, - fuchsia::sys::ServiceProviderHandle outgoing_services) override; - // |fuchsia::ui::app::ViewProvider| void CreateViewWithViewRef(zx::eventpair view_token, fuchsia::ui::views::ViewRefControl control_ref, diff --git a/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart b/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart index 5fd521661150d..93ab0c585fd57 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart @@ -238,9 +238,13 @@ ViewHolderToken _launchGfxChildView() { final viewTokens = EventPairPair(); assert(viewTokens.status == ZX.OK); final viewHolderToken = ViewHolderToken(value: viewTokens.first); - final viewToken = ViewToken(value: viewTokens.second); - viewProvider.createView(viewToken.value, null, null); + final viewRefs = EventPairPair(); + assert(viewRefs.status == ZX.OK); + final viewRefControl = ViewRefControl(reference: viewRefs.first.duplicate(ZX.DEFAULT_EVENTPAIR_RIGHTS & ~ZX.RIGHT_DUPLICATE)); + final viewRef = ViewRef(reference: viewRefs.second.duplicate(ZX.RIGHTS_BASIC)); + + viewProvider.createViewWithViewRef(viewTokens.second, viewRefControl, viewRef); viewProvider.ctrl.close(); return viewHolderToken; diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart b/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart index 7473fbdeebbe9..252ee55342cd2 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart @@ -238,9 +238,13 @@ ViewHolderToken _launchGfxChildView() { final viewTokens = EventPairPair(); assert(viewTokens.status == ZX.OK); final viewHolderToken = ViewHolderToken(value: viewTokens.first); - final viewToken = ViewToken(value: viewTokens.second); - viewProvider.createView(viewToken.value, null, null); + final viewRefs = EventPairPair(); + assert(viewRefs.status == ZX.OK); + final viewRefControl = ViewRefControl(reference: viewRefs.first.duplicate(ZX.DEFAULT_EVENTPAIR_RIGHTS & ~ZX.RIGHT_DUPLICATE)); + final viewRef = ViewRef(reference: viewRefs.second.duplicate(ZX.RIGHTS_BASIC)); + + viewProvider.createViewWithViewRef(viewTokens.second, viewRefControl, viewRef); viewProvider.ctrl.close(); return viewHolderToken; From d9690305303036cd81c35a754f6b6da589e5a4a6 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 13 Apr 2023 13:58:46 -0700 Subject: [PATCH 2/4] Implement Gradient Shaders and Fragment Shaders in Skwasm (#41144) Implement Gradient Shaders and Fragment Shaders in Skwasm --- ci/licenses_golden/licenses_flutter | 8 + lib/web_ui/lib/painting.dart | 4 +- lib/web_ui/lib/src/engine.dart | 1 + .../lib/src/engine/canvaskit/painting.dart | 123 +-------- .../lib/src/engine/canvaskit/renderer.dart | 2 - lib/web_ui/lib/src/engine/html/renderer.dart | 2 - lib/web_ui/lib/src/engine/shader_data.dart | 138 ++++++++++ .../lib/src/engine/skwasm/skwasm_impl.dart | 2 + .../src/engine/skwasm/skwasm_impl/layers.dart | 69 +++-- .../src/engine/skwasm/skwasm_impl/paint.dart | 15 +- .../skwasm/skwasm_impl/raw/raw_geometry.dart | 1 + .../skwasm/skwasm_impl/raw/raw_memory.dart | 28 +- .../skwasm/skwasm_impl/raw/raw_paint.dart | 5 + .../skwasm/skwasm_impl/raw/raw_shaders.dart | 147 ++++++++++ .../engine/skwasm/skwasm_impl/renderer.dart | 82 +++++- .../skwasm/skwasm_impl/scene_builder.dart | 40 +-- .../engine/skwasm/skwasm_impl/shaders.dart | 254 ++++++++++++++++++ .../engine/skwasm/skwasm_stub/renderer.dart | 17 +- lib/web_ui/skwasm/BUILD.gn | 1 + lib/web_ui/skwasm/paint.cpp | 10 + lib/web_ui/skwasm/shaders.cpp | 163 +++++++++++ .../test/canvaskit/fragment_program_test.dart | 4 +- lib/web_ui/test/ui/fragment_shader_test.dart | 72 +++++ lib/web_ui/test/ui/gradient_golden_test.dart | 123 +++++++++ lib/web_ui/test/ui/scene_builder_test.dart | 41 +++ lib/web_ui/test/ui/utils.dart | 37 ++- 26 files changed, 1198 insertions(+), 191 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/shader_data.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart create mode 100644 lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart create mode 100644 lib/web_ui/skwasm/shaders.cpp create mode 100644 lib/web_ui/test/ui/fragment_shader_test.dart create mode 100644 lib/web_ui/test/ui/gradient_golden_test.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 66fdc6c94173b..61cb7b1a2d696 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1985,6 +1985,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/services/buffers.dart + ../.. ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codec.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codecs.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/services/serialization.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/shader_data.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 @@ -2004,9 +2005,11 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_pa 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_shaders.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/scene_builder.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.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 @@ -2058,6 +2061,7 @@ 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/shaders.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 @@ -4572,6 +4576,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/buffers.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codec.dart 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/shader_data.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 @@ -4591,9 +4596,11 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_pain 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_shaders.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/scene_builder.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.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 @@ -4645,6 +4652,7 @@ 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/shaders.cpp FILE: ../../../flutter/lib/web_ui/skwasm/surface.cpp FILE: ../../../flutter/lib/web_ui/skwasm/wrappers.h FILE: ../../../flutter/runtime/dart_isolate.cc diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index 05c6182108c4e..59e3773ceabce 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -272,7 +272,7 @@ abstract class Shader { bool get debugDisposed; } -abstract class Gradient extends Shader { +abstract class Gradient implements Shader { factory Gradient.linear( Offset from, Offset to, @@ -736,7 +736,7 @@ class Shadow { String toString() => 'TextShadow($color, $offset, $blurRadius)'; } -abstract class ImageShader extends Shader { +abstract class ImageShader implements Shader { factory ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4, { FilterQuality? filterQuality, }) => engine.renderer.createImageShader(image, tmx, tmy, matrix4, filterQuality); diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 07e54ca03c127..8c6b9fcef6558 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -146,6 +146,7 @@ export 'engine/services/buffers.dart'; export 'engine/services/message_codec.dart'; export 'engine/services/message_codecs.dart'; export 'engine/services/serialization.dart'; +export 'engine/shader_data.dart'; export 'engine/shadow.dart'; export 'engine/svg.dart'; export 'engine/test_embedding.dart'; diff --git a/lib/web_ui/lib/src/engine/canvaskit/painting.dart b/lib/web_ui/lib/src/engine/canvaskit/painting.dart index 91e808f0c32cb..f046359b898f0 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/painting.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/painting.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:convert'; import 'dart:typed_data'; +import 'package:ui/src/engine/shader_data.dart'; import 'package:ui/ui.dart' as ui; import '../color_filter.dart'; @@ -318,130 +318,23 @@ final Float32List _invertColorMatrix = Float32List.fromList(const [ final ManagedSkColorFilter _invertColorFilter = ManagedSkColorFilter(CkMatrixColorFilter(_invertColorMatrix)); -class UniformData { - const UniformData({ - required this.name, - required this.location, - required this.type, - }); - - final String name; - final UniformType type; - final int location; - - static const UniformData empty = - UniformData(name: '', location: -1, type: UniformType.Float); -} - -enum UniformType { - Boolean, - SByte, - UByte, - Short, - UShort, - Int, - Uint, - Int64, - Uint64, - Half, - Float, - Double, - SampledImage, -} - -UniformType? uniformTypeFromJson(int value) { - switch (value) { - case 0: - return UniformType.Boolean; - case 1: - return UniformType.SByte; - case 2: - return UniformType.UByte; - case 3: - return UniformType.Short; - case 4: - return UniformType.UShort; - case 5: - return UniformType.Int; - case 6: - return UniformType.Uint; - case 7: - return UniformType.Int64; - case 8: - return UniformType.Uint64; - case 9: - return UniformType.Half; - case 10: - return UniformType.Float; - case 11: - return UniformType.Double; - case 12: - return UniformType.SampledImage; - } - return null; -} - class CkFragmentProgram implements ui.FragmentProgram { CkFragmentProgram(this.name, this.effect, this.uniforms, this.floatCount, this.textureCount); - static Future fromBytes(String name, Uint8List data) async { - final String contents = utf8.decode(data); - final Object? rawShaderData = json.decode(contents); - if (rawShaderData is! Map) { - throw const FormatException('Invalid Shader Data'); - } - final Object? source = rawShaderData['sksl']; - final Object? rawUniforms = rawShaderData['uniforms']; - if (source is! String || rawUniforms is! List) { - throw const FormatException('Invalid Shader Data'); - } - final SkRuntimeEffect? effect = MakeRuntimeEffect(source); + factory CkFragmentProgram.fromBytes(String name, Uint8List data) { + final ShaderData shaderData = ShaderData.fromBytes(data); + final SkRuntimeEffect? effect = MakeRuntimeEffect(shaderData.source); if (effect == null) { - throw const FormatException('Invalid Shader Data'); + throw const FormatException('Invalid Shader Source'); } - final List uniforms = List.filled(rawUniforms.length, UniformData.empty); - - int textureCount = 0; - int floatCount = 0; - for (int i = 0; i < rawUniforms.length; i += 1) { - final Object? rawUniformData = rawUniforms[i]; - if (rawUniformData is! Map) { - throw const FormatException('Invalid Shader Data'); - } - final Object? name = rawUniformData['name']; - final Object? location = rawUniformData['location']; - final Object? rawType = rawUniformData['type']; - if (name is! String || location is! int || rawType is! int) { - throw const FormatException('Invalid Shader Data'); - } - final UniformType? type = uniformTypeFromJson(rawType); - if (type == null) { - throw const FormatException('Invalid Shader Data'); - } - if (type == UniformType.SampledImage) { - textureCount += 1; - } else { - final Object? rows = rawUniformData['rows']; - final Object? bitWidth = rawUniformData['bit_width']; - if (bitWidth is! int || rows is! int) { - throw const FormatException('Invalid Shader Data'); - } - floatCount += (bitWidth ~/ 32) * rows; - } - uniforms[location] = UniformData( - name: name, - location: location, - type: type, - ); - } return CkFragmentProgram( name, effect, - uniforms, - floatCount, - textureCount, + shaderData.uniforms, + shaderData.floatCount, + shaderData.textureCount, ); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 5a78d009ca0d9..06db1bc9a2a61 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -138,8 +138,6 @@ class CanvasKitRenderer implements Renderer { List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4, - ui.Offset? focal, - double focalRadius = 0.0, ]) => CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); @override diff --git a/lib/web_ui/lib/src/engine/html/renderer.dart b/lib/web_ui/lib/src/engine/html/renderer.dart index dd155f2fc0efb..21de71dd1baee 100644 --- a/lib/web_ui/lib/src/engine/html/renderer.dart +++ b/lib/web_ui/lib/src/engine/html/renderer.dart @@ -91,8 +91,6 @@ class HtmlRenderer implements Renderer { List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4, - ui.Offset? focal, - double focalRadius = 0.0, ]) => GradientRadial(center, radius, colors, colorStops, tileMode, matrix4); @override diff --git a/lib/web_ui/lib/src/engine/shader_data.dart b/lib/web_ui/lib/src/engine/shader_data.dart new file mode 100644 index 0000000000000..9984e7123b7d0 --- /dev/null +++ b/lib/web_ui/lib/src/engine/shader_data.dart @@ -0,0 +1,138 @@ +// 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:convert'; +import 'dart:typed_data'; + +class ShaderData { + ShaderData({ + required this.source, + required this.uniforms, + required this.floatCount, + required this.textureCount, + }); + + factory ShaderData.fromBytes(Uint8List data) { + final String contents = utf8.decode(data); + final Object? rawShaderData = json.decode(contents); + if (rawShaderData is! Map) { + throw const FormatException('Invalid Shader Data'); + } + final Object? source = rawShaderData['sksl']; + final Object? rawUniforms = rawShaderData['uniforms']; + if (source is! String || rawUniforms is! List) { + throw const FormatException('Invalid Shader Data'); + } + + final List uniforms = List.filled(rawUniforms.length, UniformData.empty); + + int textureCount = 0; + int floatCount = 0; + for (int i = 0; i < rawUniforms.length; i += 1) { + final Object? rawUniformData = rawUniforms[i]; + if (rawUniformData is! Map) { + throw const FormatException('Invalid Shader Data'); + } + final Object? name = rawUniformData['name']; + final Object? location = rawUniformData['location']; + final Object? rawType = rawUniformData['type']; + if (name is! String || location is! int || rawType is! int) { + throw const FormatException('Invalid Shader Data'); + } + final UniformType? type = uniformTypeFromJson(rawType); + if (type == null) { + throw const FormatException('Invalid Shader Data'); + } + if (type == UniformType.SampledImage) { + textureCount += 1; + } else { + final Object? rows = rawUniformData['rows']; + final Object? bitWidth = rawUniformData['bit_width']; + if (bitWidth is! int || rows is! int) { + throw const FormatException('Invalid Shader Data'); + } + floatCount += (bitWidth ~/ 32) * rows; + } + uniforms[location] = UniformData( + name: name, + location: location, + type: type, + ); + } + return ShaderData( + source: source, + uniforms: uniforms, + floatCount: floatCount, + textureCount: textureCount, + ); + } + + String source; + List uniforms; + int floatCount; + int textureCount; +} + +class UniformData { + const UniformData({ + required this.name, + required this.location, + required this.type, + }); + + final String name; + final UniformType type; + final int location; + + static const UniformData empty = + UniformData(name: '', location: -1, type: UniformType.Float); +} + +enum UniformType { + Boolean, + SByte, + UByte, + Short, + UShort, + Int, + Uint, + Int64, + Uint64, + Half, + Float, + Double, + SampledImage, +} + +UniformType? uniformTypeFromJson(int value) { + switch (value) { + case 0: + return UniformType.Boolean; + case 1: + return UniformType.SByte; + case 2: + return UniformType.UByte; + case 3: + return UniformType.Short; + case 4: + return UniformType.UShort; + case 5: + return UniformType.Int; + case 6: + return UniformType.Uint; + case 7: + return UniformType.Int64; + case 8: + return UniformType.Uint64; + case 9: + return UniformType.Half; + case 10: + return UniformType.Float; + case 11: + return UniformType.Double; + case 12: + return UniformType.SampledImage; + } + return null; +} 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 566a2c88679d3..9d489df3ab489 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart @@ -26,8 +26,10 @@ 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_shaders.dart'; export 'skwasm_impl/raw/raw_surface.dart'; export 'skwasm_impl/renderer.dart'; export 'skwasm_impl/scene_builder.dart'; +export 'skwasm_impl/shaders.dart'; export 'skwasm_impl/surface.dart'; export 'skwasm_impl/vertices.dart'; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart index c2004d390581f..4bda6822b11d6 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart @@ -23,7 +23,7 @@ class BackdropFilterOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { // TODO(jacksongardner): Implement backdrop filter } } @@ -50,7 +50,7 @@ class ClipPathOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { if (clip == ui.Clip.antiAliasWithSaveLayer) { canvas.restore(); } @@ -80,7 +80,7 @@ class ClipRectOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { if (clip == ui.Clip.antiAliasWithSaveLayer) { canvas.restore(); } @@ -110,7 +110,7 @@ class ClipRRectOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { if (clip == ui.Clip.antiAliasWithSaveLayer) { canvas.restore(); } @@ -133,7 +133,7 @@ class ColorFilterOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { // TODO(jacksongardner): Implement color filter } } @@ -151,7 +151,7 @@ class ImageFilterOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { // TODO(jacksongardner): Implement image filter } } @@ -175,7 +175,7 @@ class OffsetOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { canvas.restore(); } } @@ -205,7 +205,7 @@ class OpacityOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { canvas.restore(); if (offset != ui.Offset.zero) { canvas.restore(); @@ -232,11 +232,48 @@ class TransformOperation implements LayerOperation { } @override - void post(ui.Canvas canvas) { + void post(ui.Canvas canvas, ui.Rect contentRect) { canvas.restore(); } } +class ShaderMaskLayer + with PictureLayer + implements ui.ShaderMaskEngineLayer {} +class ShaderMaskOperation implements LayerOperation { + ShaderMaskOperation(this.shader, this.maskRect, this.blendMode); + + final ui.Shader shader; + final ui.Rect maskRect; + final ui.BlendMode blendMode; + + @override + ui.Rect cullRect(ui.Rect contentRect) => contentRect; + + @override + void pre(ui.Canvas canvas, ui.Rect contentRect) { + canvas.saveLayer( + contentRect, + ui.Paint(), + ); + } + + @override + void post(ui.Canvas canvas, ui.Rect contentRect) { + canvas.save(); + canvas.translate(maskRect.left, maskRect.top); + canvas.drawRect( + ui.Rect.fromLTWH(0, 0, maskRect.width, maskRect.height), + ui.Paint() + ..blendMode = blendMode + ..shader = shader + ); + canvas.restore(); + canvas.restore(); + } +} + + mixin PictureLayer implements ui.EngineLayer { ui.Picture? picture; @@ -251,7 +288,7 @@ abstract class LayerOperation { ui.Rect cullRect(ui.Rect contentRect); void pre(ui.Canvas canvas, ui.Rect contentRect); - void post(ui.Canvas canvas); + void post(ui.Canvas canvas, ui.Rect contentRect); } class PictureDrawCommand { @@ -284,14 +321,15 @@ class LayerBuilder { final PictureLayer? layer; final LayerOperation? operation; final List drawCommands = []; - ui.Rect contentRect = ui.Rect.zero; + ui.Rect? contentRect; ui.Picture build() { - final ui.Rect rect = operation?.cullRect(contentRect) ?? contentRect; + final ui.Rect drawnRect = contentRect ?? ui.Rect.zero; + final ui.Rect rect = operation?.cullRect(drawnRect) ?? drawnRect; final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder, rect); - operation?.pre(canvas, contentRect); + operation?.pre(canvas, rect); for (final PictureDrawCommand command in drawCommands) { if (command.offset != ui.Offset.zero) { canvas.save(); @@ -302,7 +340,7 @@ class LayerBuilder { canvas.drawPicture(command.picture); } } - operation?.post(canvas); + operation?.post(canvas, rect); final ui.Picture picture = recorder.endRecording(); layer?.picture = picture; return picture; @@ -316,6 +354,7 @@ class LayerBuilder { }) { drawCommands.add(PictureDrawCommand(offset, picture)); final ui.Rect cullRect = (picture as SkwasmPicture).cullRect; - contentRect = contentRect.expandToInclude(cullRect.shift(offset)); + final ui.Rect shiftedRect = cullRect.shift(offset); + contentRect = contentRect?.expandToInclude(shiftedRect) ?? shiftedRect; } } 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 index daabca6a03ce8..3c953df026394 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart @@ -2,6 +2,8 @@ // 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'; import 'package:ui/ui.dart' as ui; @@ -17,6 +19,8 @@ class SkwasmPaint implements ui.Paint { ui.BlendMode _cachedBlendMode = ui.BlendMode.srcOver; + SkwasmShader? _shader; + @override ui.BlendMode get blendMode { return _cachedBlendMode; @@ -89,5 +93,14 @@ class SkwasmPaint implements ui.Paint { ui.MaskFilter? maskFilter; @override - ui.Shader? shader; + ui.Shader? get shader => _shader; + + @override + set shader(ui.Shader? uiShader) { + final SkwasmShader? skwasmShader = uiShader as SkwasmShader?; + _shader = skwasmShader; + final ShaderHandle shaderHandle = + skwasmShader != null ? skwasmShader.handle : nullptr; + paintSetShader(_handle, shaderHandle); + } } 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 index 74fc835bb920b..3bf14ad9b25c7 100644 --- 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 @@ -10,3 +10,4 @@ typedef RawRRect = Pointer; typedef RawPointArray = Pointer; typedef RawMatrix33 = Pointer; typedef RawMatrix44 = Pointer; +typedef RawColorArray = 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 index 6a5c68a7479a8..9c88cc55091d2 100644 --- 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 @@ -36,7 +36,7 @@ class StackScope { return pointer; } - Pointer convertMatrix4toSkMatrix(Float64List matrix4) { + Pointer convertMatrix4toSkMatrix(List matrix4) { final Pointer pointer = allocFloatArray(9); final int matrixLength = matrix4.length; @@ -132,6 +132,14 @@ class StackScope { return pointer; } + Pointer convertDoublesToNative(List values) { + final Pointer pointer = allocFloatArray(values.length); + for (int i = 0; i < values.length; i++) { + pointer[i] = values[i]; + } + return pointer; + } + Pointer convertPointArrayToNative(List points) { final Pointer pointer = allocFloatArray(points.length * 2); for (int i = 0; i < points.length; i++) { @@ -141,6 +149,14 @@ class StackScope { return pointer; } + Pointer convertColorArrayToNative(List colors) { + final Pointer pointer = allocUint32Array(colors.length); + for (int i = 0; i < colors.length; i++) { + pointer[i] = colors[i].value; + } + return pointer; + } + Pointer allocInt8Array(int count) { final int length = count * sizeOf(); return stackAlloc(length).cast(); @@ -151,10 +167,20 @@ class StackScope { return stackAlloc(length).cast(); } + Pointer allocUint32Array(int count) { + final int length = count * sizeOf(); + return stackAlloc(length).cast(); + } + Pointer allocFloatArray(int count) { final int length = count * sizeOf(); return stackAlloc(length).cast(); } + + Pointer> allocPointerArray(int count) { + final int length = count * sizeOf>(); + return stackAlloc(length).cast>(); + } } T withStackScope(T Function(StackScope scope) f) { 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 index 5d59a362db27d..0ac1262663cd9 100644 --- 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 @@ -7,6 +7,8 @@ library skwasm_impl; import 'dart:ffi'; +import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; + final class RawPaint extends Opaque {} typedef PaintHandle = Pointer; @@ -61,3 +63,6 @@ external void paintSetMiterLimit(PaintHandle paint, double miterLimit); @Native(symbol: 'paint_getMiterLimit', isLeaf: true) external double paintGetMiterLimit(PaintHandle paint); + +@Native(symbol: 'paint_setShader', isLeaf: true) +external void paintSetShader(PaintHandle handle, ShaderHandle shader); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart new file mode 100644 index 0000000000000..beadca256a28d --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart @@ -0,0 +1,147 @@ +// 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'; + +final class RawShader extends Opaque {} +typedef ShaderHandle = Pointer; + +final class RawSkString extends Opaque {} +typedef SkStringHandle = Pointer; + +final class RawSkData extends Opaque {} +typedef SkDataHandle = Pointer; + +final class RawRuntimeEffect extends Opaque {} +typedef RuntimeEffectHandle = Pointer; + +@Native, + Int, + Int, + RawMatrix33, +)>(symbol: 'shader_createLinearGradient', isLeaf: true) +external ShaderHandle shaderCreateLinearGradient( + RawPointArray endPoints, // two points + RawColorArray colors, + Pointer stops, // Can be nullptr + int count, // Number of stops/colors + int tileMode, + RawMatrix33 matrix, // Can be nullptr +); + +@Native, + Int, + Int, + RawMatrix33, +)>(symbol: 'shader_createRadialGradient', isLeaf: true) +external ShaderHandle shaderCreateRadialGradient( + double centerX, + double centerY, + double radius, + RawColorArray colors, + Pointer stops, + int count, + int tileMode, + RawMatrix33 localMatrix, +); + +@Native, + Int, + Int, + RawMatrix33, +)>(symbol: 'shader_createConicalGradient', isLeaf: true) +external ShaderHandle shaderCreateConicalGradient( + RawPointArray endPoints, // Two points, + double startRadius, + double endRadius, + RawColorArray colors, + Pointer stops, + int count, + int tileMode, + RawMatrix33 localMatrix, +); + +@Native, + Int, + Int, + Float, + Float, + RawMatrix33, +)>(symbol: 'shader_createSweepGradient', isLeaf: true) +external ShaderHandle shaderCreateSweepGradient( + double centerX, + double centerY, + RawColorArray colors, + Pointer stops, + int count, + int tileMode, + double startAngle, + double endAngle, + RawMatrix33 localMatrix +); + +@Native(symbol: 'shader_dispose', isLeaf: true) +external void shaderDispose(ShaderHandle handle); + +@Native(symbol: 'shaderSource_allocate', isLeaf: true) +external SkStringHandle shaderSourceAllocate(int size); + +@Native Function(SkStringHandle)>(symbol: 'shaderSource_getData', isLeaf: true) +external Pointer shaderSourceGetData(SkStringHandle handle); + +@Native(symbol: 'shaderSource_free', isLeaf: true) +external void shaderSourceFree(SkStringHandle handle); + +@Native(symbol: 'runtimeEffect_create', isLeaf: true) +external RuntimeEffectHandle runtimeEffectCreate(SkStringHandle source); + +@Native(symbol: 'runtimeEffect_dispose', isLeaf: true) +external void runtimeEffectDispose(RuntimeEffectHandle handle); + +@Native(symbol: 'runtimeEffect_getUniformSize', isLeaf: true) +external int runtimeEffectGetUniformSize(RuntimeEffectHandle handle); + +@Native(symbol: 'data_create', isLeaf: true) +external SkDataHandle dataCreate(int size); + +@Native Function(SkDataHandle)>(symbol: 'data_getPointer', isLeaf: true) +external Pointer dataGetPointer(SkDataHandle handle); + +@Native(symbol: 'data_dispose', isLeaf: true) +external void dataDispose(SkDataHandle handle); + +@Native, + Size +)>(symbol: 'shader_createRuntimeEffectShader', isLeaf: true) +external ShaderHandle shaderCreateRuntimeEffectShader( + RuntimeEffectHandle runtimeEffect, + SkDataHandle uniforms, + Pointer childShaders, + int childCount +); 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 d43bcd01e1a43..4740a2f4e9724 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 @@ -45,9 +45,24 @@ class SkwasmRenderer implements Renderer { } @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('createConicalGradient not yet implemented'); - } + 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]) => SkwasmGradient.conical( + focal: focal, + focalRadius: focalRadius, + center: center, + centerRadius: radius, + colors: colors, + colorStops: colorStops, + tileMode: tileMode, + matrix4: matrix, + ); @override ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { @@ -65,9 +80,21 @@ class SkwasmRenderer implements Renderer { } @override - ui.Gradient createLinearGradient(ui.Offset from, ui.Offset to, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) { - throw UnimplementedError('createLinearGradientn not yet implemented'); - } + ui.Gradient createLinearGradient( + ui.Offset from, + ui.Offset to, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4 + ]) => SkwasmGradient.linear( + from: from, + to: to, + colors: colors, + colorStops: colorStops, + tileMode: tileMode, + matrix4: matrix4, + ); @override ui.ImageFilter createMatrixImageFilter(Float64List matrix4, {ui.FilterQuality filterQuality = ui.FilterQuality.low}) { @@ -102,9 +129,21 @@ class SkwasmRenderer implements Renderer { 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('createRadialGradient not yet implemented'); - } + ui.Gradient createRadialGradient( + ui.Offset center, + double radius, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4 + ]) => SkwasmGradient.radial( + center: center, + radius: radius, + colors: colors, + colorStops: colorStops, + tileMode: tileMode, + matrix4: matrix4 + ); @override ui.SceneBuilder createSceneBuilder() => SkwasmSceneBuilder(); @@ -133,9 +172,15 @@ class SkwasmRenderer implements Renderer { double startAngle = 0.0, double endAngle = math.pi * 2, Float32List? matrix4 - ]) { - throw UnimplementedError('createSweepGradient not yet implemented'); - } + ]) => SkwasmGradient.sweep( + center: center, + colors: colors, + colorStops: colorStops, + tileMode: tileMode, + startAngle: startAngle, + endAngle: endAngle, + matrix4: matrix4 + ); @override ui.TextStyle createTextStyle({ @@ -251,11 +296,20 @@ class SkwasmRenderer implements Renderer { embedder.addSceneToSceneHost(sceneElement); } + static final Map> _programs = >{}; + @override - void clearFragmentProgramCache() { } + void clearFragmentProgramCache() { + _programs.clear(); + } @override Future createFragmentProgram(String assetKey) { - throw UnimplementedError('createFragmentProgram not yet implemented'); + if (_programs.containsKey(assetKey)) { + return _programs[assetKey]!; + } + return _programs[assetKey] = assetManager.load(assetKey).then((ByteData data) { + return SkwasmFragmentProgram.fromBytes(assetKey, data.buffer.asUint8List()); + }); } } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart index 8afc70a8f3a8c..e2cc62508bf03 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart @@ -90,36 +90,30 @@ class SkwasmSceneBuilder implements ui.SceneBuilder { ui.ImageFilter filter, { ui.BlendMode blendMode = ui.BlendMode.srcOver, ui.BackdropFilterEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( BackdropFilterLayer(), BackdropFilterOperation() ); - } @override ui.ClipPathEngineLayer pushClipPath( ui.Path path, { ui.Clip clipBehavior = ui.Clip.antiAlias, ui.ClipPathEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( ClipPathLayer(), ClipPathOperation(path, clipBehavior), ); - } @override ui.ClipRRectEngineLayer pushClipRRect( ui.RRect rrect, { required ui.Clip clipBehavior, ui.ClipRRectEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( ClipRRectLayer(), ClipRRectOperation(rrect, clipBehavior) ); - } @override ui.ClipRectEngineLayer pushClipRect( @@ -137,47 +131,39 @@ class SkwasmSceneBuilder implements ui.SceneBuilder { ui.ColorFilterEngineLayer pushColorFilter( ui.ColorFilter filter, { ui.ColorFilterEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( ColorFilterLayer(), ColorFilterOperation(), ); - } @override ui.ImageFilterEngineLayer pushImageFilter( ui.ImageFilter filter, { ui.Offset offset = ui.Offset.zero, ui.ImageFilterEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( ImageFilterLayer(), ImageFilterOperation(), ); - } @override ui.OffsetEngineLayer pushOffset( double dx, double dy, { ui.OffsetEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( OffsetLayer(), OffsetOperation(dx, dy) ); - } @override ui.OpacityEngineLayer pushOpacity(int alpha, { ui.Offset offset = ui.Offset.zero, ui.OpacityEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( OpacityLayer(), OpacityOperation(alpha, offset), ); - } @override ui.PhysicalShapeEngineLayer pushPhysicalShape({ @@ -199,21 +185,19 @@ class SkwasmSceneBuilder implements ui.SceneBuilder { ui.BlendMode blendMode, { ui.ShaderMaskEngineLayer? oldLayer, ui.FilterQuality filterQuality = ui.FilterQuality.low - }) { - // TODO(jacksongardner): implement pushShaderMask - throw UnimplementedError(); - } + }) => pushLayer( + ShaderMaskLayer(), + ShaderMaskOperation(shader, maskRect, blendMode) + ); @override ui.TransformEngineLayer pushTransform( Float64List matrix4, { ui.TransformEngineLayer? oldLayer - }) { - return pushLayer( + }) => pushLayer( TransformLayer(), TransformOperation(matrix4), ); - } @override void setCheckerboardOffscreenLayers(bool checkerboard) { diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart new file mode 100644 index 0000000000000..dc37b92666143 --- /dev/null +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart @@ -0,0 +1,254 @@ +// 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:convert'; +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; + +abstract class SkwasmShader implements ui.Shader { + ShaderHandle get handle; + + @override + bool get debugDisposed => handle == nullptr; + + @override + void dispose() { + if (handle != nullptr) { + shaderDispose(handle); + } + } +} + +class SkwasmGradient extends SkwasmShader implements ui.Gradient { + factory SkwasmGradient.linear({ + required ui.Offset from, + required ui.Offset to, + required List colors, + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + }) => withStackScope((StackScope scope) { + final RawPointArray endPoints = + scope.convertPointArrayToNative([from, to]); + final RawColorArray nativeColors = scope.convertColorArrayToNative(colors); + final Pointer stops = colorStops != null + ? scope.convertDoublesToNative(colorStops) + : nullptr; + final Pointer matrix = matrix4 != null + ? scope.convertMatrix4toSkMatrix(matrix4) + : nullptr; + final ShaderHandle handle = shaderCreateLinearGradient( + endPoints, + nativeColors, + stops, + colors.length, + tileMode.index, + matrix + ); + return SkwasmGradient._(handle); + }); + + factory SkwasmGradient.radial({ + required ui.Offset center, + required double radius, + required List colors, + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + }) => withStackScope((StackScope scope) { + final RawColorArray rawColors = scope.convertColorArrayToNative(colors); + final Pointer rawStops = colorStops != null + ? scope.convertDoublesToNative(colorStops) + : nullptr; + final Pointer matrix = matrix4 != null + ? scope.convertMatrix4toSkMatrix(matrix4) + : nullptr; + final ShaderHandle handle = shaderCreateRadialGradient( + center.dx, + center.dy, + radius, + rawColors, + rawStops, + colors.length, + tileMode.index, + matrix, + ); + return SkwasmGradient._(handle); + }); + + factory SkwasmGradient.conical({ + required ui.Offset focal, + required double focalRadius, + required ui.Offset center, + required double centerRadius, + required List colors, + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + }) => withStackScope((StackScope scope) { + final RawPointArray endPoints = + scope.convertPointArrayToNative([focal, center]); + final RawColorArray rawColors = scope.convertColorArrayToNative(colors); + final Pointer rawStops = colorStops != null + ? scope.convertDoublesToNative(colorStops) + : nullptr; + final Pointer matrix = matrix4 != null + ? scope.convertMatrix4toSkMatrix(matrix4) + : nullptr; + final ShaderHandle handle = shaderCreateConicalGradient( + endPoints, + focalRadius, + centerRadius, + rawColors, + rawStops, + colors.length, + tileMode.index, + matrix + ); + return SkwasmGradient._(handle); + }); + + factory SkwasmGradient.sweep({ + required ui.Offset center, + required List colors, + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + required double startAngle, + required double endAngle, + Float32List? matrix4, + }) => withStackScope((StackScope scope) { + final RawColorArray rawColors = scope.convertColorArrayToNative(colors); + final Pointer rawStops = colorStops != null + ? scope.convertDoublesToNative(colorStops) + : nullptr; + final Pointer matrix = matrix4 != null + ? scope.convertMatrix4toSkMatrix(matrix4) + : nullptr; + final ShaderHandle handle = shaderCreateSweepGradient( + center.dx, + center.dy, + rawColors, + rawStops, + colors.length, + tileMode.index, + ui.toDegrees(startAngle), + ui.toDegrees(endAngle), + matrix + ); + return SkwasmGradient._(handle); + }); + + SkwasmGradient._(this.handle); + + @override + ShaderHandle handle; + + @override + void dispose() { + super.dispose(); + handle = nullptr; + } +} + +class SkwasmFragmentProgram implements ui.FragmentProgram { + SkwasmFragmentProgram._(this.name, this.handle); + factory SkwasmFragmentProgram.fromBytes(String name, Uint8List bytes) { + final ShaderData shaderData = ShaderData.fromBytes(bytes); + + // TODO(jacksongardner): Can we avoid this copy? + final List sourceData = utf8.encode(shaderData.source); + final SkStringHandle sourceString = shaderSourceAllocate(sourceData.length); + final Pointer sourceBuffer = shaderSourceGetData(sourceString); + int i = 0; + for (final int byte in sourceData) { + sourceBuffer[i] = byte; + i++; + } + final RuntimeEffectHandle handle = runtimeEffectCreate(sourceString); + shaderSourceFree(sourceString); + return SkwasmFragmentProgram._(name, handle); + } + + RuntimeEffectHandle handle; + String name; + + @override + ui.FragmentShader fragmentShader() => + SkwasmFragmentShader(this); + + int get uniformSize => runtimeEffectGetUniformSize(handle); + + void dispose() { + runtimeEffectDispose(handle); + } +} + +class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader { + SkwasmFragmentShader( + SkwasmFragmentProgram program, { + List? childShaders, + }) : _program = program, + _uniformData = dataCreate(program.uniformSize), + _childShaders = childShaders; + + @override + ShaderHandle get handle { + if (_handle == nullptr) { + _handle = withStackScope((StackScope s) { + Pointer childShaders = nullptr; + final int childCount = _childShaders != null ? _childShaders!.length : 0; + if (childCount != 0) { + childShaders = s.allocPointerArray(childCount) + .cast(); + final List shaders = _childShaders!; + for (int i = 0; i < childCount; i++) { + childShaders[i] = shaders[i].handle; + } + } + return shaderCreateRuntimeEffectShader( + _program.handle, + _uniformData, + childShaders, + childCount, + ); + }); + } + return _handle; + } + + ShaderHandle _handle = nullptr; + final SkwasmFragmentProgram _program; + SkDataHandle _uniformData; + final List? _childShaders; + + @override + void setFloat(int index, double value) { + if (_handle != nullptr) { + // Invalidate the previous shader so that it is recreated with the new + // uniform data. + shaderDispose(_handle); + _handle = nullptr; + } + final Pointer dataPointer = dataGetPointer(_uniformData).cast(); + dataPointer[index] = value; + } + + @override + void setImageSampler(int index, ui.Image image) { + // TODO(jacksongardner): implement this when images are implemented + } + + @override + void dispose() { + super.dispose(); + if (_uniformData != nullptr) { + dataDispose(_uniformData); + _uniformData = nullptr; + } + } +} diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart index 9102aed5d4fba..40082de28d793 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart @@ -6,13 +6,9 @@ import 'dart:async'; import 'dart:math' as math; import 'dart:typed_data'; +import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../embedder.dart'; -import '../../fonts.dart'; -import '../../html_image_codec.dart'; -import '../../renderer.dart'; - class SkwasmRenderer implements Renderer { @override ui.Path combinePaths(ui.PathOperation op, ui.Path path1, ui.Path path2) { @@ -166,10 +162,17 @@ class SkwasmRenderer implements Renderer { } @override - void clearFragmentProgramCache() { } + void clearFragmentProgramCache() => _programs.clear(); + + static final Map> _programs = >{}; @override Future createFragmentProgram(String assetKey) { - throw UnimplementedError('Skwasm not implemented on this platform.'); + if (_programs.containsKey(assetKey)) { + return _programs[assetKey]!; + } + return _programs[assetKey] = assetManager.load(assetKey).then((ByteData data) { + return CkFragmentProgram.fromBytes(assetKey, data.buffer.asUint8List()); + }); } } diff --git a/lib/web_ui/skwasm/BUILD.gn b/lib/web_ui/skwasm/BUILD.gn index 301716de81762..7ad61d87fefcd 100644 --- a/lib/web_ui/skwasm/BUILD.gn +++ b/lib/web_ui/skwasm/BUILD.gn @@ -13,6 +13,7 @@ wasm_lib("skwasm") { "paint.cpp", "path.cpp", "picture.cpp", + "shaders.cpp", "surface.cpp", "wrappers.h", ] diff --git a/lib/web_ui/skwasm/paint.cpp b/lib/web_ui/skwasm/paint.cpp index 84f1b90bd39ef..eb66fff6e292a 100644 --- a/lib/web_ui/skwasm/paint.cpp +++ b/lib/web_ui/skwasm/paint.cpp @@ -6,6 +6,7 @@ #include "export.h" #include "helpers.h" #include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkShader.h" using namespace Skwasm; @@ -82,3 +83,12 @@ SKWASM_EXPORT void paint_setMiterLimit(SkPaint* paint, SkScalar miterLimit) { SKWASM_EXPORT SkScalar paint_getMiterLImit(SkPaint* paint) { return paint->getStrokeMiter(); } + +SKWASM_EXPORT void paint_setShader(SkPaint* paint, SkShader* shader) { + if (shader == nullptr) { + paint->setShader(nullptr); + return; + } + shader->ref(); + return paint->setShader(sk_sp(shader)); +} diff --git a/lib/web_ui/skwasm/shaders.cpp b/lib/web_ui/skwasm/shaders.cpp new file mode 100644 index 0000000000000..1c29c10e844fb --- /dev/null +++ b/lib/web_ui/skwasm/shaders.cpp @@ -0,0 +1,163 @@ +// 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/effects/SkGradientShader.h" +#include "third_party/skia/include/effects/SkRuntimeEffect.h" +#include "wrappers.h" + +using namespace Skwasm; + +SKWASM_EXPORT SkShader* shader_createLinearGradient( + SkPoint* endPoints, // Two points + SkColor* colors, + SkScalar* stops, + int count, // Number of stops/colors + SkTileMode tileMode, + SkScalar* matrix33 // Can be nullptr +) { + if (matrix33) { + SkMatrix localMatrix = createMatrix(matrix33); + return SkGradientShader::MakeLinear(endPoints, colors, stops, count, + tileMode, 0, &localMatrix) + .release(); + } else { + return SkGradientShader::MakeLinear(endPoints, colors, stops, count, + tileMode) + .release(); + } +} + +SKWASM_EXPORT SkShader* shader_createRadialGradient(SkScalar centerX, + SkScalar centerY, + SkScalar radius, + SkColor* colors, + SkScalar* stops, + int count, + SkTileMode tileMode, + SkScalar* matrix33) { + if (matrix33) { + SkMatrix localMatrix = createMatrix(matrix33); + return SkGradientShader::MakeRadial({centerX, centerY}, radius, colors, + stops, count, tileMode, 0, &localMatrix) + .release(); + } else { + return SkGradientShader::MakeRadial({centerX, centerY}, radius, colors, + stops, count, tileMode) + .release(); + } +} + +SKWASM_EXPORT SkShader* shader_createConicalGradient( + SkPoint* endPoints, // Two points + SkScalar startRadius, + SkScalar endRadius, + SkColor* colors, + SkScalar* stops, + int count, + SkTileMode tileMode, + SkScalar* matrix33) { + if (matrix33) { + SkMatrix localMatrix = createMatrix(matrix33); + return SkGradientShader::MakeTwoPointConical( + endPoints[0], startRadius, endPoints[1], endRadius, colors, + stops, count, tileMode, 0, &localMatrix) + .release(); + + } else { + return SkGradientShader::MakeTwoPointConical(endPoints[0], startRadius, + endPoints[1], endRadius, + colors, stops, count, tileMode) + .release(); + } +} + +SKWASM_EXPORT SkShader* shader_createSweepGradient(SkScalar centerX, + SkScalar centerY, + SkColor* colors, + SkScalar* stops, + int count, + SkTileMode tileMode, + SkScalar startAngle, + SkScalar endAngle, + SkScalar* matrix33) { + if (matrix33) { + SkMatrix localMatrix = createMatrix(matrix33); + return SkGradientShader::MakeSweep(centerX, centerY, colors, stops, count, + tileMode, startAngle, endAngle, 0, + &localMatrix) + .release(); + } else { + return SkGradientShader::MakeSweep(centerX, centerY, colors, stops, count, + tileMode, startAngle, endAngle, 0, + nullptr) + .release(); + } +} + +SKWASM_EXPORT void shader_dispose(SkShader* shader) { + shader->unref(); +} + +SKWASM_EXPORT SkString* shaderSource_allocate(size_t length) { + return new SkString(length); +} + +SKWASM_EXPORT char* shaderSource_getData(SkString* string) { + return string->data(); +} + +SKWASM_EXPORT void shaderSource_free(SkString* string) { + return delete string; +} + +SKWASM_EXPORT SkRuntimeEffect* runtimeEffect_create(SkString* source) { + auto result = SkRuntimeEffect::MakeForShader(*source); + if (result.effect == nullptr) { + printf("Failed to compile shader. Error text:\n%s", + result.errorText.data()); + return nullptr; + } else { + return result.effect.release(); + } +} + +SKWASM_EXPORT void runtimeEffect_dispose(SkRuntimeEffect* effect) { + effect->unref(); +} + +SKWASM_EXPORT size_t runtimeEffect_getUniformSize(SkRuntimeEffect* effect) { + return effect->uniformSize(); +} + +SKWASM_EXPORT SkData* data_create(size_t size) { + return SkData::MakeUninitialized(size).release(); +} + +SKWASM_EXPORT void* data_getPointer(SkData* data) { + return data->writable_data(); +} + +SKWASM_EXPORT void data_dispose(SkData* data) { + return data->unref(); +} + +SKWASM_EXPORT SkShader* shader_createRuntimeEffectShader( + SkRuntimeEffect* runtimeEffect, + SkData* uniforms, + SkShader** children, + size_t childCount) { + std::vector> childPointers; + for (size_t i = 0; i < childCount; i++) { + auto child = children[i]; + child->ref(); + childPointers.emplace_back(child); + } + return runtimeEffect + ->makeShader(SkData::MakeWithCopy(uniforms->data(), uniforms->size()), + childPointers.data(), childCount, nullptr) + .release(); +} diff --git a/lib/web_ui/test/canvaskit/fragment_program_test.dart b/lib/web_ui/test/canvaskit/fragment_program_test.dart index 178188ccf6635..632ba07792091 100644 --- a/lib/web_ui/test/canvaskit/fragment_program_test.dart +++ b/lib/web_ui/test/canvaskit/fragment_program_test.dart @@ -185,9 +185,9 @@ void testMain() { await ui.webOnlyInitializePlatform(); }); - test('FragmentProgram can be created from JSON IPLR bundle', () async { + test('FragmentProgram can be created from JSON IPLR bundle', () { final Uint8List data = utf8.encode(kJsonIPLR) as Uint8List; - final CkFragmentProgram program = await CkFragmentProgram.fromBytes('test', data); + final CkFragmentProgram program = CkFragmentProgram.fromBytes('test', data); expect(program.effect, isNotNull); expect(program.floatCount, 32); diff --git a/lib/web_ui/test/ui/fragment_shader_test.dart b/lib/web_ui/test/ui/fragment_shader_test.dart new file mode 100644 index 0000000000000..1fef73e8e0f55 --- /dev/null +++ b/lib/web_ui/test/ui/fragment_shader_test.dart @@ -0,0 +1,72 @@ +// 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:convert'; +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 'package:web_engine_tester/golden_tester.dart'; + +import 'utils.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +// This fragment shader generates some voronoi noise. It uses a pseudo-random +// number generator implemented in the shader itself, so its output is +// deterministic. +const String kVoronoiShaderSksl = r''' +{ + "sksl": "// This SkSL shader is autogenerated by spirv-cross.\n\nfloat4 flutter_FragCoord;\n\nuniform float uTileSize;\n\nvec4 fragColor;\n\nvec2 FLT_flutter_local_FlutterFragCoord()\n{\n return flutter_FragCoord.xy;\n}\n\nfloat FLT_flutter_local_rand(vec2 co)\n{\n return fract(sin(dot(co, vec2(12.98980045318603515625, 78.233001708984375))) * 43758.546875);\n}\n\nvec2 FLT_flutter_local_fuzzGridPoint(vec2 coordinate)\n{\n vec2 param = coordinate * 400.0;\n vec2 param_1 = coordinate * 400.0;\n return coordinate + vec2((FLT_flutter_local_rand(param) - 0.5) * 0.800000011920928955078125, (FLT_flutter_local_rand(param_1) - 0.5) * 0.800000011920928955078125);\n}\n\nvec3 FLT_flutter_local_getColorForGridPoint(vec2 coordinate)\n{\n vec2 param = coordinate * 100.0;\n vec2 param_1 = coordinate * 200.0;\n vec2 param_2 = coordinate * 300.0;\n return vec3(FLT_flutter_local_rand(param), FLT_flutter_local_rand(param_1), FLT_flutter_local_rand(param_2));\n}\n\nvoid FLT_main()\n{\n vec2 uv = FLT_flutter_local_FlutterFragCoord() / vec2(uTileSize);\n vec2 upperLeft = floor(uv);\n vec2 upperRight = vec2(ceil(uv.x), floor(uv.y));\n vec2 bottomLeft = vec2(floor(uv.x), ceil(uv.y));\n vec2 bottomRight = ceil(uv);\n vec2 closestPoint = upperLeft;\n vec2 param_3 = upperLeft;\n float dist = distance(uv, FLT_flutter_local_fuzzGridPoint(param_3));\n vec2 param_4 = upperRight;\n float upperRightDistance = distance(uv, FLT_flutter_local_fuzzGridPoint(param_4));\n if (upperRightDistance < dist)\n {\n dist = upperRightDistance;\n closestPoint = upperRight;\n }\n vec2 param_5 = bottomLeft;\n float bottomLeftDistance = distance(uv, FLT_flutter_local_fuzzGridPoint(param_5));\n if (bottomLeftDistance < dist)\n {\n dist = bottomLeftDistance;\n closestPoint = bottomLeft;\n }\n vec2 param_6 = bottomRight;\n float bottomRightDistance = distance(uv, FLT_flutter_local_fuzzGridPoint(param_6));\n if (bottomRightDistance < dist)\n {\n dist = bottomRightDistance;\n closestPoint = bottomRight;\n }\n vec2 param_7 = closestPoint;\n fragColor = vec4(FLT_flutter_local_getColorForGridPoint(param_7), 1.0);\n}\n\nhalf4 main(float2 iFragCoord)\n{\n flutter_FragCoord = float4(iFragCoord, 0, 0);\n FLT_main();\n return fragColor;\n}\n", + "stage": 1, + "target_platform": 2, + "uniforms": [ + { + "array_elements": 0, + "bit_width": 32, + "columns": 1, + "location": 0, + "name": "uTileSize", + "rows": 1, + "type": 10 + } + ] +} +'''; + +Future testMain() async { + setUpUiTest(); + + const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300); + + test('fragment shader', () async { + fakeAssetManager.setAsset( + 'voronoi_shader', + Uint8List.fromList(utf8.encode(kVoronoiShaderSksl)).buffer.asByteData() + ); + final ui.FragmentProgram program = await renderer.createFragmentProgram('voronoi_shader'); + final ui.FragmentShader shader = program.fragmentShader(); + + Future drawCircle(String goldenFilename) async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder, region); + canvas.drawCircle(const ui.Offset(150, 150), 100, ui.Paint()..shader = shader); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile(goldenFilename, region: region); + } + + shader.setFloat(0, 10.0); + await drawCircle('fragment_shader_voronoi_tile10px.png'); + + // Make sure we can reuse the shader object with a new uniform value. + shader.setFloat(0, 25.0); + await drawCircle('fragment_shader_voronoi_tile25px.png'); + }, skip: isHtml); // Fragment shaders are not supported by the HTML renderer. +} diff --git a/lib/web_ui/test/ui/gradient_golden_test.dart b/lib/web_ui/test/ui/gradient_golden_test.dart new file mode 100644 index 0000000000000..96ba0f7d7fd20 --- /dev/null +++ b/lib/web_ui/test/ui/gradient_golden_test.dart @@ -0,0 +1,123 @@ +// 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:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/ui.dart'; +import 'package:web_engine_tester/golden_tester.dart'; + +import 'utils.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpUiTest(); + + const Rect region = Rect.fromLTWH(0, 0, 300, 300); + + group('Gradients', () { + test('Using a linear gradient on a paint', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawRect( + const Rect.fromLTRB(50, 50, 250, 250), + Paint() + ..shader = Gradient.linear( + const Offset(50, 50), + const Offset(250, 250), + [ + const Color(0xFFFF0000), + const Color(0xFF00FF00), + const Color(0xFF0000FF), + ], + [0.0, 0.5, 1.0], + ) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('linear_gradient_paint.png', region: region); + }); + + test('Using a radial gradient on a paint', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawRect( + const Rect.fromLTRB(50, 50, 250, 250), + Paint() + ..shader = Gradient.radial( + const Offset(150, 150), + 100, + [ + const Color(0xFFFF0000), + const Color(0xFF00FF00), + const Color(0xFF0000FF), + ], + [0.0, 0.5, 1.0], + ) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('radial_gradient_paint.png', region: region); + }); + + test('Using a conical gradient on a paint', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawRect( + const Rect.fromLTRB(50, 50, 250, 250), + Paint() + ..shader = Gradient.radial( + const Offset(200, 200), + 100, + [ + const Color(0xFFFF0000), + const Color(0xFF00FF00), + const Color(0xFF0000FF), + ], + [0.0, 0.5, 1.0], + TileMode.clamp, + null, + const Offset(50, 50), + 5, + ) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('conical_gradient_paint.png', region: region); + }); + + test('Using a sweep gradient on a paint', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawRect( + const Rect.fromLTRB(50, 50, 250, 250), + Paint() + ..shader = Gradient.sweep( + const Offset(150, 150), + [ + const Color(0xFFFF0000), + const Color(0xFF00FF00), + const Color(0xFF0000FF), + ], + [0.0, 0.5, 1.0], + TileMode.clamp, + math.pi / 3.0, + 4.0 * math.pi / 3.0, + ) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('sweep_gradient_paint.png', region: region); + }); + }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 +} diff --git a/lib/web_ui/test/ui/scene_builder_test.dart b/lib/web_ui/test/ui/scene_builder_test.dart index c9969072a3ca4..18bb6a4ab7076 100644 --- a/lib/web_ui/test/ui/scene_builder_test.dart +++ b/lib/web_ui/test/ui/scene_builder_test.dart @@ -142,6 +142,47 @@ Future testMain() async { await awaitNextFrame(); await matchGoldenFile('scene_builder_opacity_circles_on_square.png', region: region); }); + + test('shader mask layer', () async { + final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); + + sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { + final ui.Paint paint = ui.Paint()..color = const ui.Color(0xFFFF0000); + canvas.drawCircle( + const ui.Offset(125, 150), + 50, + paint + ); + canvas.drawCircle( + const ui.Offset(175, 150), + 50, + paint + ); + })); + + final ui.Shader shader = ui.Gradient.linear( + ui.Offset.zero, + const ui.Offset(50, 50), [ + const ui.Color(0xFFFFFFFF), + const ui.Color(0x00000000), + ]); + sceneBuilder.pushShaderMask( + shader, + const ui.Rect.fromLTRB(125, 125, 175, 175), + ui.BlendMode.srcATop + ); + + sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { + canvas.drawRect( + ui.Rect.fromCircle(center: const ui.Offset(150, 150), radius: 50), + ui.Paint()..color = const ui.Color(0xFF00FF00) + ); + })); + + await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); + await matchGoldenFile('scene_builder_shader_mask.png', region: region); + }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 }); } diff --git a/lib/web_ui/test/ui/utils.dart b/lib/web_ui/test/ui/utils.dart index a97935837f9c6..01b42125383bb 100644 --- a/lib/web_ui/test/ui/utils.dart +++ b/lib/web_ui/test/ui/utils.dart @@ -4,20 +4,53 @@ import 'dart:async'; import 'dart:js_interop'; +import 'dart:typed_data'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; 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'; +class FakeAssetManager implements AssetManager { + FakeAssetManager(this._parent); + + @override + String get assetsDir => 'assets'; + + @override + String getAssetUrl(String asset) => asset; + + @override + Future load(String assetKey) async { + final ByteData? data = _assetMap[assetKey]; + if (data == null) { + return _parent.load(assetKey); + } + return data; + } + + @override + Future loadAsset(String asset) { + return _parent.loadAsset(asset); + } + + void setAsset(String assetKey, ByteData assetData) { + _assetMap[assetKey] = assetData; + } + + final Map _assetMap = {}; + final AssetManager _parent; +} + +FakeAssetManager fakeAssetManager = FakeAssetManager(WebOnlyMockAssetManager()); + /// Initializes the renderer for this test. void setUpUiTest() { setUpAll(() async { debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); + await initializeEngine(assetManager: fakeAssetManager); await renderer.fontCollection.debugDownloadTestFonts(); renderer.fontCollection.registerDownloadedFonts(); - }); } From 1289f4b513ef6d9d385cc41cb896ff0b12ad9c79 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 13 Apr 2023 14:26:17 -0700 Subject: [PATCH 3/4] Fix skwasm's canvasDrawArc declaration and write tests. (#41165) Fix skwasm's canvasDrawArc declaration and write tests. --- .../skwasm/skwasm_impl/raw/raw_canvas.dart | 2 +- .../test/ui/canvas_curves_golden_test.dart | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 lib/web_ui/test/ui/canvas_curves_golden_test.dart 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 index 177d30feadfd5..276fc0dd02461 100644 --- 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 @@ -108,7 +108,7 @@ external void canvasDrawCircle( CanvasHandle canvas, double x, double y, double radius, PaintHandle paint); @Native( - symbol: 'canvas_drawCircle', isLeaf: true) + symbol: 'canvas_drawArc', isLeaf: true) external void canvasDrawArc( CanvasHandle canvas, RawRect rect, diff --git a/lib/web_ui/test/ui/canvas_curves_golden_test.dart b/lib/web_ui/test/ui/canvas_curves_golden_test.dart new file mode 100644 index 0000000000000..e067e9f944b8f --- /dev/null +++ b/lib/web_ui/test/ui/canvas_curves_golden_test.dart @@ -0,0 +1,74 @@ +// 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:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/ui.dart'; +import 'package:web_engine_tester/golden_tester.dart'; + +import 'utils.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpUiTest(); + + const Rect region = Rect.fromLTWH(0, 0, 300, 300); + + test('draw arc', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawArc( + const Rect.fromLTRB(100, 100, 200, 200), + math.pi / 3.0, + 4.0 * math.pi / 3.0, + false, + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3.0 + ..color = const Color(0xFFFF00FF) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('ui_canvas_draw_arc.png', region: region); + }); + + test('draw circle', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawCircle( + const Offset(150, 150), + 50, + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3.0 + ..color = const Color(0xFFFF0000) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('ui_canvas_draw_circle.png', region: region); + }); + + test('draw oval', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + canvas.drawOval( + const Rect.fromLTRB(100, 125, 200, 175), + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3.0 + ..color = const Color(0xFF00FFFF) + ); + + await drawPictureUsingCurrentRenderer(recorder.endRecording()); + + await matchGoldenFile('ui_canvas_draw_oval.png', region: region); + }); +} From ce7be0093399666e80f0431b45d5416bdd6ccf6a Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Thu, 13 Apr 2023 14:59:04 -0700 Subject: [PATCH 4/4] Revert "Add support for double tap action from Apple Pencil 2" (#41138) Reverts flutter/engine#39637 Since this feature https://github.com/flutter/flutter/issues/73172 has been scrapped for now, previous engine work should be reverted. Refer to [this design doc](https://docs.google.com/document/d/1r4P5r-jGt2Sjqro3ldCU2axUiHTpu3yhIycnI94OKQw/edit ) for more discussion and details. --- lib/ui/platform_dispatcher.dart | 3 +- lib/ui/pointer.dart | 30 +---- lib/ui/window/pointer_data.h | 13 +-- .../window/pointer_data_packet_converter.cc | 1 - ...pointer_data_packet_converter_unittests.cc | 6 - lib/web_ui/lib/pointer.dart | 23 +--- shell/common/input_events_unittests.cc | 2 - .../android/AndroidTouchProcessor.java | 22 +--- .../framework/Source/FlutterViewController.mm | 58 +--------- .../Source/FlutterViewControllerTest.mm | 106 +++++------------- shell/platform/embedder/embedder.cc | 2 - shell/platform/embedder/embedder.h | 1 - 12 files changed, 37 insertions(+), 230 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index a18bf52d24a27..6a63871e9ccd6 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -392,7 +392,7 @@ class PlatformDispatcher { // * pointer_data.cc // * pointer.dart // * AndroidTouchProcessor.java - static const int _kPointerDataFieldCount = 36; + static const int _kPointerDataFieldCount = 35; static PointerDataPacket _unpackPointerDataPacket(ByteData packet) { const int kStride = Int64List.bytesPerElement; @@ -438,7 +438,6 @@ class PlatformDispatcher { panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian), rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - preferredStylusAuxiliaryAction: PointerPreferredStylusAuxiliaryAction.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], )); assert(offset == (i + 1) * _kPointerDataFieldCount); } diff --git a/lib/ui/pointer.dart b/lib/ui/pointer.dart index 4585fbcbf0a87..608a4d49aded5 100644 --- a/lib/ui/pointer.dart +++ b/lib/ui/pointer.dart @@ -133,31 +133,10 @@ enum PointerSignalKind { /// A pointer-generated scale event (e.g. trackpad pinch). scale, - /// A stylus generated action (e.g. double tap on Apple Pencil 2) - stylusAuxiliaryAction, - /// An unknown pointer signal kind. unknown } - /// The preferred action for stylus action -enum PointerPreferredStylusAuxiliaryAction { - /// Ignore pointer input - ignore, - - /// Show colour palette if available - showColorPalette, - - /// Switch to eraser if available - switchEraser, - - /// Switch to previous tool - switchPrevious, - - /// unknown preferred action - unknown, -} - /// Information about the state of a pointer. class PointerData { /// Creates an object that represents the state of a pointer. @@ -197,7 +176,6 @@ class PointerData { this.panDeltaY = 0.0, this.scale = 0.0, this.rotation = 0.0, - this.preferredStylusAuxiliaryAction = PointerPreferredStylusAuxiliaryAction.ignore, }); /// Unique identifier that ties the [PointerEvent] to embedder event created it. @@ -396,11 +374,6 @@ class PointerData { /// The current angle of the pan/zoom in radians, with 0.0 as the initial angle. final double rotation; - /// For events with signal kind of stylusAuxiliaryAction - /// - /// The current preferred action for stylusAuxiliaryAction, with ignore as the default. - final PointerPreferredStylusAuxiliaryAction preferredStylusAuxiliaryAction; - @override String toString() => 'PointerData(x: $physicalX, y: $physicalY)'; @@ -440,8 +413,7 @@ class PointerData { 'panDeltaX: $panDeltaX, ' 'panDeltaY: $panDeltaY, ' 'scale: $scale, ' - 'rotation: $rotation, ' - 'preferredStylusAuxiliaryAction: $preferredStylusAuxiliaryAction' + 'rotation: $rotation' ')'; } } diff --git a/lib/ui/window/pointer_data.h b/lib/ui/window/pointer_data.h index 1dc159554ad74..1f323cc4f8169 100644 --- a/lib/ui/window/pointer_data.h +++ b/lib/ui/window/pointer_data.h @@ -11,7 +11,7 @@ namespace flutter { // If this value changes, update the pointer data unpacking code in // platform_dispatcher.dart. -static constexpr int kPointerDataFieldCount = 36; +static constexpr int kPointerDataFieldCount = 35; static constexpr int kBytesPerField = sizeof(int64_t); // Must match the button constants in events.dart. enum PointerButtonMouse : int64_t { @@ -63,16 +63,6 @@ struct alignas(8) PointerData { kScroll, kScrollInertiaCancel, kScale, - kStylusAuxiliaryAction, - }; - - // Must match the PreferredStylusAuxiliaryAction enum in pointer.dart. - enum class PreferredStylusAuxiliaryAction : int64_t { - kIgnore, - kShowColorPalette, - kSwitchEraser, - kSwitchPrevious, - kUnknown }; int64_t embedder_id; @@ -110,7 +100,6 @@ struct alignas(8) PointerData { double pan_delta_y; double scale; double rotation; - PreferredStylusAuxiliaryAction preferred_auxiliary_stylus_action; void Clear(); }; diff --git a/lib/ui/window/pointer_data_packet_converter.cc b/lib/ui/window/pointer_data_packet_converter.cc index d3b19f64bea05..6750a7da22761 100644 --- a/lib/ui/window/pointer_data_packet_converter.cc +++ b/lib/ui/window/pointer_data_packet_converter.cc @@ -293,7 +293,6 @@ void PointerDataPacketConverter::ConvertPointerData( switch (pointer_data.signal_kind) { case PointerData::SignalKind::kScroll: case PointerData::SignalKind::kScrollInertiaCancel: - case PointerData::SignalKind::kStylusAuxiliaryAction: case PointerData::SignalKind::kScale: { // Makes sure we have an existing pointer auto iter = states_.find(pointer_data.device); diff --git a/lib/ui/window/pointer_data_packet_converter_unittests.cc b/lib/ui/window/pointer_data_packet_converter_unittests.cc index 235f2f9a39c52..7c4c98162a3eb 100644 --- a/lib/ui/window/pointer_data_packet_converter_unittests.cc +++ b/lib/ui/window/pointer_data_packet_converter_unittests.cc @@ -45,8 +45,6 @@ void CreateSimulatedPointerData(PointerData& data, // NOLINT data.platformData = 0; data.scroll_delta_x = 0.0; data.scroll_delta_y = 0.0; - data.preferred_auxiliary_stylus_action = - PointerData::PreferredStylusAuxiliaryAction::kIgnore; } void CreateSimulatedMousePointerData(PointerData& data, // NOLINT @@ -86,8 +84,6 @@ void CreateSimulatedMousePointerData(PointerData& data, // NOLINT data.platformData = 0; data.scroll_delta_x = scroll_delta_x; data.scroll_delta_y = scroll_delta_y; - data.preferred_auxiliary_stylus_action = - PointerData::PreferredStylusAuxiliaryAction::kIgnore; } void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT @@ -133,8 +129,6 @@ void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT data.pan_delta_y = 0.0; data.scale = scale; data.rotation = rotation; - data.preferred_auxiliary_stylus_action = - PointerData::PreferredStylusAuxiliaryAction::kIgnore; } void UnpackPointerPacket(std::vector& output, // NOLINT diff --git a/lib/web_ui/lib/pointer.dart b/lib/web_ui/lib/pointer.dart index fd7981bc354a7..542f5c70c90c3 100644 --- a/lib/web_ui/lib/pointer.dart +++ b/lib/web_ui/lib/pointer.dart @@ -34,24 +34,6 @@ enum PointerSignalKind { unknown } - /// The preferred action for stylus action -enum PointerPreferredStylusAuxiliaryAction { - /// Ignore pointer input - ignore, - - /// Show colour palette if available - showColorPalette, - - /// Switch to eraser if available - switchEraser, - - /// Switch to previous tool - switchPrevious, - - /// unknown preferred action - unknown, -} - class PointerData { const PointerData({ this.embedderId = 0, @@ -89,7 +71,6 @@ class PointerData { this.panDeltaY = 0.0, this.scale = 0.0, this.rotation = 0.0, - this.preferredStylusAuxiliaryAction = PointerPreferredStylusAuxiliaryAction.ignore, }); final int embedderId; final Duration timeStamp; @@ -126,7 +107,6 @@ class PointerData { final double panDeltaY; final double scale; final double rotation; - final PointerPreferredStylusAuxiliaryAction preferredStylusAuxiliaryAction; @override String toString() => 'PointerData(x: $physicalX, y: $physicalY)'; @@ -165,8 +145,7 @@ class PointerData { 'panDeltaX: $panDeltaX, ' 'panDeltaY: $panDeltaY, ' 'scale: $scale, ' - 'rotation: $rotation, ' - 'preferredStylusAuxiliaryAction: $preferredStylusAuxiliaryAction' + 'rotation: $rotation' ')'; } } diff --git a/shell/common/input_events_unittests.cc b/shell/common/input_events_unittests.cc index d9d1e52618788..3f117c006c65f 100644 --- a/shell/common/input_events_unittests.cc +++ b/shell/common/input_events_unittests.cc @@ -176,8 +176,6 @@ void CreateSimulatedPointerData(PointerData& data, data.platformData = 0; data.scroll_delta_x = 0.0; data.scroll_delta_y = 0.0; - data.preferred_auxiliary_stylus_action = - PointerData::PreferredStylusAuxiliaryAction::kIgnore; } TEST_F(ShellTest, MissAtMostOneFrameForIrregularInputEvents) { diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java index ea36686e7c8b7..b3db5df1eb99b 100644 --- a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java +++ b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java @@ -66,7 +66,6 @@ public class AndroidTouchProcessor { PointerSignalKind.SCROLL, PointerSignalKind.SCROLL_INERTIA_CANCEL, PointerSignalKind.SCALE, - PointerSignalKind.STYLUS_AUXILIARY_ACTION, PointerSignalKind.UNKNOWN }) public @interface PointerSignalKind { @@ -74,28 +73,11 @@ public class AndroidTouchProcessor { int SCROLL = 1; int SCROLL_INERTIA_CANCEL = 2; int SCALE = 3; - int STYLUS_AUXILIARY_ACTION = 4; - int UNKNOWN = 5; - } - - // Must match the PointerPreferredStylusAuxiliaryAction enum in pointer.dart. - @IntDef({ - PointerPreferredStylusAuxiliaryAction.IGNORE, - PointerPreferredStylusAuxiliaryAction.SHOW_COLOR_PALETTE, - PointerPreferredStylusAuxiliaryAction.SWITCH_ERASER, - PointerPreferredStylusAuxiliaryAction.SWITCH_PREVIOUS, - PointerPreferredStylusAuxiliaryAction.UNKNOWN - }) - public @interface PointerPreferredStylusAuxiliaryAction { - int IGNORE = 0; - int SHOW_COLOR_PALETTE = 1; - int SWITCH_ERASER = 2; - int SWITCH_PREVIOUS = 3; int UNKNOWN = 4; } // Must match the unpacking code in hooks.dart. - private static final int POINTER_DATA_FIELD_COUNT = 36; + private static final int POINTER_DATA_FIELD_COUNT = 35; @VisibleForTesting static final int BYTES_PER_FIELD = 8; // This value must match the value in framework's platform_view.dart. @@ -373,8 +355,6 @@ private void addPointerForIndex( packet.putDouble(1.0); // scale packet.putDouble(0.0); // rotation - packet.putLong(PointerPreferredStylusAuxiliaryAction.IGNORE); // preferred stylus action - if (isTrackpadPan && getPointerChangeForPanZoom(pointerChange) == PointerChange.PAN_ZOOM_END) { ongoingPans.remove(event.getPointerId(pointerIndex)); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index fcf9989762a5e..5f628675f87e8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -56,9 +56,7 @@ // This is left a FlutterBinaryMessenger privately for now to give people a chance to notice the // change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are // just a warning. -@interface FlutterViewController () +@interface FlutterViewController () @property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI; @property(nonatomic, assign) BOOL isHomeIndicatorHidden; @property(nonatomic, assign) BOOL isPresentingViewControllerAnimating; @@ -99,7 +97,7 @@ @interface FlutterViewController () (1); - packet->SetPointerData(/*index=*/0, pointer_data); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; -} - -- (flutter::PointerData)createAuxillaryStylusActionData API_AVAILABLE(ios(13.4)) { - flutter::PointerData pointer_data; - pointer_data.Clear(); - - switch (UIPencilInteraction.preferredTapAction) { - case UIPencilPreferredActionIgnore: - pointer_data.preferred_auxiliary_stylus_action = - flutter::PointerData::PreferredStylusAuxiliaryAction::kIgnore; - break; - case UIPencilPreferredActionShowColorPalette: - pointer_data.preferred_auxiliary_stylus_action = - flutter::PointerData::PreferredStylusAuxiliaryAction::kShowColorPalette; - break; - case UIPencilPreferredActionSwitchEraser: - pointer_data.preferred_auxiliary_stylus_action = - flutter::PointerData::PreferredStylusAuxiliaryAction::kSwitchEraser; - break; - case UIPencilPreferredActionSwitchPrevious: - pointer_data.preferred_auxiliary_stylus_action = - flutter::PointerData::PreferredStylusAuxiliaryAction::kSwitchPrevious; - break; - default: - pointer_data.preferred_auxiliary_stylus_action = - flutter::PointerData::PreferredStylusAuxiliaryAction::kUnknown; - break; - } - - pointer_data.time_stamp = [[NSProcessInfo processInfo] systemUptime] * kMicrosecondsPerSecond; - pointer_data.kind = flutter::PointerData::DeviceKind::kStylus; - pointer_data.signal_kind = flutter::PointerData::SignalKind::kStylusAuxiliaryAction; - - return pointer_data; -} - #pragma mark - Handle view resizing - (void)updateViewportMetrics { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 4a5d6fd6176a9..594a264ddbe6c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -112,8 +112,7 @@ @interface FlutterEmbedderKeyResponder (Tests) @property(nonatomic, copy, readonly) FlutterSendKeyEvent sendEvent; @end -@interface FlutterViewController (Tests) -; +@interface FlutterViewController (Tests) @property(nonatomic, assign) double targetViewInsetBottom; @property(nonatomic, assign) BOOL isKeyboardInOrTransitioningFromBackground; @@ -125,7 +124,6 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences; - (void)handlePressEvent:(FlutterUIPressProxy*)press nextAction:(void (^)())next API_AVAILABLE(ios(13.4)); - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer; -- (flutter::PointerData)createAuxillaryStylusActionData; - (void)updateViewportMetrics; - (void)onUserSettingsChanged:(NSNotification*)notification; - (void)applicationWillTerminate:(NSNotification*)notification; @@ -1483,6 +1481,11 @@ - (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback } - (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) @@ -1513,6 +1516,12 @@ - (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) { } - (void)testValidKeyDownEvent API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) @@ -1544,6 +1553,11 @@ - (void)testValidKeyDownEvent API_AVAILABLE(ios(13.4)) { } - (void)testIgnoredKeyEvents API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } id keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) .andCall(self, @selector(sendMessage:reply:)); @@ -1577,6 +1591,12 @@ - (void)testIgnoredKeyEvents API_AVAILABLE(ios(13.4)) { } - (void)testPanGestureRecognizer API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; @@ -1597,6 +1617,12 @@ - (void)testPanGestureRecognizer API_AVAILABLE(ios(13.4)) { } - (void)testMouseSupport API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; @@ -1611,80 +1637,6 @@ - (void)testMouseSupport API_AVAILABLE(ios(13.4)) { dispatchPointerDataPacket:std::make_unique(0)]; } -- (void)testPencilSupport API_AVAILABLE(ios(13.4)) { - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - XCTAssertNotNil(vc); - - id mockPencilInteraction = OCMClassMock([UIPencilInteraction class]); - - OCMStub([mockPencilInteraction preferredTapAction]) - .andReturn(UIPencilPreferredActionShowColorPalette); - - // Check that the helper function is being called - FlutterViewController* viewControllerMock = OCMPartialMock(vc); - [viewControllerMock pencilInteractionDidTap:mockPencilInteraction]; - OCMVerify([viewControllerMock createAuxillaryStylusActionData]); - - [mockPencilInteraction stopMocking]; -} - -- (void)testCreateAuxillaryStylusActionData API_AVAILABLE(ios(13.4)) { - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - XCTAssertNotNil(vc); - - id mockPencilInteraction = OCMClassMock([UIPencilInteraction class]); - - OCMExpect([mockPencilInteraction preferredTapAction]) - .andReturn(UIPencilPreferredActionShowColorPalette); - - // Check the return value of the helper function - flutter::PointerData pointer_data = [vc createAuxillaryStylusActionData]; - - XCTAssertEqual(pointer_data.kind, flutter::PointerData::DeviceKind::kStylus); - XCTAssertEqual(pointer_data.signal_kind, - flutter::PointerData::SignalKind::kStylusAuxiliaryAction); - XCTAssertEqual(pointer_data.preferred_auxiliary_stylus_action, - flutter::PointerData::PreferredStylusAuxiliaryAction::kShowColorPalette); - - OCMExpect([mockPencilInteraction preferredTapAction]) - .andReturn(UIPencilPreferredActionSwitchEraser); - - pointer_data = [vc createAuxillaryStylusActionData]; - - XCTAssertEqual(pointer_data.kind, flutter::PointerData::DeviceKind::kStylus); - XCTAssertEqual(pointer_data.signal_kind, - flutter::PointerData::SignalKind::kStylusAuxiliaryAction); - XCTAssertEqual(pointer_data.preferred_auxiliary_stylus_action, - flutter::PointerData::PreferredStylusAuxiliaryAction::kSwitchEraser); - - OCMExpect([mockPencilInteraction preferredTapAction]) - .andReturn(UIPencilPreferredActionSwitchPrevious); - - pointer_data = [vc createAuxillaryStylusActionData]; - - XCTAssertEqual(pointer_data.kind, flutter::PointerData::DeviceKind::kStylus); - XCTAssertEqual(pointer_data.signal_kind, - flutter::PointerData::SignalKind::kStylusAuxiliaryAction); - XCTAssertEqual(pointer_data.preferred_auxiliary_stylus_action, - flutter::PointerData::PreferredStylusAuxiliaryAction::kSwitchPrevious); - - OCMExpect([mockPencilInteraction preferredTapAction]).andReturn(UIPencilPreferredActionIgnore); - - pointer_data = [vc createAuxillaryStylusActionData]; - - XCTAssertEqual(pointer_data.kind, flutter::PointerData::DeviceKind::kStylus); - XCTAssertEqual(pointer_data.signal_kind, - flutter::PointerData::SignalKind::kStylusAuxiliaryAction); - XCTAssertEqual(pointer_data.preferred_auxiliary_stylus_action, - flutter::PointerData::PreferredStylusAuxiliaryAction::kIgnore); - - [mockPencilInteraction stopMocking]; -} - - (void)testFakeEventTimeStamp { FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index e1c74cf041d08..6e59644216064 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2152,8 +2152,6 @@ inline flutter::PointerData::SignalKind ToPointerDataSignalKind( return flutter::PointerData::SignalKind::kScrollInertiaCancel; case kFlutterPointerSignalKindScale: return flutter::PointerData::SignalKind::kScale; - case kFlutterPointerSignalKindStylusAuxiliaryAction: - return flutter::PointerData::SignalKind::kStylusAuxiliaryAction; } return flutter::PointerData::SignalKind::kNone; } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 5cdba06ef505d..1710299f6555d 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -896,7 +896,6 @@ typedef enum { kFlutterPointerSignalKindScroll, kFlutterPointerSignalKindScrollInertiaCancel, kFlutterPointerSignalKindScale, - kFlutterPointerSignalKindStylusAuxiliaryAction, } FlutterPointerSignalKind; typedef struct {