From e5cd3857ed67ea54ac8e78ba0f1c044f66930fc2 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 31 Aug 2023 15:43:17 -0700 Subject: [PATCH 1/3] Revert "Revert dl split (#45326)" This reverts commit 7a2eaf42dbaed57406eb9be4f8fdbfaa079c3d71. --- ci/licenses_golden/excluded_files | 3 +- ci/licenses_golden/licenses_flutter | 32 +- display_list/BUILD.gn | 11 +- display_list/benchmarking/dl_benchmarks.cc | 2 +- display_list/benchmarking/dl_complexity_gl.cc | 8 +- display_list/benchmarking/dl_complexity_gl.h | 8 +- .../benchmarking/dl_complexity_helper.h | 4 +- .../benchmarking/dl_complexity_metal.cc | 8 +- .../benchmarking/dl_complexity_metal.h | 8 +- .../benchmarking/dl_complexity_unittests.cc | 4 +- display_list/display_list.cc | 89 +- display_list/display_list.h | 74 +- display_list/display_list_builder.cc | 34 + display_list/display_list_builder.h | 59 + display_list/display_list_unittests.cc | 1519 +++++++--------- display_list/dl_builder.cc | 1619 ----------------- display_list/dl_builder.h | 780 -------- display_list/dl_canvas.h | 14 +- display_list/dl_canvas_to_receiver.cc | 1158 ++++++++++++ display_list/dl_canvas_to_receiver.h | 573 ++++++ display_list/dl_op_flags.h | 35 +- display_list/dl_op_receiver.h | 20 +- display_list/dl_op_recorder.cc | 691 +++++++ display_list/dl_op_recorder.h | 210 +++ display_list/dl_op_records.h | 92 +- {shell/common => display_list}/dl_op_spy.cc | 14 +- {shell/common => display_list}/dl_op_spy.h | 17 +- .../dl_op_spy_unittests.cc | 4 +- display_list/dl_paint.h | 4 +- display_list/dl_vertices.h | 2 +- display_list/dl_vertices_unittests.cc | 2 +- display_list/effects/dl_color_source.h | 8 +- display_list/effects/dl_path_effect.h | 2 +- display_list/geometry/dl_rtree.h | 2 +- display_list/skia/dl_sk_canvas.cc | 7 + display_list/skia/dl_sk_canvas.h | 3 + display_list/skia/dl_sk_dispatcher.cc | 12 +- display_list/skia/dl_sk_dispatcher.h | 12 +- .../testing/dl_rendering_unittests.cc | 2 +- display_list/testing/dl_test_snippets.cc | 52 +- display_list/testing/dl_test_snippets.h | 2 +- display_list/utils/dl_bounds_accumulator.cc | 7 +- display_list/utils/dl_bounds_accumulator.h | 29 +- display_list/utils/dl_matrix_clip_tracker.cc | 2 +- display_list/utils/dl_receiver_utils.h | 12 +- flow/BUILD.gn | 7 + flow/embedded_views.cc | 46 +- flow/embedded_views.h | 30 +- flow/layers/aiks_layer.cc | 64 + flow/layers/aiks_layer.h | 44 + flow/layers/aiks_layer_unittests.cc | 49 + .../checkerboard_layertree_unittests.cc | 15 +- flow/layers/display_list_layer.cc | 1 - flow/layers/display_list_layer_unittests.cc | 2 +- flow/layers/image_filter_layer_unittests.cc | 2 + flow/layers/layer.h | 2 + flow/layers/layer_tree.cc | 63 + flow/layers/layer_tree.h | 4 + flow/raster_cache.cc | 6 +- flow/raster_cache.h | 8 +- flow/raster_cache_unittests.cc | 2 +- flow/surface_frame.cc | 22 +- flow/surface_frame.h | 14 +- flow/surface_frame_unittests.cc | 2 + flow/testing/diff_context_test.cc | 3 +- flow/testing/layer_test.h | 52 +- flow/testing/mock_texture_unittests.cc | 2 +- impeller/aiks/BUILD.gn | 1 + impeller/aiks/canvas.cc | 19 +- impeller/aiks/canvas.h | 4 +- impeller/aiks/picture.h | 2 + impeller/display_list/BUILD.gn | 2 + impeller/display_list/dl_aiks_canvas.cc | 1204 ++++++++++++ impeller/display_list/dl_aiks_canvas.h | 242 +++ impeller/display_list/dl_dispatcher.cc | 12 +- impeller/display_list/dl_dispatcher.h | 12 +- impeller/display_list/dl_playground.h | 2 +- impeller/display_list/dl_unittests.cc | 2 +- impeller/display_list/skia_conversions.h | 27 + impeller/entity/entity_pass.cc | 3 + impeller/entity/entity_pass.h | 1 - impeller/entity/geometry/geometry.cc | 9 +- impeller/entity/geometry/geometry.h | 5 +- .../entity/geometry/point_field_geometry.cc | 4 +- .../entity/geometry/point_field_geometry.h | 4 +- impeller/geometry/rect.h | 5 + impeller/geometry/sigma.h | 6 +- lib/ui/compositing/scene.cc | 7 +- lib/ui/compositing/scene_builder.cc | 10 + lib/ui/painting/canvas.cc | 197 +- lib/ui/painting/canvas.h | 6 +- ...isplay_list_deferred_image_gpu_impeller.cc | 143 +- ...display_list_deferred_image_gpu_impeller.h | 32 +- lib/ui/painting/paint.cc | 9 +- lib/ui/painting/picture.cc | 164 +- lib/ui/painting/picture.h | 69 +- lib/ui/painting/picture_recorder.cc | 33 +- lib/ui/painting/picture_recorder.h | 17 +- lib/ui/snapshot_delegate.h | 4 + lib/ui/text/paragraph.cc | 6 +- shell/common/BUILD.gn | 3 - shell/common/rasterizer.cc | 6 + shell/common/rasterizer.h | 5 + shell/common/snapshot_controller.h | 4 + shell/common/snapshot_controller_impeller.cc | 25 +- shell/common/snapshot_controller_impeller.h | 7 + shell/common/snapshot_controller_skia.cc | 7 + shell/common/snapshot_controller_skia.h | 4 + shell/gpu/gpu_surface_gl_impeller.cc | 24 +- shell/gpu/gpu_surface_metal_impeller.mm | 33 +- shell/gpu/gpu_surface_vulkan_impeller.cc | 19 +- .../external_view_embedder.cc | 12 +- .../external_view_embedder.h | 5 +- .../external_view_embedder_unittests.cc | 41 +- .../platform/android/platform_view_android.cc | 3 +- .../ios/framework/Source/FlutterEngine.mm | 2 +- .../framework/Source/FlutterPlatformViews.mm | 6 +- .../Source/FlutterPlatformViewsTest.mm | 93 +- .../Source/FlutterPlatformViews_Internal.h | 4 +- .../Source/FlutterPlatformViews_Internal.mm | 5 +- .../Source/accessibility_bridge_test.mm | 15 +- shell/platform/embedder/embedder.cc | 2 +- .../embedder/embedder_external_view.cc | 50 +- .../embedder/embedder_external_view.h | 8 +- .../embedder_external_view_embedder.cc | 11 +- .../embedder_external_view_embedder.h | 4 +- testing/display_list_testing.cc | 12 +- testing/display_list_testing.h | 12 +- testing/mock_canvas.cc | 18 + testing/mock_canvas.h | 17 +- third_party/txt/src/skia/paragraph_skia.cc | 38 +- third_party/txt/src/skia/paragraph_skia.h | 2 +- third_party/txt/src/txt/paragraph.h | 8 +- third_party/txt/tests/paragraph_unittests.cc | 1 + 134 files changed, 6440 insertions(+), 4019 deletions(-) create mode 100644 display_list/display_list_builder.cc create mode 100644 display_list/display_list_builder.h delete mode 100644 display_list/dl_builder.cc delete mode 100644 display_list/dl_builder.h create mode 100644 display_list/dl_canvas_to_receiver.cc create mode 100644 display_list/dl_canvas_to_receiver.h create mode 100644 display_list/dl_op_recorder.cc create mode 100644 display_list/dl_op_recorder.h rename {shell/common => display_list}/dl_op_spy.cc (91%) rename {shell/common => display_list}/dl_op_spy.h (90%) rename {shell/common => display_list}/dl_op_spy_unittests.cc (99%) create mode 100644 flow/layers/aiks_layer.cc create mode 100644 flow/layers/aiks_layer.h create mode 100644 flow/layers/aiks_layer_unittests.cc create mode 100644 impeller/display_list/dl_aiks_canvas.cc create mode 100644 impeller/display_list/dl_aiks_canvas.h diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 07cfad821a1b2..75b5fd6f47b9e 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -32,6 +32,7 @@ ../../../flutter/display_list/benchmarking/dl_complexity_unittests.cc ../../../flutter/display_list/display_list_unittests.cc ../../../flutter/display_list/dl_color_unittests.cc +../../../flutter/display_list/dl_op_spy_unittests.cc ../../../flutter/display_list/dl_paint_unittests.cc ../../../flutter/display_list/dl_vertices_unittests.cc ../../../flutter/display_list/effects/dl_color_filter_unittests.cc @@ -53,6 +54,7 @@ ../../../flutter/flow/flow_run_all_unittests.cc ../../../flutter/flow/frame_timings_recorder_unittests.cc ../../../flutter/flow/gl_context_switch_unittests.cc +../../../flutter/flow/layers/aiks_layer_unittests.cc ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc ../../../flutter/flow/layers/checkerboard_layertree_unittests.cc ../../../flutter/flow/layers/clip_path_layer_unittests.cc @@ -222,7 +224,6 @@ ../../../flutter/runtime/type_conversions_unittests.cc ../../../flutter/shell/common/animator_unittests.cc ../../../flutter/shell/common/context_options_unittests.cc -../../../flutter/shell/common/dl_op_spy_unittests.cc ../../../flutter/shell/common/engine_unittests.cc ../../../flutter/shell/common/fixtures ../../../flutter/shell/common/input_events_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f70d45e2c6632..89f591e6cc19e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -717,20 +717,26 @@ ORIGIN: ../../../flutter/display_list/benchmarking/dl_complexity_metal.h + ../.. ORIGIN: ../../../flutter/display_list/benchmarking/dl_region_benchmarks.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/display_list.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/display_list.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/display_list_builder.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/display_list_builder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_attributes.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_blend_mode.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_blend_mode.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/dl_builder.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/dl_builder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_canvas.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_canvas.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_canvas_to_receiver.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_canvas_to_receiver.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_color.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_flags.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_flags.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_receiver.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_receiver.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_op_recorder.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_op_recorder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_records.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_records.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_op_spy.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_op_spy.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_paint.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_paint.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_sampling_options.h + ../../../flutter/LICENSE @@ -785,6 +791,8 @@ ORIGIN: ../../../flutter/flow/frame_timings.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/frame_timings.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/layer_snapshot_store.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/layer_snapshot_store.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/layers/aiks_layer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/layers/aiks_layer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/layers/backdrop_filter_layer.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/layers/backdrop_filter_layer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/layers/cacheable_layer.cc + ../../../flutter/LICENSE @@ -1163,6 +1171,8 @@ ORIGIN: ../../../flutter/impeller/core/texture_descriptor.cc + ../../../flutter/ ORIGIN: ../../../flutter/impeller/core/texture_descriptor.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/vertex_buffer.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/core/vertex_buffer.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/display_list/dl_aiks_canvas.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/display_list/dl_aiks_canvas.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/display_list/dl_dispatcher.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/display_list/dl_dispatcher.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/display_list/dl_image_impeller.cc + ../../../flutter/LICENSE @@ -2243,8 +2253,6 @@ ORIGIN: ../../../flutter/shell/common/display.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display_manager.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/display_manager.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/common/dl_op_spy.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/common/dl_op_spy.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/engine.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/engine.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/pipeline.cc + ../../../flutter/LICENSE @@ -3463,20 +3471,26 @@ FILE: ../../../flutter/display_list/benchmarking/dl_complexity_metal.h FILE: ../../../flutter/display_list/benchmarking/dl_region_benchmarks.cc FILE: ../../../flutter/display_list/display_list.cc FILE: ../../../flutter/display_list/display_list.h +FILE: ../../../flutter/display_list/display_list_builder.cc +FILE: ../../../flutter/display_list/display_list_builder.h FILE: ../../../flutter/display_list/dl_attributes.h FILE: ../../../flutter/display_list/dl_blend_mode.cc FILE: ../../../flutter/display_list/dl_blend_mode.h -FILE: ../../../flutter/display_list/dl_builder.cc -FILE: ../../../flutter/display_list/dl_builder.h FILE: ../../../flutter/display_list/dl_canvas.cc FILE: ../../../flutter/display_list/dl_canvas.h +FILE: ../../../flutter/display_list/dl_canvas_to_receiver.cc +FILE: ../../../flutter/display_list/dl_canvas_to_receiver.h FILE: ../../../flutter/display_list/dl_color.h FILE: ../../../flutter/display_list/dl_op_flags.cc FILE: ../../../flutter/display_list/dl_op_flags.h FILE: ../../../flutter/display_list/dl_op_receiver.cc FILE: ../../../flutter/display_list/dl_op_receiver.h +FILE: ../../../flutter/display_list/dl_op_recorder.cc +FILE: ../../../flutter/display_list/dl_op_recorder.h FILE: ../../../flutter/display_list/dl_op_records.cc FILE: ../../../flutter/display_list/dl_op_records.h +FILE: ../../../flutter/display_list/dl_op_spy.cc +FILE: ../../../flutter/display_list/dl_op_spy.h FILE: ../../../flutter/display_list/dl_paint.cc FILE: ../../../flutter/display_list/dl_paint.h FILE: ../../../flutter/display_list/dl_sampling_options.h @@ -3531,6 +3545,8 @@ FILE: ../../../flutter/flow/frame_timings.cc FILE: ../../../flutter/flow/frame_timings.h FILE: ../../../flutter/flow/layer_snapshot_store.cc FILE: ../../../flutter/flow/layer_snapshot_store.h +FILE: ../../../flutter/flow/layers/aiks_layer.cc +FILE: ../../../flutter/flow/layers/aiks_layer.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h FILE: ../../../flutter/flow/layers/cacheable_layer.cc @@ -3909,6 +3925,8 @@ FILE: ../../../flutter/impeller/core/texture_descriptor.cc FILE: ../../../flutter/impeller/core/texture_descriptor.h FILE: ../../../flutter/impeller/core/vertex_buffer.cc FILE: ../../../flutter/impeller/core/vertex_buffer.h +FILE: ../../../flutter/impeller/display_list/dl_aiks_canvas.cc +FILE: ../../../flutter/impeller/display_list/dl_aiks_canvas.h FILE: ../../../flutter/impeller/display_list/dl_dispatcher.cc FILE: ../../../flutter/impeller/display_list/dl_dispatcher.h FILE: ../../../flutter/impeller/display_list/dl_image_impeller.cc @@ -4994,8 +5012,6 @@ FILE: ../../../flutter/shell/common/display.cc FILE: ../../../flutter/shell/common/display.h FILE: ../../../flutter/shell/common/display_manager.cc FILE: ../../../flutter/shell/common/display_manager.h -FILE: ../../../flutter/shell/common/dl_op_spy.cc -FILE: ../../../flutter/shell/common/dl_op_spy.h FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/pipeline.cc diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 97c9683511b61..d4b21754a8fb0 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -25,20 +25,26 @@ source_set("display_list") { "benchmarking/dl_complexity_metal.h", "display_list.cc", "display_list.h", + "display_list_builder.cc", + "display_list_builder.h", "dl_attributes.h", "dl_blend_mode.cc", "dl_blend_mode.h", - "dl_builder.cc", - "dl_builder.h", "dl_canvas.cc", "dl_canvas.h", + "dl_canvas_to_receiver.cc", + "dl_canvas_to_receiver.h", "dl_color.h", "dl_op_flags.cc", "dl_op_flags.h", "dl_op_receiver.cc", "dl_op_receiver.h", + "dl_op_recorder.cc", + "dl_op_recorder.h", "dl_op_records.cc", "dl_op_records.h", + "dl_op_spy.cc", + "dl_op_spy.h", "dl_paint.cc", "dl_paint.h", "dl_sampling_options.h", @@ -107,6 +113,7 @@ if (enable_unittests) { "benchmarking/dl_complexity_unittests.cc", "display_list_unittests.cc", "dl_color_unittests.cc", + "dl_op_spy_unittests.cc", "dl_paint_unittests.cc", "dl_vertices_unittests.cc", "effects/dl_color_filter_unittests.cc", diff --git a/display_list/benchmarking/dl_benchmarks.cc b/display_list/benchmarking/dl_benchmarks.cc index b2dcf78a2c3bc..07458b5a96474 100644 --- a/display_list/benchmarking/dl_benchmarks.cc +++ b/display_list/benchmarking/dl_benchmarks.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/display_list/benchmarking/dl_benchmarks.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_op_flags.h" #include "flutter/display_list/skia/dl_sk_canvas.h" diff --git a/display_list/benchmarking/dl_complexity_gl.cc b/display_list/benchmarking/dl_complexity_gl.cc index f8dd1c52a1260..c3738d0dc5cd5 100644 --- a/display_list/benchmarking/dl_complexity_gl.cc +++ b/display_list/benchmarking/dl_complexity_gl.cc @@ -503,7 +503,7 @@ void DisplayListGLComplexityCalculator::GLHelper::drawVertices( } void DisplayListGLComplexityCalculator::GLHelper::drawImage( - const sk_sp image, + const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { @@ -583,7 +583,7 @@ void DisplayListGLComplexityCalculator::GLHelper::ImageRect( } void DisplayListGLComplexityCalculator::GLHelper::drawImageNine( - const sk_sp image, + const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -608,7 +608,7 @@ void DisplayListGLComplexityCalculator::GLHelper::drawImageNine( } void DisplayListGLComplexityCalculator::GLHelper::drawDisplayList( - const sk_sp display_list, + const sk_sp& display_list, SkScalar opacity) { if (IsComplex()) { return; @@ -622,7 +622,7 @@ void DisplayListGLComplexityCalculator::GLHelper::drawDisplayList( } void DisplayListGLComplexityCalculator::GLHelper::drawTextBlob( - const sk_sp blob, + const sk_sp& blob, SkScalar x, SkScalar y) { if (IsComplex()) { diff --git a/display_list/benchmarking/dl_complexity_gl.h b/display_list/benchmarking/dl_complexity_gl.h index 9fc7596687051..f08357c0d79a7 100644 --- a/display_list/benchmarking/dl_complexity_gl.h +++ b/display_list/benchmarking/dl_complexity_gl.h @@ -56,18 +56,18 @@ class DisplayListGLComplexityCalculator uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/benchmarking/dl_complexity_helper.h b/display_list/benchmarking/dl_complexity_helper.h index 759d6e64460ed..464b4675caeeb 100644 --- a/display_list/benchmarking/dl_complexity_helper.h +++ b/display_list/benchmarking/dl_complexity_helper.h @@ -146,7 +146,7 @@ class ComplexityCalculatorHelper } void drawImageRect( - const sk_sp image, + const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -159,7 +159,7 @@ class ComplexityCalculatorHelper render_with_attributes, constraint == SrcRectConstraint::kStrict); } - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], diff --git a/display_list/benchmarking/dl_complexity_metal.cc b/display_list/benchmarking/dl_complexity_metal.cc index 56d4f3b406ad9..dcf6e9fcdf870 100644 --- a/display_list/benchmarking/dl_complexity_metal.cc +++ b/display_list/benchmarking/dl_complexity_metal.cc @@ -455,7 +455,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawVertices( } void DisplayListMetalComplexityCalculator::MetalHelper::drawImage( - const sk_sp image, + const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { @@ -532,7 +532,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::ImageRect( } void DisplayListMetalComplexityCalculator::MetalHelper::drawImageNine( - const sk_sp image, + const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -552,7 +552,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawImageNine( } void DisplayListMetalComplexityCalculator::MetalHelper::drawDisplayList( - const sk_sp display_list, + const sk_sp& display_list, SkScalar opacity) { if (IsComplex()) { return; @@ -566,7 +566,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawDisplayList( } void DisplayListMetalComplexityCalculator::MetalHelper::drawTextBlob( - const sk_sp blob, + const sk_sp& blob, SkScalar x, SkScalar y) { if (IsComplex()) { diff --git a/display_list/benchmarking/dl_complexity_metal.h b/display_list/benchmarking/dl_complexity_metal.h index aa63863fa4d05..16d58aa2b4bff 100644 --- a/display_list/benchmarking/dl_complexity_metal.h +++ b/display_list/benchmarking/dl_complexity_metal.h @@ -56,18 +56,18 @@ class DisplayListMetalComplexityCalculator uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/benchmarking/dl_complexity_unittests.cc b/display_list/benchmarking/dl_complexity_unittests.cc index eee77ac9bdf55..d349d1ce5afe6 100644 --- a/display_list/benchmarking/dl_complexity_unittests.cc +++ b/display_list/benchmarking/dl_complexity_unittests.cc @@ -6,7 +6,7 @@ #include "flutter/display_list/benchmarking/dl_complexity_gl.h" #include "flutter/display_list/benchmarking/dl_complexity_metal.h" #include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/testing/dl_test_snippets.h" #include "flutter/testing/testing.h" @@ -102,7 +102,7 @@ TEST(DisplayListComplexity, StrokeWidth) { auto display_list_stroke_0 = builder_stroke_0.Build(); DisplayListBuilder builder_stroke_1; - builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), + builder_stroke_1.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), DlPaint().setStrokeWidth(1.0f)); auto display_list_stroke_1 = builder_stroke_1.Build(); diff --git a/display_list/display_list.cc b/display_list/display_list.cc index e06bcf246d3d4..cee978dd56536 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -15,7 +15,7 @@ const SaveLayerOptions SaveLayerOptions::kWithAttributes = kNoAttributes.with_renders_with_attributes(); DisplayList::DisplayList() - : byte_count_(0), + : storage_(), op_count_(0), nested_byte_count_(0), nested_op_count_(0), @@ -25,8 +25,7 @@ DisplayList::DisplayList() is_ui_thread_safe_(true), modifies_transparent_black_(false) {} -DisplayList::DisplayList(DisplayListStorage&& storage, - size_t byte_count, +DisplayList::DisplayList(DlStorage&& storage, unsigned int op_count, size_t nested_byte_count, unsigned int nested_op_count, @@ -34,9 +33,8 @@ DisplayList::DisplayList(DisplayListStorage&& storage, bool can_apply_group_opacity, bool is_ui_thread_safe, bool modifies_transparent_black, - sk_sp rtree) + std::shared_ptr rtree) : storage_(std::move(storage)), - byte_count_(byte_count), op_count_(op_count), nested_byte_count_(nested_byte_count), nested_op_count_(nested_op_count), @@ -47,9 +45,65 @@ DisplayList::DisplayList(DisplayListStorage&& storage, modifies_transparent_black_(modifies_transparent_black), rtree_(std::move(rtree)) {} -DisplayList::~DisplayList() { - uint8_t* ptr = storage_.get(); - DisposeOps(ptr, ptr + byte_count_); +DisplayList::DlStorage::DlStorage(DisplayList::DlStorage&& source) + : ptr_(std::move(source.ptr_)), + used_(source.used_), + allocated_(source.allocated_) { + FML_DCHECK(source.ptr_.get() == nullptr); + FML_DCHECK(!source.disabled_); + source.ptr_ = nullptr; + source.used_ = 0u; + source.allocated_ = 0u; + source.disabled_ = true; +} + +DisplayList::DlStorage::~DlStorage() { + uint8_t* ptr = get(); + if (ptr != nullptr) { + FML_DCHECK(used_ <= allocated_); + DisposeOps(ptr, ptr + used_); + } else { + FML_DCHECK(used_ == 0u); + FML_DCHECK(allocated_ == 0u); + } +} + +static constexpr inline bool is_power_of_two(int value) { + return (value & (value - 1)) == 0; +} + +uint8_t* DisplayList::DlStorage::alloc(size_t bytes) { + if (disabled_) { + FML_DCHECK(is_valid()); + return nullptr; + } + FML_DCHECK(bytes < (1 << 24)); + if (used_ + bytes > allocated_) { + static_assert(is_power_of_two(kPageSize), + "This math needs updating for non-pow2."); + // Next greater multiple of DL_BUILDER_PAGE. + allocated_ = (used_ + bytes + kPageSize) & ~(kPageSize - 1); + realloc(allocated_); + FML_DCHECK(get()); + memset(get() + used_, 0, allocated_ - used_); + } + FML_DCHECK(used_ + bytes <= allocated_); + uint8_t* ret = get() + used_; + used_ += bytes; + return ret; +} + +void DisplayList::DlStorage::realloc(size_t count) { + FML_DCHECK(is_valid()); + FML_DCHECK(count >= used_); + ptr_.reset(static_cast(std::realloc(ptr_.release(), count))); + allocated_ = count; + FML_CHECK(ptr_); +} + +DisplayList::DlStorage DisplayList::DlStorage::take() { + realloc(used_); + return std::move(*this); } uint32_t DisplayList::next_unique_id() { @@ -112,10 +166,10 @@ class VectorCuller final : public Culler { } } void update(DispatchContext& context) override { - if (++context.cur_index > context.next_render_index) { + if (context.cur_render_index > context.next_render_index) { while (cur_ < end_) { context.next_render_index = rtree_->id(*cur_++); - if (context.next_render_index >= context.cur_index) { + if (context.next_render_index >= context.cur_render_index) { // It should be rare that we have duplicate indices // but if we do, then having a while loop is a cheap // insurance for those cases. @@ -139,8 +193,7 @@ class VectorCuller final : public Culler { }; void DisplayList::Dispatch(DlOpReceiver& receiver) const { - uint8_t* ptr = storage_.get(); - Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance); + Dispatch(receiver, storage_.get(), storage_.end(), NopCuller::instance); } void DisplayList::Dispatch(DlOpReceiver& receiver, @@ -164,11 +217,10 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, Dispatch(receiver); return; } - uint8_t* ptr = storage_.get(); std::vector rect_indices; rtree->search(cull_rect, &rect_indices); VectorCuller culler(rtree, rect_indices); - Dispatch(receiver, ptr, ptr + byte_count_, culler); + Dispatch(receiver, storage_.get(), storage_.end(), culler); } void DisplayList::Dispatch(DlOpReceiver& receiver, @@ -177,7 +229,7 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, Culler& culler) const { DispatchContext context = { .receiver = receiver, - .cur_index = 0, + .cur_render_index = 0, // next_render_index will be initialized by culler.init() .next_restore_index = std::numeric_limits::max(), }; @@ -209,7 +261,7 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, } } -void DisplayList::DisposeOps(uint8_t* ptr, uint8_t* end) { +void DisplayList::DlStorage::DisposeOps(uint8_t* ptr, uint8_t* end) { while (ptr < end) { auto op = reinterpret_cast(ptr); ptr += op->size; @@ -309,7 +361,8 @@ bool DisplayList::Equals(const DisplayList* other) const { if (this == other) { return true; } - if (byte_count_ != other->byte_count_ || op_count_ != other->op_count_) { + if (storage_.used() != other->storage_.used() || + op_count_ != other->op_count_) { return false; } uint8_t* ptr = storage_.get(); @@ -317,7 +370,7 @@ bool DisplayList::Equals(const DisplayList* other) const { if (ptr == o_ptr) { return true; } - return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other->byte_count_); + return CompareOps(ptr, storage_.end(), o_ptr, other->storage_.end()); } } // namespace flutter diff --git a/display_list/display_list.h b/display_list/display_list.h index 668a247d5596f..b5fdc8c5c8a26 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -150,7 +150,6 @@ enum class DisplayListOpType { #undef DL_OP_TO_ENUM_VALUE class DlOpReceiver; -class DisplayListBuilder; class SaveLayerOptions { public: @@ -202,26 +201,6 @@ class SaveLayerOptions { }; }; -// Manages a buffer allocated with malloc. -class DisplayListStorage { - public: - DisplayListStorage() = default; - DisplayListStorage(DisplayListStorage&&) = default; - - uint8_t* get() const { return ptr_.get(); } - - void realloc(size_t count) { - ptr_.reset(static_cast(std::realloc(ptr_.release(), count))); - FML_CHECK(ptr_); - } - - private: - struct FreeDeleter { - void operator()(uint8_t* p) { std::free(p); } - }; - std::unique_ptr ptr_; -}; - class Culler; // The base class that contains a sequence of rendering operations @@ -231,7 +210,7 @@ class DisplayList : public SkRefCnt { public: DisplayList(); - ~DisplayList(); + ~DisplayList() = default; void Dispatch(DlOpReceiver& ctx) const; void Dispatch(DlOpReceiver& ctx, const SkRect& cull_rect) const; @@ -241,7 +220,8 @@ class DisplayList : public SkRefCnt { // but nested ops are only included if requested. The defaults used // here for these accessors follow that pattern. size_t bytes(bool nested = true) const { - return sizeof(DisplayList) + byte_count_ + + FML_DCHECK(storage_.used() == storage_.allocated()); + return sizeof(DisplayList) + storage_.allocated() + (nested ? nested_byte_count_ : 0); } @@ -254,7 +234,7 @@ class DisplayList : public SkRefCnt { const SkRect& bounds() const { return bounds_; } bool has_rtree() const { return rtree_ != nullptr; } - sk_sp rtree() const { return rtree_; } + std::shared_ptr rtree() const { return rtree_; } bool Equals(const DisplayList* other) const; bool Equals(const DisplayList& other) const { return Equals(&other); } @@ -279,8 +259,40 @@ class DisplayList : public SkRefCnt { } private: - DisplayList(DisplayListStorage&& ptr, - size_t byte_count, + // Manages a buffer allocated with malloc. + class DlStorage { + public: + DlStorage() = default; + DlStorage(DlStorage&& source); + + ~DlStorage(); + + uint8_t* get() const { return ptr_.get(); } + uint8_t* end() const { return ptr_.get() + used_; } + size_t used() const { return used_; } + size_t allocated() const { return allocated_; } + bool is_valid() const { return !disabled_; } + + uint8_t* alloc(size_t bytes); + void realloc(size_t count); + DlStorage take(); + + private: + static constexpr size_t kPageSize = 4096u; + + static void DisposeOps(uint8_t* ptr, uint8_t* end); + + struct FreeDeleter { + void operator()(uint8_t* p) { std::free(p); } + }; + std::unique_ptr ptr_; + + bool disabled_ = false; + size_t used_ = 0u; + size_t allocated_ = 0u; + }; + + DisplayList(DlStorage&& ptr, unsigned int op_count, size_t nested_byte_count, unsigned int nested_op_count, @@ -288,14 +300,11 @@ class DisplayList : public SkRefCnt { bool can_apply_group_opacity, bool is_ui_thread_safe, bool modifies_transparent_black, - sk_sp rtree); + std::shared_ptr rtree); static uint32_t next_unique_id(); - static void DisposeOps(uint8_t* ptr, uint8_t* end); - - const DisplayListStorage storage_; - const size_t byte_count_; + const DlStorage storage_; const unsigned int op_count_; const size_t nested_byte_count_; @@ -308,13 +317,14 @@ class DisplayList : public SkRefCnt { const bool is_ui_thread_safe_; const bool modifies_transparent_black_; - const sk_sp rtree_; + const std::shared_ptr rtree_; void Dispatch(DlOpReceiver& ctx, uint8_t* ptr, uint8_t* end, Culler& culler) const; + friend class DlOpRecorder; friend class DisplayListBuilder; }; diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc new file mode 100644 index 0000000000000..871f5ee016863 --- /dev/null +++ b/display_list/display_list_builder.cc @@ -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. + +#include "flutter/display_list/display_list_builder.h" + +namespace flutter { + +DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, + bool prepare_rtree) + : DisplayListBuilder( + std::make_shared(cull_rect, prepare_rtree)) {} + +DisplayListBuilder::DisplayListBuilder( + const std::shared_ptr& recorder) + : DlCanvasToReceiver(recorder), recorder_(recorder) {} + +sk_sp DisplayListBuilder::Build() { + FML_CHECK(recorder_ != nullptr); + FML_CHECK(receiver_ != nullptr); + + RestoreToCount(1); + + sk_sp dl = + recorder_->Build(current_group_opacity_compatibility(), + current_affects_transparent_layer()); + + recorder_ = nullptr; + receiver_ = nullptr; + + return dl; +} + +} // namespace flutter diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h new file mode 100644 index 0000000000000..3fb50efb9b279 --- /dev/null +++ b/display_list/display_list_builder.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ + +#include "flutter/display_list/dl_canvas.h" +#include "flutter/display_list/dl_canvas_to_receiver.h" +#include "flutter/display_list/dl_op_flags.h" +#include "flutter/display_list/dl_op_receiver.h" +#include "flutter/display_list/dl_op_recorder.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/display_list/utils/dl_matrix_clip_tracker.h" +#include "flutter/fml/macros.h" + +namespace flutter { + +// The primary class used to build a display list. The list of methods +// here matches the list of methods invoked on a |DlOpReceiver| combined +// with the list of methods invoked on a |DlCanvas|. +class DisplayListBuilder final : public DlCanvasToReceiver, // + public SkRefCnt { + public: + static constexpr SkRect kMaxCullRect = + SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); + + explicit DisplayListBuilder(bool prepare_rtree) + : DisplayListBuilder(kMaxCullRect, prepare_rtree) {} + + explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect, + bool prepare_rtree = false); + + ~DisplayListBuilder() = default; + + sk_sp Build(); + + private: + explicit DisplayListBuilder(const std::shared_ptr& recorder); + + std::shared_ptr recorder_; + + // This method exposes the internal stateful DlOpReceiver implementation + // of the DisplayListBuilder, primarily for testing purposes. Its use + // is obsolete and forbidden in every other case and is only shared to a + // pair of "friend" accessors in the benchmark/unittest files. + DlOpReceiver& asReceiver() { return *receiver_; } + + friend DlOpReceiver& DisplayListBuilderBenchmarkAccessor( + DisplayListBuilder& builder); + friend DlOpReceiver& DisplayListBuilderTestingAccessor( + DisplayListBuilder& builder); + friend DlPaint DisplayListBuilderTestingAttributes( + DisplayListBuilder& builder); +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index bef888bcf8633..e9df415abb2dd 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -9,8 +9,8 @@ #include #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_blend_mode.h" -#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_paint.h" #include "flutter/display_list/geometry/dl_rtree.h" #include "flutter/display_list/skia/dl_sk_dispatcher.h" @@ -141,6 +141,9 @@ class DisplayListTestBase : public BaseT { setup(builder); renderer(builder, paint, render_rect); auto dl = builder.Build(); + FML_LOG(ERROR) << "dl: " << *dl; + FML_LOG(ERROR) << "ops: " << dl->op_count(); + FML_LOG(ERROR) << "bytes: " << dl->bytes(); EXPECT_EQ(dl->op_count(), 1u) << desc; EXPECT_EQ(dl->bounds(), expected_bounds) << desc; } @@ -233,23 +236,21 @@ TEST_F(DisplayListTest, EmptyBuild) { EXPECT_EQ(dl->bytes(), sizeof(DisplayList)); } -TEST_F(DisplayListTest, EmptyRebuild) { +#ifndef NDEBUG +TEST_F(DisplayListTest, SecondBuildDies) { DisplayListBuilder builder; auto dl1 = builder.Build(); - auto dl2 = builder.Build(); - auto dl3 = builder.Build(); - ASSERT_TRUE(dl1->Equals(dl2)); - ASSERT_TRUE(dl2->Equals(dl3)); + EXPECT_DEATH_IF_SUPPORTED(builder.Build(), "recorder_ != nullptr"); } -TEST_F(DisplayListTest, BuilderCanBeReused) { +TEST_F(DisplayListTest, BuilderCannotBeReused) { DisplayListBuilder builder(kTestBounds); builder.DrawRect(kTestBounds, DlPaint()); auto dl = builder.Build(); - builder.DrawRect(kTestBounds, DlPaint()); - auto dl2 = builder.Build(); - ASSERT_TRUE(dl->Equals(dl2)); + EXPECT_DEATH_IF_SUPPORTED(builder.DrawRect(kTestBounds, DlPaint()), + "receiver_ != nullptr"); } +#endif TEST_F(DisplayListTest, SaveRestoreRestoresTransform) { SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); @@ -300,47 +301,6 @@ TEST_F(DisplayListTest, SaveRestoreRestoresTransform) { check_defaults(builder, cull_rect); } -TEST_F(DisplayListTest, BuildRestoresTransform) { - SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); - DisplayListBuilder builder(cull_rect); - - builder.Translate(10.0f, 10.0f); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.Scale(10.0f, 10.0f); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.Skew(0.1f, 0.1f); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.Rotate(45.0f); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.Transform(SkMatrix::Scale(10.0f, 10.0f)); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.Transform2DAffine(1.0f, 0.0f, 12.0f, // - 0.0f, 1.0f, 35.0f); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.Transform(SkM44(SkMatrix::Scale(10.0f, 10.0f))); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.TransformFullPerspective(1.0f, 0.0f, 0.0f, 12.0f, // - 0.0f, 1.0f, 0.0f, 35.0f, // - 0.0f, 0.0f, 1.0f, 5.0f, // - 0.0f, 0.0f, 0.0f, 1.0f); - builder.Build(); - check_defaults(builder, cull_rect); -} - TEST_F(DisplayListTest, SaveRestoreRestoresClip) { SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); DisplayListBuilder builder(cull_rect); @@ -361,89 +321,6 @@ TEST_F(DisplayListTest, SaveRestoreRestoresClip) { check_defaults(builder, cull_rect); } -TEST_F(DisplayListTest, BuildRestoresClip) { - SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); - DisplayListBuilder builder(cull_rect); - - builder.ClipRect({0.0f, 0.0f, 10.0f, 10.0f}); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.ClipRRect(SkRRect::MakeRectXY({0.0f, 0.0f, 5.0f, 5.0f}, 2.0f, 2.0f)); - builder.Build(); - check_defaults(builder, cull_rect); - - builder.ClipPath(SkPath().addOval({0.0f, 0.0f, 10.0f, 10.0f})); - builder.Build(); - check_defaults(builder, cull_rect); -} - -TEST_F(DisplayListTest, BuildRestoresAttributes) { - SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); - DisplayListBuilder builder(cull_rect); - DlOpReceiver& receiver = ToReceiver(builder); - - receiver.setAntiAlias(true); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setDither(true); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setInvertColors(true); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setColor(DlColor::kRed()); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setBlendMode(DlBlendMode::kColorBurn); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setDrawStyle(DlDrawStyle::kStrokeAndFill); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setStrokeWidth(300.0f); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setStrokeMiter(300.0f); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setStrokeCap(DlStrokeCap::kRound); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setStrokeJoin(DlStrokeJoin::kRound); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setColorSource(&kTestSource1); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setColorFilter(&kTestMatrixColorFilter1); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setImageFilter(&kTestBlurImageFilter1); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setMaskFilter(&kTestMaskFilter1); - builder.Build(); - check_defaults(builder, cull_rect); - - receiver.setPathEffect(kTestPathEffect1.get()); - builder.Build(); - check_defaults(builder, cull_rect); -} - TEST_F(DisplayListTest, BuilderBoundsTransformComparedToSkia) { const SkRect frame_rect = SkRect::MakeLTRB(10, 10, 100, 100); DisplayListBuilder builder(frame_rect); @@ -575,8 +452,8 @@ TEST_F(DisplayListTest, SingleOpSizes) { auto& invocation = group.variants[i]; sk_sp dl = Build(invocation); auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")"; - ASSERT_EQ(dl->op_count(false), invocation.op_count()) << desc; - ASSERT_EQ(dl->bytes(false), invocation.byte_count()) << desc; + EXPECT_EQ(dl->op_count(false), invocation.op_count()) << desc; + EXPECT_EQ(dl->bytes(false), invocation.byte_count()) << desc; } } } @@ -684,23 +561,11 @@ TEST_F(DisplayListTest, SingleOpDisplayListsAreEqualWithOrWithoutRtree) { TEST_F(DisplayListTest, FullRotationsAreNop) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.rotate(0); - receiver.rotate(360); - receiver.rotate(720); - receiver.rotate(1080); - receiver.rotate(1440); - sk_sp dl = builder.Build(); - ASSERT_EQ(dl->bytes(false), sizeof(DisplayList)); - ASSERT_EQ(dl->bytes(true), sizeof(DisplayList)); - ASSERT_EQ(dl->op_count(false), 0u); - ASSERT_EQ(dl->op_count(true), 0u); -} - -TEST_F(DisplayListTest, AllBlendModeNops) { - DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setBlendMode(DlBlendMode::kSrcOver); + builder.Rotate(0); + builder.Rotate(360); + builder.Rotate(720); + builder.Rotate(1080); + builder.Rotate(1440); sk_sp dl = builder.Build(); ASSERT_EQ(dl->bytes(false), sizeof(DisplayList)); ASSERT_EQ(dl->bytes(true), sizeof(DisplayList)); @@ -769,10 +634,10 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // No tricky stuff, just verifying drawing a rect produces rect bounds DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); - receiver.drawRect(rect); - receiver.restore(); + DlPaint save_paint; + builder.SaveLayer(&save_bounds, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), rect); } @@ -780,12 +645,11 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Now checking that a normal color filter still produces rect bounds DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColorFilter(&base_color_filter); - receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); - receiver.setColorFilter(nullptr); - receiver.drawRect(rect); - receiver.restore(); + DlPaint save_paint; + save_paint.setColorFilter(&base_color_filter); + builder.SaveLayer(&save_bounds, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), rect); } @@ -813,12 +677,11 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // cull rect of the DisplayListBuilder when it encounters a // save layer that modifies an unbounded region DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColorFilter(&alpha_color_filter); - receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); - receiver.setColorFilter(nullptr); - receiver.drawRect(rect); - receiver.restore(); + DlPaint save_paint; + save_paint.setColorFilter(&alpha_color_filter); + builder.SaveLayer(&save_bounds, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -827,12 +690,11 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // Verifying that the save layer bounds are not relevant // to the behavior in the previous example DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColorFilter(&alpha_color_filter); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setColorFilter(nullptr); - receiver.drawRect(rect); - receiver.restore(); + DlPaint save_paint; + save_paint.setColorFilter(&alpha_color_filter); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -841,13 +703,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // Making sure hiding a ColorFilter as an ImageFilter will // generate the same behavior as setting it as a ColorFilter DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); + DlPaint save_paint; DlColorFilterImageFilter color_filter_image_filter(base_color_filter); - receiver.setImageFilter(&color_filter_image_filter); - receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); - receiver.setImageFilter(nullptr); - receiver.drawRect(rect); - receiver.restore(); + save_paint.setImageFilter(&color_filter_image_filter); + builder.SaveLayer(&save_bounds, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), rect); } @@ -856,13 +717,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // Making sure hiding a problematic ColorFilter as an ImageFilter // will generate the same behavior as setting it as a ColorFilter DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); + DlPaint save_paint; DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter); - receiver.setImageFilter(&color_filter_image_filter); - receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); - receiver.setImageFilter(nullptr); - receiver.drawRect(rect); - receiver.restore(); + save_paint.setImageFilter(&color_filter_image_filter); + builder.SaveLayer(&save_bounds, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -870,13 +730,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Same as above (ImageFilter hiding ColorFilter) with no save bounds DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); + DlPaint save_paint; DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter); - receiver.setImageFilter(&color_filter_image_filter); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setImageFilter(nullptr); - receiver.drawRect(rect); - receiver.restore(); + save_paint.setImageFilter(&color_filter_image_filter); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -884,12 +743,11 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Testing behavior with an unboundable blend mode DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setBlendMode(DlBlendMode::kClear); - receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); - receiver.setBlendMode(DlBlendMode::kSrcOver); - receiver.drawRect(rect); - receiver.restore(); + DlPaint save_paint; + save_paint.setBlendMode(DlBlendMode::kClear); + builder.SaveLayer(&save_bounds, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -897,12 +755,11 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Same as previous with no save bounds DisplayListBuilder builder(build_bounds); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setBlendMode(DlBlendMode::kClear); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setBlendMode(DlBlendMode::kSrcOver); - receiver.drawRect(rect); - receiver.restore(); + DlPaint save_paint; + save_paint.setBlendMode(DlBlendMode::kClear); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect(rect, DlPaint()); + builder.Restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -929,16 +786,14 @@ TEST_F(DisplayListTest, NestedOpCountMetricsSameAsSkPicture) { ASSERT_EQ(picture->approximateOpCount(true), 36); DisplayListBuilder builder(SkRect::MakeWH(150, 100)); - DlOpReceiver& receiver = ToReceiver(builder); for (int y = 10; y <= 60; y += 10) { for (int x = 10; x <= 60; x += 10) { - receiver.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE); - receiver.drawRect(SkRect::MakeXYWH(x, y, 80, 80)); + DlColor color = ((x + y) % 20) == 10 ? DlColor::kRed() : DlColor::kBlue(); + builder.DrawRect(SkRect::MakeXYWH(x, y, 80, 80), DlPaint(color)); } } DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100)); - DlOpReceiver& outer_receiver = ToReceiver(outer_builder); - outer_receiver.drawDisplayList(builder.Build()); + outer_builder.DrawDisplayList(builder.Build()); auto display_list = outer_builder.Build(); ASSERT_EQ(display_list->op_count(), 1u); @@ -963,9 +818,8 @@ TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) { { // First test == DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - // receiver.transformFullPerspective takes row-major order - receiver.transformFullPerspective( + // builder.transformFullPerspective takes row-major order + builder.TransformFullPerspective( // clang-format off 1, 2, 3, 4, 5, 6, 7, 8, @@ -986,9 +840,8 @@ TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) { } { // Next test != DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - // receiver.transformFullPerspective takes row-major order - receiver.transformFullPerspective( + // builder.transformFullPerspective takes row-major order + builder.TransformFullPerspective( // clang-format off 1, 5, 9, 13, 2, 6, 7, 11, @@ -1011,9 +864,8 @@ TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) { TEST_F(DisplayListTest, DisplayListTransformResetHandling) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.scale(20.0, 20.0); - receiver.transformReset(); + builder.Scale(20.0, 20.0); + builder.TransformReset(); auto display_list = builder.Build(); ASSERT_NE(display_list, nullptr); sk_sp surface = @@ -1028,14 +880,14 @@ TEST_F(DisplayListTest, DisplayListTransformResetHandling) { TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { auto run_tests = [](const std::string& name, - void build(DlOpReceiver & receiver), bool expect_for_op, - bool expect_with_kSrc) { + void build(DlCanvas & cv, const DlPaint& paint), + bool expect_for_op, bool expect_with_kSrc) { { // First test is the draw op, by itself // (usually supports group opacity) DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - build(receiver); + DlPaint paint; + build(builder, paint); auto display_list = builder.Build(); EXPECT_EQ(display_list->can_apply_group_opacity(), expect_for_op) << "{" << std::endl @@ -1043,12 +895,12 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { << "}"; } { - // Second test i the draw op with kSrc, + // Second test is the draw op with kSrc, // (usually fails group opacity) DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setBlendMode(DlBlendMode::kSrc); - build(receiver); + DlPaint paint; + paint.setBlendMode(DlBlendMode::kSrc); + build(builder, paint); auto display_list = builder.Build(); EXPECT_EQ(display_list->can_apply_group_opacity(), expect_with_kSrc) << "{" << std::endl @@ -1060,67 +912,64 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { #define RUN_TESTS(body) \ run_tests( \ - #body, [](DlOpReceiver& receiver) { body }, true, false) + #body, [](DlCanvas& cv, const DlPaint& paint) { body }, true, false) #define RUN_TESTS2(body, expect) \ run_tests( \ - #body, [](DlOpReceiver& receiver) { body }, expect, expect) - - RUN_TESTS(receiver.drawPaint();); - RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true); - RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrc);, false); - RUN_TESTS(receiver.drawLine({0, 0}, {10, 10});); - RUN_TESTS(receiver.drawRect({0, 0, 10, 10});); - RUN_TESTS(receiver.drawOval({0, 0, 10, 10});); - RUN_TESTS(receiver.drawCircle({10, 10}, 5);); - RUN_TESTS(receiver.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2));); - RUN_TESTS(receiver.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), - SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2));); - RUN_TESTS(receiver.drawPath( - SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15}));); - RUN_TESTS(receiver.drawArc({0, 0, 10, 10}, 0, math::kPi, true);); + #body, [](DlCanvas& cv, const DlPaint& paint) { body }, expect, expect) + + RUN_TESTS(cv.DrawPaint(paint);); + RUN_TESTS(cv.DrawColor(SK_ColorRED, paint.getBlendMode());); + RUN_TESTS2(cv.DrawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true); + RUN_TESTS2(cv.DrawColor(SK_ColorRED, DlBlendMode::kSrc);, false); + RUN_TESTS(cv.DrawLine({0, 0}, {10, 10}, paint);); + RUN_TESTS(cv.DrawRect({0, 0, 10, 10}, paint);); + RUN_TESTS(cv.DrawOval({0, 0, 10, 10}, paint);); + RUN_TESTS(cv.DrawCircle({10, 10}, 5, paint);); + RUN_TESTS(cv.DrawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), paint);); + RUN_TESTS(cv.DrawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), + SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2), paint);); + RUN_TESTS(cv.DrawPath( + SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15}), paint);); + RUN_TESTS(cv.DrawArc({0, 0, 10, 10}, 0, math::kPi, true, paint);); RUN_TESTS2( - receiver.drawPoints(PointMode::kPoints, TestPointCount, TestPoints); + cv.DrawPoints(PointMode::kPoints, TestPointCount, TestPoints, paint); , false); - RUN_TESTS2(receiver.drawVertices(TestVertices1.get(), DlBlendMode::kSrc); + RUN_TESTS2(cv.DrawVertices(TestVertices1.get(), DlBlendMode::kSrc, paint); , false); - RUN_TESTS(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, true);); - RUN_TESTS2(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, false); + RUN_TESTS(cv.DrawImage(TestImage1, {0, 0}, kLinearSampling, &paint);); + RUN_TESTS(cv.DrawImageRect(TestImage1, SkRect{10.0f, 10.0f, 20.0f, 20.0f}, + {0, 0, 10, 10}, kNearestSampling, &paint, + DlCanvas::SrcRectConstraint::kFast);); + RUN_TESTS2(cv.DrawImageRect(TestImage1, SkRect{10, 10, 20, 20}, + {0, 0, 10, 10}, kNearestSampling, nullptr, + DlCanvas::SrcRectConstraint::kFast); , true); - RUN_TESTS(receiver.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10}, - kNearestSampling, true, - DlCanvas::SrcRectConstraint::kFast);); - RUN_TESTS2(receiver.drawImageRect(TestImage1, {10, 10, 20, 20}, - {0, 0, 10, 10}, kNearestSampling, false, - DlCanvas::SrcRectConstraint::kFast); + RUN_TESTS(cv.DrawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, + DlFilterMode::kLinear, &paint);); + RUN_TESTS2(cv.DrawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, + DlFilterMode::kLinear, nullptr); , true); - RUN_TESTS(receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, - DlFilterMode::kLinear, true);); - RUN_TESTS2( - receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, - DlFilterMode::kLinear, false); - , true); static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}}; static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}}; RUN_TESTS2( - receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2, - DlBlendMode::kSrcIn, kNearestSampling, nullptr, true); + cv.DrawAtlas(TestImage1, xforms, texs, nullptr, 2, DlBlendMode::kSrcIn, + kNearestSampling, nullptr, &paint); , false); RUN_TESTS2( - receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2, - DlBlendMode::kSrcIn, kNearestSampling, nullptr, false); + cv.DrawAtlas(TestImage1, xforms, texs, nullptr, 2, DlBlendMode::kSrcIn, + kNearestSampling, nullptr, nullptr); , false); EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity()); - RUN_TESTS2(receiver.drawDisplayList(TestDisplayList1);, true); + RUN_TESTS2(cv.DrawDisplayList(TestDisplayList1);, true); { static DisplayListBuilder builder; builder.DrawRect({0, 0, 10, 10}, DlPaint()); builder.DrawRect({5, 5, 15, 15}, DlPaint()); static auto display_list = builder.Build(); - RUN_TESTS2(receiver.drawDisplayList(display_list);, false); + RUN_TESTS2(cv.DrawDisplayList(display_list);, false); } - RUN_TESTS2(receiver.drawTextBlob(TestBlob1, 0, 0);, false); - RUN_TESTS2(receiver.drawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0); - , false); + RUN_TESTS2(cv.DrawTextBlob(TestBlob1, 0, 0, paint);, false); + RUN_TESTS2(cv.DrawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0);, false); #undef RUN_TESTS2 #undef RUN_TESTS @@ -1128,9 +977,8 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); for (int i = 0; i < 10; i++) { - receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); } auto display_list = builder.Build(); EXPECT_FALSE(display_list->can_apply_group_opacity()); @@ -1138,80 +986,65 @@ TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) { TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + builder.SaveLayer(nullptr, nullptr); for (int i = 0; i < 10; i++) { - receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); } - receiver.restore(); + builder.Restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityOverlappingChidren) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + DlPaint save_paint; + builder.SaveLayer(nullptr, &save_paint); for (int i = 0; i < 10; i++) { - receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); } - receiver.restore(); - auto display_list = builder.Build(); - EXPECT_TRUE(display_list->can_apply_group_opacity()); -} - -TEST_F(DisplayListTest, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) { - DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setBlendMode(DlBlendMode::kSrc); - receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); - receiver.drawRect({0, 0, 10, 10}); - receiver.restore(); + builder.Restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } -TEST_F(DisplayListTest, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) { +TEST_F(DisplayListTest, SaveLayerPaintWithSrcBlendDoesNotSupportGroupOpacity) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setBlendMode(DlBlendMode::kSrc); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.drawRect({0, 0, 10, 10}); - receiver.restore(); + DlPaint save_paint; + save_paint.setBlendMode(DlBlendMode::kSrc); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({0, 0, 10, 10}, DlPaint()); + builder.Restore(); auto display_list = builder.Build(); EXPECT_FALSE(display_list->can_apply_group_opacity()); } TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); - receiver.setBlendMode(DlBlendMode::kSrc); - receiver.drawRect({0, 0, 10, 10}); - receiver.restore(); + builder.SaveLayer(nullptr, nullptr); + builder.DrawRect({0, 0, 10, 10}, DlPaint().setBlendMode(DlBlendMode::kSrc)); + builder.Restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } -TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) { +TEST_F(DisplayListTest, SaveLayerPaintSupportsGroupOpacityWithChildSrcBlend) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setBlendMode(DlBlendMode::kSrc); - receiver.drawRect({0, 0, 10, 10}); - receiver.restore(); + DlPaint save_paint; + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({0, 0, 10, 10}, DlPaint().setBlendMode(DlBlendMode::kSrc)); + builder.Restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } TEST_F(DisplayListTest, SaveLayerBoundsSnapshotsImageFilter) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.drawRect({50, 50, 100, 100}); + DlPaint save_paint; + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({50, 50, 100, 100}, DlPaint()); // This image filter should be ignored since it was not set before saveLayer - receiver.setImageFilter(&kTestBlurImageFilter1); - receiver.restore(); + ToReceiver(builder).setImageFilter(&kTestBlurImageFilter1); + builder.Restore(); SkRect bounds = builder.Build()->bounds(); EXPECT_EQ(bounds, SkRect::MakeLTRB(50, 50, 100, 100)); } @@ -1249,11 +1082,11 @@ TEST_F(DisplayListTest, SaveLayerOneSimpleOpInheritsOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1265,10 +1098,9 @@ TEST_F(DisplayListTest, SaveLayerNoAttributesInheritsOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + builder.SaveLayer(nullptr, nullptr); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1279,12 +1111,12 @@ TEST_F(DisplayListTest, SaveLayerTwoOverlappingOpsDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.drawRect({10, 10, 20, 20}); - receiver.drawRect({15, 15, 25, 25}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.DrawRect({15, 15, 25, 25}, DlPaint()); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1299,16 +1131,16 @@ TEST_F(DisplayListTest, NestedSaveLayersMightInheritOpacity) { SaveLayerOptionsExpector expector({expected1, expected2, expected3}); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.drawRect({10, 10, 20, 20}); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.drawRect({15, 15, 25, 25}); - receiver.restore(); - receiver.restore(); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({15, 15, 25, 25}, DlPaint()); + builder.Restore(); + builder.Restore(); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 3); @@ -1322,13 +1154,13 @@ TEST_F(DisplayListTest, NestedSaveLayersCanBothSupportOpacityOptimization) { SaveLayerOptionsExpector expector({expected1, expected2}); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.SaveLayer(nullptr, nullptr); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Restore(); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 2); @@ -1339,13 +1171,12 @@ TEST_F(DisplayListTest, SaveLayerImageFilterDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.setImageFilter(&kTestBlurImageFilter1); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setImageFilter(nullptr); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + save_paint.setImageFilter(&kTestBlurImageFilter1); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1356,13 +1187,12 @@ TEST_F(DisplayListTest, SaveLayerColorFilterDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.setColorFilter(&kTestMatrixColorFilter1); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setColorFilter(nullptr); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + save_paint.setColorFilter(&kTestMatrixColorFilter1); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1373,13 +1203,12 @@ TEST_F(DisplayListTest, SaveLayerSrcBlendDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.setBlendMode(DlBlendMode::kSrc); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setBlendMode(DlBlendMode::kSrcOver); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + save_paint.setBlendMode(DlBlendMode::kSrc); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1391,12 +1220,12 @@ TEST_F(DisplayListTest, SaveLayerImageFilterOnChildInheritsOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setImageFilter(&kTestBlurImageFilter1); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, + DlPaint().setImageFilter(&kTestBlurImageFilter1)); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1407,12 +1236,12 @@ TEST_F(DisplayListTest, SaveLayerColorFilterOnChildDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setColorFilter(&kTestMatrixColorFilter1); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, + DlPaint().setColorFilter(&kTestMatrixColorFilter1)); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1423,12 +1252,11 @@ TEST_F(DisplayListTest, SaveLayerSrcBlendOnChildDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); - receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); - receiver.setBlendMode(DlBlendMode::kSrc); - receiver.drawRect({10, 10, 20, 20}); - receiver.restore(); + DlPaint save_paint; + save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect({10, 10, 20, 20}, DlPaint().setBlendMode(DlBlendMode::kSrc)); + builder.Restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1562,15 +1390,14 @@ TEST_F(DisplayListTest, FlutterSvgIssue661BoundsWereEmpty) { TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.translate(12.3, 14.5); + builder.Translate(12.3, 14.5); SkMatrix matrix = SkMatrix::Translate(12.3, 14.5); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - receiver.translate(10, 10); + builder.Translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1581,15 +1408,14 @@ TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) { TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.scale(12.3, 14.5); + builder.Scale(12.3, 14.5); SkMatrix matrix = SkMatrix::Scale(12.3, 14.5); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - receiver.translate(10, 10); + builder.Translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1600,15 +1426,14 @@ TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) { TEST_F(DisplayListTest, RotateAffectsCurrentTransform) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.rotate(12.3); + builder.Rotate(12.3); SkMatrix matrix = SkMatrix::RotateDeg(12.3); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - receiver.translate(10, 10); + builder.Translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1619,15 +1444,14 @@ TEST_F(DisplayListTest, RotateAffectsCurrentTransform) { TEST_F(DisplayListTest, SkewAffectsCurrentTransform) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.skew(12.3, 14.5); + builder.Skew(12.3, 14.5); SkMatrix matrix = SkMatrix::Skew(12.3, 14.5); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - receiver.translate(10, 10); + builder.Translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1638,9 +1462,8 @@ TEST_F(DisplayListTest, SkewAffectsCurrentTransform) { TEST_F(DisplayListTest, TransformAffectsCurrentTransform) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.transform2DAffine(3, 0, 12.3, // - 1, 5, 14.5); + builder.Transform2DAffine(3, 0, 12.3, // + 1, 5, 14.5); SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3, // 1, 5, 14.5, // 0, 0, 1); @@ -1649,7 +1472,7 @@ TEST_F(DisplayListTest, TransformAffectsCurrentTransform) { SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - receiver.translate(10, 10); + builder.Translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1660,11 +1483,10 @@ TEST_F(DisplayListTest, TransformAffectsCurrentTransform) { TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.transformFullPerspective(3, 0, 4, 12.3, // - 1, 5, 3, 14.5, // - 0, 0, 7, 16.2, // - 0, 0, 0, 1); + builder.TransformFullPerspective(3, 0, 4, 12.3, // + 1, 5, 3, 14.5, // + 0, 0, 7, 16.2, // + 0, 0, 0, 1); SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3, // 1, 5, 14.5, // 0, 0, 1); @@ -1676,7 +1498,7 @@ TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) { SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - receiver.translate(10, 10); + builder.Translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1687,9 +1509,8 @@ TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) { TEST_F(DisplayListTest, ClipRectAffectsClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); - receiver.clipRect(clip_bounds, ClipOp::kIntersect, false); + builder.ClipRect(clip_bounds, ClipOp::kIntersect, false); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1697,27 +1518,27 @@ TEST_F(DisplayListTest, ClipRectAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.save(); - receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - receiver.save(); - receiver.scale(2, 2); + builder.Save(); + builder.Scale(2, 2); SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1726,10 +1547,9 @@ TEST_F(DisplayListTest, ClipRectAffectsClipBounds) { TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26); - receiver.clipRect(clip_bounds, ClipOp::kIntersect, true); + builder.ClipRect(clip_bounds, ClipOp::kIntersect, true); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1737,27 +1557,27 @@ TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - receiver.save(); - receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); + builder.Save(); + builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - receiver.save(); - receiver.scale(2, 2); + builder.Save(); + builder.Scale(2, 2); SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1766,30 +1586,28 @@ TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) { TEST_F(DisplayListTest, ClipRectAffectsClipBoundsWithMatrix) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10); SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20); - receiver.save(); - receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false); - receiver.translate(10, 0); - receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRect(clip_bounds_1, ClipOp::kIntersect, false); + builder.Translate(10, 0); + builder.ClipRect(clip_bounds_1, ClipOp::kIntersect, false); ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty()); - receiver.restore(); + builder.Restore(); - receiver.save(); - receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false); - receiver.translate(-10, -10); - receiver.clipRect(clip_bounds_2, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRect(clip_bounds_1, ClipOp::kIntersect, false); + builder.Translate(-10, -10); + builder.ClipRect(clip_bounds_2, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1); - receiver.restore(); + builder.Restore(); } TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2); - receiver.clipRRect(clip, ClipOp::kIntersect, false); + builder.ClipRRect(clip, ClipOp::kIntersect, false); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1797,27 +1615,27 @@ TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.save(); - receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - receiver.save(); - receiver.scale(2, 2); + builder.Save(); + builder.Scale(2, 2); SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1826,11 +1644,10 @@ TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) { TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26); SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2); - receiver.clipRRect(clip, ClipOp::kIntersect, true); + builder.ClipRRect(clip, ClipOp::kIntersect, true); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1838,27 +1655,27 @@ TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - receiver.save(); - receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); + builder.Save(); + builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - receiver.save(); - receiver.scale(2, 2); + builder.Save(); + builder.Scale(2, 2); SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1867,33 +1684,31 @@ TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) { TEST_F(DisplayListTest, ClipRRectAffectsClipBoundsWithMatrix) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10); SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20); SkRRect clip1 = SkRRect::MakeRectXY(clip_bounds_1, 3, 2); SkRRect clip2 = SkRRect::MakeRectXY(clip_bounds_2, 3, 2); - receiver.save(); - receiver.clipRRect(clip1, ClipOp::kIntersect, false); - receiver.translate(10, 0); - receiver.clipRRect(clip1, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRRect(clip1, ClipOp::kIntersect, false); + builder.Translate(10, 0); + builder.ClipRRect(clip1, ClipOp::kIntersect, false); ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty()); - receiver.restore(); + builder.Restore(); - receiver.save(); - receiver.clipRRect(clip1, ClipOp::kIntersect, false); - receiver.translate(-10, -10); - receiver.clipRRect(clip2, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRRect(clip1, ClipOp::kIntersect, false); + builder.Translate(-10, -10); + builder.ClipRRect(clip2, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1); - receiver.restore(); + builder.Restore(); } TEST_F(DisplayListTest, ClipPathAffectsClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); - receiver.clipPath(clip, ClipOp::kIntersect, false); + builder.ClipPath(clip, ClipOp::kIntersect, false); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1901,27 +1716,27 @@ TEST_F(DisplayListTest, ClipPathAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.save(); - receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - receiver.save(); - receiver.scale(2, 2); + builder.Save(); + builder.Scale(2, 2); SkRect scaled_clip_bounds = SkRect::MakeLTRB(4.1, 4.65, 11.2, 13.85); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1930,10 +1745,9 @@ TEST_F(DisplayListTest, ClipPathAffectsClipBounds) { TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28); - receiver.clipPath(clip, ClipOp::kIntersect, true); + builder.ClipPath(clip, ClipOp::kIntersect, true); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1941,27 +1755,27 @@ TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - receiver.save(); - receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); + builder.Save(); + builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - receiver.save(); - receiver.scale(2, 2); + builder.Save(); + builder.Scale(2, 2); SkRect scaled_expanded_bounds = SkRect::MakeLTRB(4, 4.5, 11.5, 14); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds); - receiver.restore(); + builder.Restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1970,32 +1784,30 @@ TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) { TEST_F(DisplayListTest, ClipPathAffectsClipBoundsWithMatrix) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(0, 0, 10, 10); SkPath clip1 = SkPath().addCircle(2.5, 2.5, 2.5).addCircle(7.5, 7.5, 2.5); SkPath clip2 = SkPath().addCircle(12.5, 12.5, 2.5).addCircle(17.5, 17.5, 2.5); - receiver.save(); - receiver.clipPath(clip1, ClipOp::kIntersect, false); - receiver.translate(10, 0); - receiver.clipPath(clip1, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipPath(clip1, ClipOp::kIntersect, false); + builder.Translate(10, 0); + builder.ClipPath(clip1, ClipOp::kIntersect, false); ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty()); - receiver.restore(); + builder.Restore(); - receiver.save(); - receiver.clipPath(clip1, ClipOp::kIntersect, false); - receiver.translate(-10, -10); - receiver.clipPath(clip2, ClipOp::kIntersect, false); + builder.Save(); + builder.ClipPath(clip1, ClipOp::kIntersect, false); + builder.Translate(-10, -10); + builder.ClipPath(clip2, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - receiver.restore(); + builder.Restore(); } TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); - receiver.clipRect(clip_bounds, ClipOp::kIntersect, false); + builder.ClipRect(clip_bounds, ClipOp::kIntersect, false); // Save initial return values for testing after kDifference clip SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -2003,18 +1815,17 @@ TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.clipRect(diff_clip, ClipOp::kDifference, false); + builder.ClipRect(diff_clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); } TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkRRect diff_clip = SkRRect::MakeRectXY({0, 0, 15, 15}, 1, 1); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRRect clip = SkRRect::MakeRectXY({10.2, 11.3, 20.4, 25.7}, 3, 2); - receiver.clipRRect(clip, ClipOp::kIntersect, false); + builder.ClipRRect(clip, ClipOp::kIntersect, false); // Save initial return values for testing after kDifference clip SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -2022,18 +1833,17 @@ TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.clipRRect(diff_clip, ClipOp::kDifference, false); + builder.ClipRRect(diff_clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); } TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); SkPath diff_clip = SkPath().addRect({0, 0, 15, 15}); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); - receiver.clipPath(clip, ClipOp::kIntersect, false); + builder.ClipPath(clip, ClipOp::kIntersect, false); // Save initial return values for testing after kDifference clip SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -2041,7 +1851,7 @@ TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - receiver.clipPath(diff_clip, ClipOp::kDifference, false); + builder.ClipPath(diff_clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); } @@ -2049,10 +1859,9 @@ TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) { TEST_F(DisplayListTest, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) { SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0); DisplayListBuilder builder(cull_rect); - DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); clip.setFillType(SkPathFillType::kInverseWinding); - receiver.clipPath(clip, ClipOp::kIntersect, false); + builder.ClipPath(clip, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetLocalClipBounds(), cull_rect); ASSERT_EQ(builder.GetDestinationClipBounds(), cull_rect); @@ -2061,11 +1870,10 @@ TEST_F(DisplayListTest, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) { TEST_F(DisplayListTest, DiffClipPathWithInvertFillTypeAffectsClipBounds) { SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0); DisplayListBuilder builder(cull_rect); - DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); clip.setFillType(SkPathFillType::kInverseWinding); SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); - receiver.clipPath(clip, ClipOp::kDifference, false); + builder.ClipPath(clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), clip_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); @@ -2076,8 +1884,7 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { SkPoint vertical_points[2] = {{10, 10}, {10, 20}}; { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points); + builder.DrawPoints(PointMode::kPolygon, 2, horizontal_points, DlPaint()); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(20, 10)); @@ -2085,8 +1892,7 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawPoints(PointMode::kPolygon, 2, vertical_points); + builder.DrawPoints(PointMode::kPolygon, 2, vertical_points, DlPaint()); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(10, 20)); @@ -2094,16 +1900,14 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawPoints(PointMode::kPoints, 1, horizontal_points); + builder.DrawPoints(PointMode::kPoints, 1, horizontal_points, DlPaint()); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); } { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setStrokeWidth(2); - receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points); + builder.DrawPoints(PointMode::kPolygon, 2, horizontal_points, + DlPaint().setStrokeWidth(2)); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(20, 10)); @@ -2111,9 +1915,8 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setStrokeWidth(2); - receiver.drawPoints(PointMode::kPolygon, 2, vertical_points); + builder.DrawPoints(PointMode::kPolygon, 2, vertical_points, + DlPaint().setStrokeWidth(2)); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(10, 20)); @@ -2121,16 +1924,15 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.setStrokeWidth(2); - receiver.drawPoints(PointMode::kPoints, 1, horizontal_points); + builder.DrawPoints(PointMode::kPoints, 1, horizontal_points, + DlPaint().setStrokeWidth(2)); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_EQ(bounds, SkRect::MakeLTRB(9, 9, 11, 11)); } } -static void test_rtree(const sk_sp& rtree, +static void test_rtree(const std::shared_ptr& rtree, const SkRect& query, std::vector expected_rects, const std::vector& expected_indices) { @@ -2149,9 +1951,8 @@ static void test_rtree(const sk_sp& rtree, TEST_F(DisplayListTest, RTreeOfSimpleScene) { DisplayListBuilder builder(/*prepare_rtree=*/true); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawRect({10, 10, 20, 20}); - receiver.drawRect({50, 50, 60, 60}); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.DrawRect({50, 50, 60, 60}, DlPaint()); auto display_list = builder.Build(); auto rtree = display_list->rtree(); std::vector rects = { @@ -2177,11 +1978,10 @@ TEST_F(DisplayListTest, RTreeOfSimpleScene) { TEST_F(DisplayListTest, RTreeOfSaveRestoreScene) { DisplayListBuilder builder(/*prepare_rtree=*/true); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawRect({10, 10, 20, 20}); - receiver.save(); - receiver.drawRect({50, 50, 60, 60}); - receiver.restore(); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Save(); + builder.DrawRect({50, 50, 60, 60}, DlPaint()); + builder.Restore(); auto display_list = builder.Build(); auto rtree = display_list->rtree(); std::vector rects = { @@ -2242,14 +2042,12 @@ TEST_F(DisplayListTest, RTreeOfSaveLayerFilterScene) { TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) { DisplayListBuilder nested_dl_builder(/**prepare_rtree=*/true); - DlOpReceiver& nested_dl_receiver = ToReceiver(nested_dl_builder); - nested_dl_receiver.drawRect({10, 10, 20, 20}); - nested_dl_receiver.drawRect({50, 50, 60, 60}); + nested_dl_builder.DrawRect({10, 10, 20, 20}, DlPaint()); + nested_dl_builder.DrawRect({50, 50, 60, 60}, DlPaint()); auto nested_display_list = nested_dl_builder.Build(); DisplayListBuilder builder(/**prepare_rtree=*/true); - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawDisplayList(nested_display_list); + builder.DrawDisplayList(nested_display_list); auto display_list = builder.Build(); auto rtree = display_list->rtree(); @@ -2265,67 +2063,61 @@ TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) { TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairs) { { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawRect({10, 10, 20, 20}); - receiver.save(); // This save op is unnecessary - receiver.drawRect({50, 50, 60, 60}); - receiver.restore(); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Save(); // This save op is unnecessary + builder.DrawRect({50, 50, 60, 60}, DlPaint()); + builder.Restore(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({10, 10, 20, 20}); - receiver2.drawRect({50, 50, 60, 60}); + builder2.DrawRect({10, 10, 20, 20}, DlPaint()); + builder2.DrawRect({50, 50, 60, 60}, DlPaint()); ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build())); } { DisplayListBuilder builder; - DlOpReceiver& receiver = ToReceiver(builder); - receiver.drawRect({10, 10, 20, 20}); - receiver.save(); - receiver.translate(1.0, 1.0); + builder.DrawRect({10, 10, 20, 20}, DlPaint()); + builder.Save(); + builder.Translate(1.0, 1.0); { - receiver.save(); // unnecessary - receiver.drawRect({50, 50, 60, 60}); - receiver.restore(); + builder.Save(); // unnecessary + builder.DrawRect({50, 50, 60, 60}, DlPaint()); + builder.Restore(); } - receiver.restore(); + builder.Restore(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({10, 10, 20, 20}); - receiver2.save(); - receiver2.translate(1.0, 1.0); - { receiver2.drawRect({50, 50, 60, 60}); } - receiver2.restore(); + builder2.DrawRect({10, 10, 20, 20}, DlPaint()); + builder2.Save(); + builder2.Translate(1.0, 1.0); + { builder2.DrawRect({50, 50, 60, 60}, DlPaint()); } + builder2.Restore(); ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build())); } } TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.save(); - receiver1.translate(10, 10); - receiver1.scale(2, 2); - receiver1.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Save(); + builder1.Translate(10, 10); + builder1.Scale(2, 2); + builder1.ClipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.translate(10, 10); - receiver2.scale(2, 2); - receiver2.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.Translate(10, 10); + builder2.Scale(2, 2); + builder2.ClipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2333,21 +2125,19 @@ TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) { TEST_F(DisplayListTest, CollapseNestedSaveAndSaveLayerRestore) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.scale(2, 2); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.SaveLayer(nullptr, nullptr); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Scale(2, 2); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.scale(2, 2); - receiver2.restore(); + builder2.SaveLayer(nullptr, nullptr); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Scale(2, 2); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2410,39 +2200,37 @@ TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairsInSetPaint) { TEST_F(DisplayListTest, TransformTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.transformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.TransformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.TransformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.transformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); - receiver2.save(); - receiver2.transformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.TransformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); + builder2.Save(); + builder2.TransformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2450,21 +2238,19 @@ TEST_F(DisplayListTest, TransformTriggersDeferredSave) { TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transform2DAffine(0, 1, 12, 1, 0, 33); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Transform2DAffine(0, 1, 12, 1, 0, 33); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.transform2DAffine(0, 1, 12, 1, 0, 33); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.Transform2DAffine(0, 1, 12, 1, 0, 33); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2472,27 +2258,25 @@ TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) { TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transformFullPerspective(0, 1, 0, 12, // - 1, 0, 0, 33, // - 3, 2, 5, 29, // - 0, 0, 0, 12); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.TransformFullPerspective(0, 1, 0, 12, // + 1, 0, 0, 33, // + 3, 2, 5, 29, // + 0, 0, 0, 12); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.transformFullPerspective(0, 1, 0, 12, // - 1, 0, 0, 33, // - 3, 2, 5, 29, // - 0, 0, 0, 12); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.TransformFullPerspective(0, 1, 0, 12, // + 1, 0, 0, 33, // + 3, 2, 5, 29, // + 0, 0, 0, 12); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2500,21 +2284,19 @@ TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) { TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transformReset(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.TransformReset(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.transformReset(); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.TransformReset(); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2522,21 +2304,19 @@ TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) { TEST_F(DisplayListTest, SkewTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.skew(10, 10); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Skew(10, 10); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.skew(10, 10); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.Skew(10, 10); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2544,21 +2324,19 @@ TEST_F(DisplayListTest, SkewTriggersDeferredSave) { TEST_F(DisplayListTest, TranslateTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.translate(10, 10); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Translate(10, 10); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.translate(10, 10); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.Translate(10, 10); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2566,21 +2344,19 @@ TEST_F(DisplayListTest, TranslateTriggersDeferredSave) { TEST_F(DisplayListTest, ScaleTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.scale(0.5, 0.5); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Scale(0.5, 0.5); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.scale(0.5, 0.5); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); + builder2.Save(); + builder2.Scale(0.5, 0.5); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2588,33 +2364,29 @@ TEST_F(DisplayListTest, ScaleTriggersDeferredSave) { TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, - true); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.ClipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, true); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, - true); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); - receiver2.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver2.drawRect({0, 0, 100, 100}); + builder2.Save(); + builder2.ClipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, true); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); + builder2.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2622,33 +2394,31 @@ TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) { TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.clipRRect(kTestRRect, ClipOp::kIntersect, true); - - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.ClipRRect(kTestRRect, ClipOp::kIntersect, true); + + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.clipRRect(kTestRRect, ClipOp::kIntersect, true); - - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); - receiver2.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver2.drawRect({0, 0, 100, 100}); + builder2.Save(); + builder2.ClipRRect(kTestRRect, ClipOp::kIntersect, true); + + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); + builder2.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2656,31 +2426,29 @@ TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) { TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.clipPath(kTestPath1, ClipOp::kIntersect, true); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.ClipPath(kTestPath1, ClipOp::kIntersect, true); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.clipPath(kTestPath1, ClipOp::kIntersect, true); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); - receiver2.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver2.drawRect({0, 0, 100, 100}); + builder2.Save(); + builder2.ClipPath(kTestPath1, ClipOp::kIntersect, true); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); + builder2.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2688,20 +2456,18 @@ TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) { TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.translate(0, 0); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Translate(0, 0); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2709,20 +2475,18 @@ TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.scale(1.0, 1.0); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Scale(1.0, 1.0); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2730,20 +2494,18 @@ TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.rotate(360); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Rotate(360); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2751,20 +2513,18 @@ TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.skew(0, 0); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Skew(0, 0); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2772,27 +2532,25 @@ TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2800,20 +2558,18 @@ TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transform2DAffine(1, 0, 0, 0, 1, 0); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.Transform2DAffine(1, 0, 0, 0, 1, 0); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2822,23 +2578,21 @@ TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2846,27 +2600,25 @@ TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.transformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - receiver1.transformReset(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.TransformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + builder1.TransformReset(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.save(); - receiver2.transformReset(); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.restore(); - receiver2.drawRect({0, 0, 100, 100}); + builder2.Save(); + builder2.TransformReset(); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.Restore(); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); @@ -2876,21 +2628,19 @@ TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPClipDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - DlOpReceiver& receiver1 = ToReceiver(builder1); - receiver1.save(); - receiver1.save(); - receiver1.clipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0), - ClipOp::kIntersect, true); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); - receiver1.drawRect({0, 0, 100, 100}); - receiver1.restore(); + builder1.Save(); + builder1.Save(); + builder1.ClipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0), + ClipOp::kIntersect, true); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); + builder1.DrawRect({0, 0, 100, 100}, DlPaint()); + builder1.Restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - DlOpReceiver& receiver2 = ToReceiver(builder2); - receiver2.drawRect({0, 0, 100, 100}); - receiver2.drawRect({0, 0, 100, 100}); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + builder2.DrawRect({0, 0, 100, 100}, DlPaint()); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2935,11 +2685,10 @@ TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) { TEST_F(DisplayListTest, RTreeRenderCulling) { DisplayListBuilder main_builder(true); - DlOpReceiver& main_receiver = ToReceiver(main_builder); - main_receiver.drawRect({0, 0, 10, 10}); - main_receiver.drawRect({20, 0, 30, 10}); - main_receiver.drawRect({0, 20, 10, 30}); - main_receiver.drawRect({20, 20, 30, 30}); + main_builder.DrawRect({0, 0, 10, 10}, DlPaint()); + main_builder.DrawRect({20, 0, 30, 10}, DlPaint()); + main_builder.DrawRect({0, 20, 10, 30}, DlPaint()); + main_builder.DrawRect({20, 20, 30, 30}, DlPaint()); auto main = main_builder.Build(); auto test = [main](SkIRect cull_rect, const sk_sp& expected) { @@ -2971,8 +2720,7 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {9, 9, 19, 19}; DisplayListBuilder expected_builder; - DlOpReceiver& expected_receiver = ToReceiver(expected_builder); - expected_receiver.drawRect({0, 0, 10, 10}); + expected_builder.DrawRect({0, 0, 10, 10}, DlPaint()); auto expected = expected_builder.Build(); test(cull_rect, expected); @@ -2982,8 +2730,7 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {11, 9, 21, 19}; DisplayListBuilder expected_builder; - DlOpReceiver& expected_receiver = ToReceiver(expected_builder); - expected_receiver.drawRect({20, 0, 30, 10}); + expected_builder.DrawRect({20, 0, 30, 10}, DlPaint()); auto expected = expected_builder.Build(); test(cull_rect, expected); @@ -2993,8 +2740,7 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {9, 11, 19, 21}; DisplayListBuilder expected_builder; - DlOpReceiver& expected_receiver = ToReceiver(expected_builder); - expected_receiver.drawRect({0, 20, 10, 30}); + expected_builder.DrawRect({0, 20, 10, 30}, DlPaint()); auto expected = expected_builder.Build(); test(cull_rect, expected); @@ -3004,8 +2750,7 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {11, 11, 21, 21}; DisplayListBuilder expected_builder; - DlOpReceiver& expected_receiver = ToReceiver(expected_builder); - expected_receiver.drawRect({20, 20, 30, 30}); + expected_builder.DrawRect({20, 20, 30, 30}, DlPaint()); auto expected = expected_builder.Build(); test(cull_rect, expected); diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc deleted file mode 100644 index b78145c124f42..0000000000000 --- a/display_list/dl_builder.cc +++ /dev/null @@ -1,1619 +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. - -#include "flutter/display_list/dl_builder.h" - -#include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_blend_mode.h" -#include "flutter/display_list/dl_op_flags.h" -#include "flutter/display_list/dl_op_records.h" -#include "flutter/display_list/effects/dl_color_source.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" -#include "fml/logging.h" -#include "third_party/skia/include/core/SkScalar.h" - -namespace flutter { - -#define DL_BUILDER_PAGE 4096 - -// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. -static void CopyV(void* dst) {} - -template -static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { - FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) - << "Expected " << dst << " to be aligned for at least " << alignof(S) - << " bytes."; - // If n is 0, there is nothing to copy into dst from src. - if (n > 0) { - memcpy(dst, src, n * sizeof(S)); - dst = reinterpret_cast(reinterpret_cast(dst) + - n * sizeof(S)); - } - // Repeat for the next items, if any - CopyV(dst, std::forward(rest)...); -} - -static constexpr inline bool is_power_of_two(int value) { - return (value & (value - 1)) == 0; -} - -template -void* DisplayListBuilder::Push(size_t pod, int render_op_inc, Args&&... args) { - size_t size = SkAlignPtr(sizeof(T) + pod); - FML_DCHECK(size < (1 << 24)); - if (used_ + size > allocated_) { - static_assert(is_power_of_two(DL_BUILDER_PAGE), - "This math needs updating for non-pow2."); - // Next greater multiple of DL_BUILDER_PAGE. - allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); - storage_.realloc(allocated_); - FML_DCHECK(storage_.get()); - memset(storage_.get() + used_, 0, allocated_ - used_); - } - FML_DCHECK(used_ + size <= allocated_); - auto op = reinterpret_cast(storage_.get() + used_); - used_ += size; - new (op) T{std::forward(args)...}; - op->type = T::kType; - op->size = size; - render_op_count_ += render_op_inc; - op_index_++; - return op + 1; -} - -sk_sp DisplayListBuilder::Build() { - while (layer_stack_.size() > 1) { - restore(); - } - - size_t bytes = used_; - int count = render_op_count_; - size_t nested_bytes = nested_bytes_; - int nested_count = nested_op_count_; - bool compatible = current_layer_->is_group_opacity_compatible(); - bool is_safe = is_ui_thread_safe_; - bool affects_transparency = current_layer_->affects_transparent_layer(); - - used_ = allocated_ = render_op_count_ = op_index_ = 0; - nested_bytes_ = nested_op_count_ = 0; - is_ui_thread_safe_ = true; - storage_.realloc(bytes); - layer_stack_.pop_back(); - layer_stack_.emplace_back(); - tracker_.reset(); - current_ = DlPaint(); - - return sk_sp(new DisplayList( - std::move(storage_), bytes, count, nested_bytes, nested_count, bounds(), - compatible, is_safe, affects_transparency, rtree())); -} - -DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, - bool prepare_rtree) - : tracker_(cull_rect, SkMatrix::I()) { - if (prepare_rtree) { - accumulator_ = std::make_unique(); - } else { - accumulator_ = std::make_unique(); - } - - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); -} - -DisplayListBuilder::~DisplayListBuilder() { - uint8_t* ptr = storage_.get(); - if (ptr) { - DisplayList::DisposeOps(ptr, ptr + used_); - } -} - -SkISize DisplayListBuilder::GetBaseLayerSize() const { - return tracker_.base_device_cull_rect().roundOut().size(); -} - -SkImageInfo DisplayListBuilder::GetImageInfo() const { - SkISize size = GetBaseLayerSize(); - return SkImageInfo::MakeUnknown(size.width(), size.height()); -} - -void DisplayListBuilder::onSetAntiAlias(bool aa) { - current_.setAntiAlias(aa); - Push(0, 0, aa); -} -void DisplayListBuilder::onSetDither(bool dither) { - current_.setDither(dither); - Push(0, 0, dither); -} -void DisplayListBuilder::onSetInvertColors(bool invert) { - current_.setInvertColors(invert); - Push(0, 0, invert); - UpdateCurrentOpacityCompatibility(); -} -void DisplayListBuilder::onSetStrokeCap(DlStrokeCap cap) { - current_.setStrokeCap(cap); - Push(0, 0, cap); -} -void DisplayListBuilder::onSetStrokeJoin(DlStrokeJoin join) { - current_.setStrokeJoin(join); - Push(0, 0, join); -} -void DisplayListBuilder::onSetDrawStyle(DlDrawStyle style) { - current_.setDrawStyle(style); - Push(0, 0, style); -} -void DisplayListBuilder::onSetStrokeWidth(float width) { - current_.setStrokeWidth(width); - Push(0, 0, width); -} -void DisplayListBuilder::onSetStrokeMiter(float limit) { - current_.setStrokeMiter(limit); - Push(0, 0, limit); -} -void DisplayListBuilder::onSetColor(DlColor color) { - current_.setColor(color); - Push(0, 0, color); -} -void DisplayListBuilder::onSetBlendMode(DlBlendMode mode) { - current_.setBlendMode(mode); - Push(0, 0, mode); - UpdateCurrentOpacityCompatibility(); -} - -void DisplayListBuilder::onSetColorSource(const DlColorSource* source) { - if (source == nullptr) { - current_.setColorSource(nullptr); - Push(0, 0); - } else { - current_.setColorSource(source->shared()); - is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe(); - switch (source->type()) { - case DlColorSourceType::kColor: { - const DlColorColorSource* color_source = source->asColor(); - current_.setColorSource(nullptr); - setColor(color_source->color()); - break; - } - case DlColorSourceType::kImage: { - const DlImageColorSource* image_source = source->asImage(); - FML_DCHECK(image_source); - Push(0, 0, image_source); - break; - } - case DlColorSourceType::kLinearGradient: { - const DlLinearGradientColorSource* linear = source->asLinearGradient(); - FML_DCHECK(linear); - void* pod = Push(linear->size(), 0); - new (pod) DlLinearGradientColorSource(linear); - break; - } - case DlColorSourceType::kRadialGradient: { - const DlRadialGradientColorSource* radial = source->asRadialGradient(); - FML_DCHECK(radial); - void* pod = Push(radial->size(), 0); - new (pod) DlRadialGradientColorSource(radial); - break; - } - case DlColorSourceType::kConicalGradient: { - const DlConicalGradientColorSource* conical = - source->asConicalGradient(); - FML_DCHECK(conical); - void* pod = Push(conical->size(), 0); - new (pod) DlConicalGradientColorSource(conical); - break; - } - case DlColorSourceType::kSweepGradient: { - const DlSweepGradientColorSource* sweep = source->asSweepGradient(); - FML_DCHECK(sweep); - void* pod = Push(sweep->size(), 0); - new (pod) DlSweepGradientColorSource(sweep); - break; - } - case DlColorSourceType::kRuntimeEffect: { - const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect(); - FML_DCHECK(effect); - Push(0, 0, effect); - break; - } -#ifdef IMPELLER_ENABLE_3D - case DlColorSourceType::kScene: { - const DlSceneColorSource* scene = source->asScene(); - FML_DCHECK(scene); - Push(0, 0, scene); - break; - } -#endif // IMPELLER_ENABLE_3D - } - } -} -void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) { - if (filter == nullptr) { - current_.setImageFilter(nullptr); - Push(0, 0); - } else { - current_.setImageFilter(filter->shared()); - switch (filter->type()) { - case DlImageFilterType::kBlur: { - const DlBlurImageFilter* blur_filter = filter->asBlur(); - FML_DCHECK(blur_filter); - void* pod = Push(blur_filter->size(), 0); - new (pod) DlBlurImageFilter(blur_filter); - break; - } - case DlImageFilterType::kDilate: { - const DlDilateImageFilter* dilate_filter = filter->asDilate(); - FML_DCHECK(dilate_filter); - void* pod = Push(dilate_filter->size(), 0); - new (pod) DlDilateImageFilter(dilate_filter); - break; - } - case DlImageFilterType::kErode: { - const DlErodeImageFilter* erode_filter = filter->asErode(); - FML_DCHECK(erode_filter); - void* pod = Push(erode_filter->size(), 0); - new (pod) DlErodeImageFilter(erode_filter); - break; - } - case DlImageFilterType::kMatrix: { - const DlMatrixImageFilter* matrix_filter = filter->asMatrix(); - FML_DCHECK(matrix_filter); - void* pod = Push(matrix_filter->size(), 0); - new (pod) DlMatrixImageFilter(matrix_filter); - break; - } - case DlImageFilterType::kCompose: - case DlImageFilterType::kLocalMatrix: - case DlImageFilterType::kColorFilter: { - Push(0, 0, filter); - break; - } - } - } -} -void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) { - if (filter == nullptr) { - current_.setColorFilter(nullptr); - Push(0, 0); - } else { - current_.setColorFilter(filter->shared()); - switch (filter->type()) { - case DlColorFilterType::kBlend: { - const DlBlendColorFilter* blend_filter = filter->asBlend(); - FML_DCHECK(blend_filter); - void* pod = Push(blend_filter->size(), 0); - new (pod) DlBlendColorFilter(blend_filter); - break; - } - case DlColorFilterType::kMatrix: { - const DlMatrixColorFilter* matrix_filter = filter->asMatrix(); - FML_DCHECK(matrix_filter); - void* pod = Push(matrix_filter->size(), 0); - new (pod) DlMatrixColorFilter(matrix_filter); - break; - } - case DlColorFilterType::kSrgbToLinearGamma: { - void* pod = Push(filter->size(), 0); - new (pod) DlSrgbToLinearGammaColorFilter(); - break; - } - case DlColorFilterType::kLinearToSrgbGamma: { - void* pod = Push(filter->size(), 0); - new (pod) DlLinearToSrgbGammaColorFilter(); - break; - } - } - } - UpdateCurrentOpacityCompatibility(); -} -void DisplayListBuilder::onSetPathEffect(const DlPathEffect* effect) { - if (effect == nullptr) { - current_.setPathEffect(nullptr); - Push(0, 0); - } else { - current_.setPathEffect(effect->shared()); - switch (effect->type()) { - case DlPathEffectType::kDash: { - const DlDashPathEffect* dash_effect = effect->asDash(); - void* pod = Push(dash_effect->size(), 0); - new (pod) DlDashPathEffect(dash_effect); - break; - } - } - } -} -void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) { - if (filter == nullptr) { - current_.setMaskFilter(nullptr); - Push(0, 0); - } else { - current_.setMaskFilter(filter->shared()); - switch (filter->type()) { - case DlMaskFilterType::kBlur: { - const DlBlurMaskFilter* blur_filter = filter->asBlur(); - FML_DCHECK(blur_filter); - void* pod = Push(blur_filter->size(), 0); - new (pod) DlBlurMaskFilter(blur_filter); - break; - } - } - } -} - -void DisplayListBuilder::SetAttributesFromPaint( - const DlPaint& paint, - const DisplayListAttributeFlags flags) { - if (flags.applies_anti_alias()) { - setAntiAlias(paint.isAntiAlias()); - } - if (flags.applies_dither()) { - setDither(paint.isDither()); - } - if (flags.applies_alpha_or_color()) { - setColor(paint.getColor().argb); - } - if (flags.applies_blend()) { - setBlendMode(paint.getBlendMode()); - } - if (flags.applies_style()) { - setDrawStyle(paint.getDrawStyle()); - } - if (flags.is_stroked(paint.getDrawStyle())) { - setStrokeWidth(paint.getStrokeWidth()); - setStrokeMiter(paint.getStrokeMiter()); - setStrokeCap(paint.getStrokeCap()); - setStrokeJoin(paint.getStrokeJoin()); - } - if (flags.applies_shader()) { - setColorSource(paint.getColorSource().get()); - } - if (flags.applies_color_filter()) { - setInvertColors(paint.isInvertColors()); - setColorFilter(paint.getColorFilter().get()); - } - if (flags.applies_image_filter()) { - setImageFilter(paint.getImageFilter().get()); - } - if (flags.applies_path_effect()) { - setPathEffect(paint.getPathEffect().get()); - } - if (flags.applies_mask_filter()) { - setMaskFilter(paint.getMaskFilter().get()); - } -} - -void DisplayListBuilder::checkForDeferredSave() { - if (current_layer_->has_deferred_save_op_) { - size_t save_offset_ = used_; - Push(0, 1); - current_layer_->save_offset_ = save_offset_; - current_layer_->has_deferred_save_op_ = false; - } -} - -void DisplayListBuilder::Save() { - bool is_nop = current_layer_->is_nop_; - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); - current_layer_->has_deferred_save_op_ = true; - current_layer_->is_nop_ = is_nop; - tracker_.save(); - accumulator()->save(); -} - -void DisplayListBuilder::Restore() { - if (layer_stack_.size() > 1) { - SaveOpBase* op = reinterpret_cast( - storage_.get() + current_layer_->save_offset()); - if (!current_layer_->has_deferred_save_op_) { - op->restore_index = op_index_; - Push(0, 1); - } - // Grab the current layer info before we push the restore - // on the stack. - LayerInfo layer_info = layer_stack_.back(); - - tracker_.restore(); - layer_stack_.pop_back(); - current_layer_ = &layer_stack_.back(); - bool is_unbounded = layer_info.is_unbounded(); - - // Before we pop_back we will get the current layer bounds from the - // current accumulator and adjust it as required based on the filter. - std::shared_ptr filter = layer_info.filter(); - if (filter) { - const SkRect clip = tracker_.device_cull_rect(); - if (!accumulator()->restore( - [filter = filter, matrix = GetTransform()](const SkRect& input, - SkRect& output) { - SkIRect output_bounds; - bool ret = filter->map_device_bounds(input.roundOut(), matrix, - output_bounds); - output.set(output_bounds); - return ret; - }, - &clip)) { - is_unbounded = true; - } - } else { - accumulator()->restore(); - } - - if (is_unbounded) { - AccumulateUnbounded(); - } - - if (layer_info.has_layer()) { - // Layers are never deferred for now, we need to update the - // following code if we ever do saveLayer culling... - FML_DCHECK(!layer_info.has_deferred_save_op_); - if (layer_info.is_group_opacity_compatible()) { - // We are now going to go back and modify the matching saveLayer - // call to add the option indicating it can distribute an opacity - // value to its children. - // - // Note that this operation cannot and does not change the size - // or structure of the SaveLayerOp record. It only sets an option - // flag on an existing field. - // - // Note that these kinds of modification operations on data already - // in the DisplayList are only allowed *during* the build phase. - // Once built, the DisplayList records must remain read only to - // ensure consistency of rendering and |Equals()| behavior. - op->options = op->options.with_can_distribute_opacity(); - } - } else { - // For regular save() ops there was no protecting layer so we have to - // accumulate the values into the enclosing layer. - if (layer_info.cannot_inherit_opacity()) { - current_layer_->mark_incompatible(); - } else if (layer_info.has_compatible_op()) { - current_layer_->add_compatible_op(); - } - } - } -} -void DisplayListBuilder::RestoreToCount(int restore_count) { - FML_DCHECK(restore_count <= GetSaveCount()); - while (restore_count < GetSaveCount() && GetSaveCount() > 1) { - restore(); - } -} -void DisplayListBuilder::saveLayer(const SkRect* bounds, - const SaveLayerOptions in_options, - const DlImageFilter* backdrop) { - SaveLayerOptions options = in_options.without_optimizations(); - DisplayListAttributeFlags flags = options.renders_with_attributes() - ? kSaveLayerWithPaintFlags - : kSaveLayerFlags; - OpResult result = PaintResult(current_, flags); - if (result == OpResult::kNoEffect) { - save(); - current_layer_->is_nop_ = true; - return; - } - size_t save_layer_offset = used_; - if (options.renders_with_attributes()) { - // The actual flood of the outer layer clip will occur after the - // (eventual) corresponding restore is called, but rather than - // remember this information in the LayerInfo until the restore - // method is processed, we just mark the unbounded state up front. - // Another reason to accumulate the clip here rather than in - // restore is so that this savelayer will be tagged in the rtree - // with its full bounds and the right op_index so that it doesn't - // get culled during rendering. - if (!paint_nops_on_transparency()) { - // We will fill the clip of the outer layer when we restore. - // Accumulate should always return true here because if the - // clip was empty then that would have been caught up above - // when we tested the PaintResult. - [[maybe_unused]] bool unclipped = AccumulateUnbounded(); - FML_DCHECK(unclipped); - } - CheckLayerOpacityCompatibility(true); - layer_stack_.emplace_back(save_layer_offset, true, - current_.getImageFilter()); - } else { - CheckLayerOpacityCompatibility(false); - layer_stack_.emplace_back(save_layer_offset, true, nullptr); - } - current_layer_ = &layer_stack_.back(); - - tracker_.save(); - accumulator()->save(); - - if (backdrop) { - // A backdrop will affect up to the entire surface, bounded by the clip - // Accumulate should always return true here because if the - // clip was empty then that would have been caught up above - // when we tested the PaintResult. - [[maybe_unused]] bool unclipped = AccumulateUnbounded(); - FML_DCHECK(unclipped); - bounds // - ? Push(0, 1, options, *bounds, backdrop) - : Push(0, 1, options, backdrop); - } else { - bounds // - ? Push(0, 1, options, *bounds) - : Push(0, 1, options); - } - - if (options.renders_with_attributes()) { - // |current_opacity_compatibility_| does not take an ImageFilter into - // account because an individual primitive with an ImageFilter can apply - // opacity on top of it. But, if the layer is applying the ImageFilter - // then it cannot pass the opacity on. - if (!current_opacity_compatibility_ || - current_.getImageFilter() != nullptr) { - UpdateLayerOpacityCompatibility(false); - } - } - UpdateLayerResult(result); - - if (options.renders_with_attributes() && current_.getImageFilter()) { - // We use |resetCullRect| here because we will be accumulating bounds of - // primitives before applying the filter to those bounds. We might - // encounter a primitive whose bounds are clipped, but whose filtered - // bounds will not be clipped. If the individual rendering ops bounds - // are clipped, it will not contribute to the overall bounds which - // could lead to inaccurate (subset) bounds of the DisplayList. - // We need to reset the cull rect here to avoid this premature clipping. - // The filtered bounds will be clipped to the existing clip rect when - // this layer is restored. - // If bounds is null then the original cull_rect will be used. - tracker_.resetCullRect(bounds); - } else if (bounds) { - // Even though Skia claims that the bounds are only a hint, they actually - // use them as the temporary layer bounds during rendering the layer, so - // we set them as if a clip operation were performed. - tracker_.clipRect(*bounds, ClipOp::kIntersect, false); - } -} -void DisplayListBuilder::SaveLayer(const SkRect* bounds, - const DlPaint* paint, - const DlImageFilter* backdrop) { - if (paint != nullptr) { - SetAttributesFromPaint(*paint, - DisplayListOpFlags::kSaveLayerWithPaintFlags); - saveLayer(bounds, SaveLayerOptions::kWithAttributes, backdrop); - } else { - saveLayer(bounds, SaveLayerOptions::kNoAttributes, backdrop); - } -} - -void DisplayListBuilder::Translate(SkScalar tx, SkScalar ty) { - if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && - (tx != 0.0 || ty != 0.0)) { - checkForDeferredSave(); - Push(0, 1, tx, ty); - tracker_.translate(tx, ty); - } -} -void DisplayListBuilder::Scale(SkScalar sx, SkScalar sy) { - if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && - (sx != 1.0 || sy != 1.0)) { - checkForDeferredSave(); - Push(0, 1, sx, sy); - tracker_.scale(sx, sy); - } -} -void DisplayListBuilder::Rotate(SkScalar degrees) { - if (SkScalarMod(degrees, 360.0) != 0.0) { - checkForDeferredSave(); - Push(0, 1, degrees); - tracker_.rotate(degrees); - } -} -void DisplayListBuilder::Skew(SkScalar sx, SkScalar sy) { - if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && - (sx != 0.0 || sy != 0.0)) { - checkForDeferredSave(); - Push(0, 1, sx, sy); - tracker_.skew(sx, sy); - } -} - -// clang-format off - -// 2x3 2D affine subset of a 4x4 transform in row major order -void DisplayListBuilder::Transform2DAffine( - SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) { - if (SkScalarsAreFinite(mxx, myx) && - SkScalarsAreFinite(mxy, myy) && - SkScalarsAreFinite(mxt, myt)) { - if (mxx == 1 && mxy == 0 && - myx == 0 && myy == 1) { - Translate(mxt, myt); - } else { - checkForDeferredSave(); - Push(0, 1, - mxx, mxy, mxt, - myx, myy, myt); - tracker_.transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - } - } -} -// full 4x4 transform in row major order -void DisplayListBuilder::TransformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { - if ( mxz == 0 && - myz == 0 && - mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 && - mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { - Transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && - SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && - SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && - SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { - checkForDeferredSave(); - Push(0, 1, - mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - tracker_.transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - } -} -// clang-format on -void DisplayListBuilder::TransformReset() { - checkForDeferredSave(); - Push(0, 0); - tracker_.setIdentity(); -} -void DisplayListBuilder::Transform(const SkMatrix* matrix) { - if (matrix != nullptr) { - Transform(SkM44(*matrix)); - } -} -void DisplayListBuilder::Transform(const SkM44* m44) { - if (m44 != nullptr) { - transformFullPerspective( - m44->rc(0, 0), m44->rc(0, 1), m44->rc(0, 2), m44->rc(0, 3), - m44->rc(1, 0), m44->rc(1, 1), m44->rc(1, 2), m44->rc(1, 3), - m44->rc(2, 0), m44->rc(2, 1), m44->rc(2, 2), m44->rc(2, 3), - m44->rc(3, 0), m44->rc(3, 1), m44->rc(3, 2), m44->rc(3, 3)); - } -} - -void DisplayListBuilder::ClipRect(const SkRect& rect, - ClipOp clip_op, - bool is_aa) { - if (!rect.isFinite()) { - return; - } - tracker_.clipRect(rect, clip_op, is_aa); - if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { - current_layer_->is_nop_ = true; - return; - } - checkForDeferredSave(); - switch (clip_op) { - case ClipOp::kIntersect: - Push(0, 1, rect, is_aa); - break; - case ClipOp::kDifference: - Push(0, 1, rect, is_aa); - break; - } -} -void DisplayListBuilder::ClipRRect(const SkRRect& rrect, - ClipOp clip_op, - bool is_aa) { - if (rrect.isRect()) { - clipRect(rrect.rect(), clip_op, is_aa); - } else { - tracker_.clipRRect(rrect, clip_op, is_aa); - if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { - current_layer_->is_nop_ = true; - return; - } - checkForDeferredSave(); - switch (clip_op) { - case ClipOp::kIntersect: - Push(0, 1, rrect, is_aa); - break; - case ClipOp::kDifference: - Push(0, 1, rrect, is_aa); - break; - } - } -} -void DisplayListBuilder::ClipPath(const SkPath& path, - ClipOp clip_op, - bool is_aa) { - if (!path.isInverseFillType()) { - SkRect rect; - if (path.isRect(&rect)) { - this->clipRect(rect, clip_op, is_aa); - return; - } - SkRRect rrect; - if (path.isOval(&rect)) { - rrect.setOval(rect); - this->clipRRect(rrect, clip_op, is_aa); - return; - } - if (path.isRRect(&rrect)) { - this->clipRRect(rrect, clip_op, is_aa); - return; - } - } - tracker_.clipPath(path, clip_op, is_aa); - if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { - current_layer_->is_nop_ = true; - return; - } - checkForDeferredSave(); - switch (clip_op) { - case ClipOp::kIntersect: - Push(0, 1, path, is_aa); - break; - case ClipOp::kDifference: - Push(0, 1, path, is_aa); - break; - } -} - -bool DisplayListBuilder::QuickReject(const SkRect& bounds) const { - return tracker_.content_culled(bounds); -} - -void DisplayListBuilder::drawPaint() { - OpResult result = PaintResult(current_, kDrawPaintFlags); - if (result != OpResult::kNoEffect && AccumulateUnbounded()) { - Push(0, 1); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawPaint(const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags); - drawPaint(); -} -void DisplayListBuilder::DrawColor(DlColor color, DlBlendMode mode) { - OpResult result = PaintResult(DlPaint(color).setBlendMode(mode)); - if (result != OpResult::kNoEffect && AccumulateUnbounded()) { - Push(0, 1, color, mode); - CheckLayerOpacityCompatibility(mode); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { - SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); - DisplayListAttributeFlags flags = - (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags - : kDrawHVLineFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) { - Push(0, 1, p0, p1); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawLine(const SkPoint& p0, - const SkPoint& p1, - const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags); - drawLine(p0, p1); -} -void DisplayListBuilder::drawRect(const SkRect& rect) { - DisplayListAttributeFlags flags = kDrawRectFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(rect.makeSorted(), flags)) { - Push(0, 1, rect); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawRect(const SkRect& rect, const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags); - drawRect(rect); -} -void DisplayListBuilder::drawOval(const SkRect& bounds) { - DisplayListAttributeFlags flags = kDrawOvalFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(bounds.makeSorted(), flags)) { - Push(0, 1, bounds); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawOval(const SkRect& bounds, const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags); - drawOval(bounds); -} -void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { - DisplayListAttributeFlags flags = kDrawCircleFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect) { - SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius, - center.fX + radius, center.fY + radius); - if (AccumulateOpBounds(bounds, flags)) { - Push(0, 1, center, radius); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } - } -} -void DisplayListBuilder::DrawCircle(const SkPoint& center, - SkScalar radius, - const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags); - drawCircle(center, radius); -} -void DisplayListBuilder::drawRRect(const SkRRect& rrect) { - if (rrect.isRect()) { - drawRect(rrect.rect()); - } else if (rrect.isOval()) { - drawOval(rrect.rect()); - } else { - DisplayListAttributeFlags flags = kDrawRRectFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(rrect.getBounds(), flags)) { - Push(0, 1, rrect); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } - } -} -void DisplayListBuilder::DrawRRect(const SkRRect& rrect, const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags); - drawRRect(rrect); -} -void DisplayListBuilder::drawDRRect(const SkRRect& outer, - const SkRRect& inner) { - DisplayListAttributeFlags flags = kDrawDRRectFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(outer.getBounds(), flags)) { - Push(0, 1, outer, inner); - CheckLayerOpacityCompatibility(); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags); - drawDRRect(outer, inner); -} -void DisplayListBuilder::drawPath(const SkPath& path) { - DisplayListAttributeFlags flags = kDrawPathFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect) { - bool is_visible = path.isInverseFillType() - ? AccumulateUnbounded() - : AccumulateOpBounds(path.getBounds(), flags); - if (is_visible) { - Push(0, 1, path); - CheckLayerOpacityHairlineCompatibility(); - UpdateLayerResult(result); - } - } -} -void DisplayListBuilder::DrawPath(const SkPath& path, const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPathFlags); - drawPath(path); -} - -void DisplayListBuilder::drawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter) { - DisplayListAttributeFlags flags = // - useCenter // - ? kDrawArcWithCenterFlags - : kDrawArcNoCenterFlags; - OpResult result = PaintResult(current_, flags); - // This could be tighter if we compute where the start and end - // angles are and then also consider the quadrants swept and - // the center if specified. - if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) { - Push(0, 1, bounds, start, sweep, useCenter); - if (useCenter) { - CheckLayerOpacityHairlineCompatibility(); - } else { - CheckLayerOpacityCompatibility(); - } - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter, - const DlPaint& paint) { - SetAttributesFromPaint( - paint, useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags); - drawArc(bounds, start, sweep, useCenter); -} - -DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode( - PointMode mode) { - switch (mode) { - case DlCanvas::PointMode::kPoints: - return kDrawPointsAsPointsFlags; - case PointMode::kLines: - return kDrawPointsAsLinesFlags; - case PointMode::kPolygon: - return kDrawPointsAsPolygonFlags; - } - FML_UNREACHABLE(); -} -void DisplayListBuilder::drawPoints(PointMode mode, - uint32_t count, - const SkPoint pts[]) { - if (count == 0) { - return; - } - DisplayListAttributeFlags flags = FlagsForPointMode(mode); - OpResult result = PaintResult(current_, flags); - if (result == OpResult::kNoEffect) { - return; - } - - FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); - int bytes = count * sizeof(SkPoint); - RectBoundsAccumulator ptBounds; - for (size_t i = 0; i < count; i++) { - ptBounds.accumulate(pts[i]); - } - SkRect point_bounds = ptBounds.bounds(); - if (!AccumulateOpBounds(point_bounds, flags)) { - return; - } - - void* data_ptr; - switch (mode) { - case PointMode::kPoints: - data_ptr = Push(bytes, 1, count); - break; - case PointMode::kLines: - data_ptr = Push(bytes, 1, count); - break; - case PointMode::kPolygon: - data_ptr = Push(bytes, 1, count); - break; - default: - FML_UNREACHABLE(); - return; - } - CopyV(data_ptr, pts, count); - // drawPoints treats every point or line (or segment of a polygon) - // as a completely separate operation meaning we cannot ensure - // distribution of group opacity without analyzing the mode and the - // bounds of every sub-primitive. - // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c - UpdateLayerOpacityCompatibility(false); - UpdateLayerResult(result); -} -void DisplayListBuilder::DrawPoints(PointMode mode, - uint32_t count, - const SkPoint pts[], - const DlPaint& paint) { - SetAttributesFromPaint(paint, FlagsForPointMode(mode)); - drawPoints(mode, count, pts); -} -void DisplayListBuilder::drawVertices(const DlVertices* vertices, - DlBlendMode mode) { - DisplayListAttributeFlags flags = kDrawVerticesFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(vertices->bounds(), flags)) { - void* pod = Push(vertices->size(), 1, mode); - new (pod) DlVertices(vertices); - // DrawVertices applies its colors to the paint so we have no way - // of controlling opacity using the current paint attributes. - // Although, examination of the |mode| might find some predictable - // cases. - UpdateLayerOpacityCompatibility(false); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawVertices(const DlVertices* vertices, - DlBlendMode mode, - const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawVerticesFlags); - drawVertices(vertices, mode); -} - -void DisplayListBuilder::drawImage(const sk_sp image, - const SkPoint point, - DlImageSampling sampling, - bool render_with_attributes) { - DisplayListAttributeFlags flags = render_with_attributes // - ? kDrawImageWithPaintFlags - : kDrawImageFlags; - OpResult result = PaintResult(current_, flags); - if (result == OpResult::kNoEffect) { - return; - } - SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, // - image->width(), image->height()); - if (AccumulateOpBounds(bounds, flags)) { - render_with_attributes - ? Push(0, 1, image, point, sampling) - : Push(0, 1, image, point, sampling); - CheckLayerOpacityCompatibility(render_with_attributes); - UpdateLayerResult(result); - is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); - } -} -void DisplayListBuilder::DrawImage(const sk_sp& image, - const SkPoint point, - DlImageSampling sampling, - const DlPaint* paint) { - if (paint != nullptr) { - SetAttributesFromPaint(*paint, - DisplayListOpFlags::kDrawImageWithPaintFlags); - drawImage(image, point, sampling, true); - } else { - drawImage(image, point, sampling, false); - } -} -void DisplayListBuilder::drawImageRect(const sk_sp image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - bool render_with_attributes, - SrcRectConstraint constraint) { - DisplayListAttributeFlags flags = render_with_attributes - ? kDrawImageRectWithPaintFlags - : kDrawImageRectFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) { - Push(0, 1, image, src, dst, sampling, - render_with_attributes, constraint); - CheckLayerOpacityCompatibility(render_with_attributes); - UpdateLayerResult(result); - is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); - } -} -void DisplayListBuilder::DrawImageRect(const sk_sp& image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - const DlPaint* paint, - SrcRectConstraint constraint) { - if (paint != nullptr) { - SetAttributesFromPaint(*paint, - DisplayListOpFlags::kDrawImageRectWithPaintFlags); - drawImageRect(image, src, dst, sampling, true, constraint); - } else { - drawImageRect(image, src, dst, sampling, false, constraint); - } -} -void DisplayListBuilder::drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - bool render_with_attributes) { - DisplayListAttributeFlags flags = render_with_attributes - ? kDrawImageNineWithPaintFlags - : kDrawImageNineFlags; - OpResult result = PaintResult(current_, flags); - if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) { - render_with_attributes - ? Push(0, 1, image, center, dst, filter) - : Push(0, 1, image, center, dst, filter); - CheckLayerOpacityCompatibility(render_with_attributes); - UpdateLayerResult(result); - is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); - } -} -void DisplayListBuilder::DrawImageNine(const sk_sp& image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - const DlPaint* paint) { - if (paint != nullptr) { - SetAttributesFromPaint(*paint, - DisplayListOpFlags::kDrawImageNineWithPaintFlags); - drawImageNine(image, center, dst, filter, true); - } else { - drawImageNine(image, center, dst, filter, false); - } -} -void DisplayListBuilder::drawAtlas(const sk_sp atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cull_rect, - bool render_with_attributes) { - DisplayListAttributeFlags flags = render_with_attributes // - ? kDrawAtlasWithPaintFlags - : kDrawAtlasFlags; - OpResult result = PaintResult(current_, flags); - if (result == OpResult::kNoEffect) { - return; - } - SkPoint quad[4]; - RectBoundsAccumulator atlasBounds; - for (int i = 0; i < count; i++) { - const SkRect& src = tex[i]; - xform[i].toQuad(src.width(), src.height(), quad); - for (int j = 0; j < 4; j++) { - atlasBounds.accumulate(quad[j]); - } - } - if (atlasBounds.is_empty() || - !AccumulateOpBounds(atlasBounds.bounds(), flags)) { - return; - } - - int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); - void* data_ptr; - if (colors != nullptr) { - bytes += count * sizeof(DlColor); - if (cull_rect != nullptr) { - data_ptr = - Push(bytes, 1, atlas, count, mode, sampling, true, - *cull_rect, render_with_attributes); - } else { - data_ptr = Push(bytes, 1, atlas, count, mode, sampling, true, - render_with_attributes); - } - CopyV(data_ptr, xform, count, tex, count, colors, count); - } else { - if (cull_rect != nullptr) { - data_ptr = - Push(bytes, 1, atlas, count, mode, sampling, false, - *cull_rect, render_with_attributes); - } else { - data_ptr = Push(bytes, 1, atlas, count, mode, sampling, - false, render_with_attributes); - } - CopyV(data_ptr, xform, count, tex, count); - } - // drawAtlas treats each image as a separate operation so we cannot rely - // on it to distribute the opacity without overlap without checking all - // of the transforms and texture rectangles. - UpdateLayerOpacityCompatibility(false); - UpdateLayerResult(result); - is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe(); -} -void DisplayListBuilder::DrawAtlas(const sk_sp& atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cull_rect, - const DlPaint* paint) { - if (paint != nullptr) { - SetAttributesFromPaint(*paint, - DisplayListOpFlags::kDrawAtlasWithPaintFlags); - drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect, - true); - } else { - drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect, - false); - } -} - -void DisplayListBuilder::DrawDisplayList(const sk_sp display_list, - SkScalar opacity) { - if (!SkScalarIsFinite(opacity) || opacity <= SK_ScalarNearlyZero || - display_list->op_count() == 0 || display_list->bounds().isEmpty() || - current_layer_->is_nop_) { - return; - } - const SkRect bounds = display_list->bounds(); - bool accumulated; - switch (accumulator()->type()) { - case BoundsAccumulatorType::kRect: - accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); - break; - case BoundsAccumulatorType::kRTree: - auto rtree = display_list->rtree(); - if (rtree) { - std::list rects = - rtree->searchAndConsolidateRects(bounds, false); - accumulated = false; - for (const SkRect& rect : rects) { - // TODO (https://github.com/flutter/flutter/issues/114919): Attributes - // are not necessarily `kDrawDisplayListFlags`. - if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) { - accumulated = true; - } - } - } else { - accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); - } - break; - } - if (!accumulated) { - return; - } - - DlPaint current_paint = current_; - Push(0, 1, display_list, - opacity < SK_Scalar1 ? opacity : SK_Scalar1); - is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe(); - // Not really necessary if the developer is interacting with us via - // our attribute-state-less DlCanvas methods, but this avoids surprises - // for those who may have been using the stateful Dispatcher methods. - SetAttributesFromPaint(current_paint, - DisplayListOpFlags::kSaveLayerWithPaintFlags); - - // The non-nested op count accumulated in the |Push| method will include - // this call to |drawDisplayList| for non-nested op count metrics. - // But, for nested op count metrics we want the |drawDisplayList| call itself - // to be transparent. So we subtract 1 from our accumulated nested count to - // balance out against the 1 that was accumulated into the regular count. - // This behavior is identical to the way SkPicture computed nested op counts. - nested_op_count_ += display_list->op_count(true) - 1; - nested_bytes_ += display_list->bytes(true); - UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity()); - // Nop DisplayLists are eliminated above so we either affect transparent - // pixels or we do not. We should not have [kNoEffect]. - UpdateLayerResult(display_list->modifies_transparent_black() - ? OpResult::kAffectsAll - : OpResult::kPreservesTransparency); -} -void DisplayListBuilder::drawTextBlob(const sk_sp blob, - SkScalar x, - SkScalar y) { - DisplayListAttributeFlags flags = kDrawTextBlobFlags; - OpResult result = PaintResult(current_, flags); - if (result == OpResult::kNoEffect) { - return; - } - bool unclipped = AccumulateOpBounds(blob->bounds().makeOffset(x, y), flags); - // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the - // unit tests can use Fuchsia's font manager instead of the empty default. - // Until then we might encounter empty bounds for otherwise valid text and - // thus we ignore the results from AccumulateOpBounds. -#if defined(OS_FUCHSIA) - unclipped = true; -#endif // OS_FUCHSIA - if (unclipped) { - Push(0, 1, blob, x, y); - // There is no way to query if the glyphs of a text blob overlap and - // there are no current guarantees from either Skia or Impeller that - // they will protect overlapping glyphs from the effects of overdraw - // so we must make the conservative assessment that this DL layer is - // not compatible with group opacity inheritance. - UpdateLayerOpacityCompatibility(false); - UpdateLayerResult(result); - } -} -void DisplayListBuilder::DrawTextBlob(const sk_sp& blob, - SkScalar x, - SkScalar y, - const DlPaint& paint) { - SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags); - drawTextBlob(blob, x, y); -} -void DisplayListBuilder::DrawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) { - OpResult result = PaintResult(DlPaint(color)); - if (result != OpResult::kNoEffect) { - SkRect shadow_bounds = - DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform()); - if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) { - transparent_occluder // - ? Push(0, 1, path, color, elevation, - dpr) - : Push(0, 1, path, color, elevation, dpr); - UpdateLayerOpacityCompatibility(false); - UpdateLayerResult(result); - } - } -} - -bool DisplayListBuilder::ComputeFilteredBounds(SkRect& bounds, - const DlImageFilter* filter) { - if (filter) { - if (!filter->map_local_bounds(bounds, bounds)) { - return false; - } - } - return true; -} - -bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds, - DisplayListAttributeFlags flags) { - if (flags.ignores_paint()) { - return true; - } - - if (flags.is_geometric()) { - bool is_stroked = flags.is_stroked(current_.getDrawStyle()); - - // Path effect occurs before stroking... - DisplayListSpecialGeometryFlags special_flags = - flags.WithPathEffect(current_.getPathEffectPtr(), is_stroked); - if (current_.getPathEffect()) { - auto effect_bounds = current_.getPathEffect()->effect_bounds(bounds); - if (!effect_bounds.has_value()) { - return false; - } - bounds = effect_bounds.value(); - } - - if (is_stroked) { - // Determine the max multiplier to the stroke width first. - SkScalar pad = 1.0f; - if (current_.getStrokeJoin() == DlStrokeJoin::kMiter && - special_flags.may_have_acute_joins()) { - pad = std::max(pad, current_.getStrokeMiter()); - } - if (current_.getStrokeCap() == DlStrokeCap::kSquare && - special_flags.may_have_diagonal_caps()) { - pad = std::max(pad, SK_ScalarSqrt2); - } - SkScalar min_stroke_width = 0.01; - pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width); - bounds.outset(pad, pad); - } - } - - if (flags.applies_mask_filter()) { - auto filter = current_.getMaskFilter(); - if (filter) { - switch (filter->type()) { - case DlMaskFilterType::kBlur: { - FML_DCHECK(filter->asBlur()); - SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0; - bounds.outset(mask_sigma_pad, mask_sigma_pad); - } - } - } - } - - if (flags.applies_image_filter()) { - return ComputeFilteredBounds(bounds, current_.getImageFilter().get()); - } - - return true; -} - -bool DisplayListBuilder::AccumulateUnbounded() { - SkRect clip = tracker_.device_cull_rect(); - if (clip.isEmpty()) { - return false; - } - accumulator()->accumulate(clip, op_index_); - return true; -} - -bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds, - DisplayListAttributeFlags flags) { - if (AdjustBoundsForPaint(bounds, flags)) { - return AccumulateBounds(bounds); - } else { - return AccumulateUnbounded(); - } -} -bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) { - if (!bounds.isEmpty()) { - tracker_.mapRect(&bounds); - if (bounds.intersect(tracker_.device_cull_rect())) { - accumulator()->accumulate(bounds, op_index_); - return true; - } - } - return false; -} - -bool DisplayListBuilder::paint_nops_on_transparency() { - // SkImageFilter::canComputeFastBounds tests for transparency behavior - // This test assumes that the blend mode checked down below will - // NOP on transparent black. - if (current_.getImageFilterPtr() && - current_.getImageFilterPtr()->modifies_transparent_black()) { - return false; - } - - // We filter the transparent black that is used for the background of a - // saveLayer and make sure it returns transparent black. If it does, then - // the color filter will leave all area surrounding the contents of the - // save layer untouched out to the edge of the output surface. - // This test assumes that the blend mode checked down below will - // NOP on transparent black. - if (current_.getColorFilterPtr() && - current_.getColorFilterPtr()->modifies_transparent_black()) { - return false; - } - - // Unusual blendmodes require us to process a saved layer - // even with operations outside the clip. - // For example, DstIn is used by masking layers. - // https://code.google.com/p/skia/issues/detail?id=1291 - // https://crbug.com/401593 - switch (current_.getBlendMode()) { - // For each of the following transfer modes, if the source - // alpha is zero (our transparent black), the resulting - // blended pixel is not necessarily equal to the original - // destination pixel. - // Mathematically, any time in the following equations where - // the result is not d assuming source is 0 - case DlBlendMode::kClear: // r = 0 - case DlBlendMode::kSrc: // r = s - case DlBlendMode::kSrcIn: // r = s * da - case DlBlendMode::kDstIn: // r = d * sa - case DlBlendMode::kSrcOut: // r = s * (1-da) - case DlBlendMode::kDstATop: // r = d*sa + s*(1-da) - case DlBlendMode::kModulate: // r = s*d - return false; - break; - - // And in these equations, the result must be d if the - // source is 0 - case DlBlendMode::kDst: // r = d - case DlBlendMode::kSrcOver: // r = s + (1-sa)*d - case DlBlendMode::kDstOver: // r = d + (1-da)*s - case DlBlendMode::kDstOut: // r = d * (1-sa) - case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa) - case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa) - case DlBlendMode::kPlus: // r = min(s + d, 1) - case DlBlendMode::kScreen: // r = s + d - s*d - case DlBlendMode::kOverlay: // multiply or screen, depending on dest - case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa), - // ra = kSrcOver - case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa), - // ra = kSrcOver - case DlBlendMode::kColorDodge: // brighten destination to reflect source - case DlBlendMode::kColorBurn: // darken destination to reflect source - case DlBlendMode::kHardLight: // multiply or screen, depending on source - case DlBlendMode::kSoftLight: // lighten or darken, depending on source - case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)), - // ra = kSrcOver - case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver - case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d - case DlBlendMode::kHue: // ra = kSrcOver - case DlBlendMode::kSaturation: // ra = kSrcOver - case DlBlendMode::kColor: // ra = kSrcOver - case DlBlendMode::kLuminosity: // ra = kSrcOver - return true; - break; - } -} - -DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint, - DisplayListAttributeFlags flags) { - DlColor color; - if (flags.applies_color()) { - const DlColorSource* source = paint.getColorSourcePtr(); - if (source) { - if (source->asColor()) { - color = source->asColor()->color(); - } else { - color = source->is_opaque() ? DlColor::kBlack() : kAnyColor; - } - } else { - color = paint.getColor(); - } - } else if (flags.applies_alpha()) { - // If the operation applies alpha, but not color, then the only impact - // of the alpha is to modulate the output towards transparency. - // We can not guarantee an opaque source even if the alpha is opaque - // since that would require knowing something about the colors that - // the alpha is modulating, but we can guarantee a transparent source - // if the alpha is 0. - color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor; - } else { - color = kAnyColor; - } - if (flags.applies_image_filter()) { - auto filter = paint.getImageFilterPtr(); - if (filter) { - if (!color.isTransparent() || filter->modifies_transparent_black()) { - color = kAnyColor; - } - } - } - if (flags.applies_color_filter()) { - auto filter = paint.getColorFilterPtr(); - if (filter) { - if (!color.isTransparent() || filter->modifies_transparent_black()) { - color = kAnyColor; - } - } - } - return color; -} - -DisplayListBuilder::OpResult DisplayListBuilder::PaintResult( - const DlPaint& paint, - DisplayListAttributeFlags flags) { - if (current_layer_->is_nop_) { - return OpResult::kNoEffect; - } - if (flags.applies_blend()) { - switch (paint.getBlendMode()) { - // Nop blend mode (singular, there is only one) - case DlBlendMode::kDst: - return OpResult::kNoEffect; - - // Always clears pixels blend mode (singular, there is only one) - case DlBlendMode::kClear: - return OpResult::kPreservesTransparency; - - case DlBlendMode::kHue: - case DlBlendMode::kSaturation: - case DlBlendMode::kColor: - case DlBlendMode::kLuminosity: - case DlBlendMode::kColorBurn: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kNoEffect - : OpResult::kAffectsAll; - - // kSrcIn modifies pixels towards transparency - case DlBlendMode::kSrcIn: - return OpResult::kPreservesTransparency; - - // These blend modes preserve destination alpha - case DlBlendMode::kSrcATop: - case DlBlendMode::kDstOut: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kNoEffect - : OpResult::kPreservesTransparency; - - // Always destructive blend modes, potentially not affecting transparency - case DlBlendMode::kSrc: - case DlBlendMode::kSrcOut: - case DlBlendMode::kDstATop: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kPreservesTransparency - : OpResult::kAffectsAll; - - // The kDstIn blend mode modifies the destination unless the - // source color is opaque. - case DlBlendMode::kDstIn: - return GetEffectiveColor(paint, flags).isOpaque() - ? OpResult::kNoEffect - : OpResult::kPreservesTransparency; - - // The next group of blend modes modifies the destination unless the - // source color is transparent. - case DlBlendMode::kSrcOver: - case DlBlendMode::kDstOver: - case DlBlendMode::kXor: - case DlBlendMode::kPlus: - case DlBlendMode::kScreen: - case DlBlendMode::kMultiply: - case DlBlendMode::kOverlay: - case DlBlendMode::kDarken: - case DlBlendMode::kLighten: - case DlBlendMode::kColorDodge: - case DlBlendMode::kHardLight: - case DlBlendMode::kSoftLight: - case DlBlendMode::kDifference: - case DlBlendMode::kExclusion: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kNoEffect - : OpResult::kAffectsAll; - - // Modulate only leaves the pixel alone when the source is white. - case DlBlendMode::kModulate: - return GetEffectiveColor(paint, flags) == DlColor::kWhite() - ? OpResult::kNoEffect - : OpResult::kPreservesTransparency; - } - } - return OpResult::kAffectsAll; -} - -} // namespace flutter diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h deleted file mode 100644 index ab0bce0db3c38..0000000000000 --- a/display_list/dl_builder.h +++ /dev/null @@ -1,780 +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. - -#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ -#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ - -#include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_blend_mode.h" -#include "flutter/display_list/dl_canvas.h" -#include "flutter/display_list/dl_op_flags.h" -#include "flutter/display_list/dl_op_receiver.h" -#include "flutter/display_list/dl_paint.h" -#include "flutter/display_list/dl_sampling_options.h" -#include "flutter/display_list/effects/dl_path_effect.h" -#include "flutter/display_list/image/dl_image.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" -#include "flutter/display_list/utils/dl_comparable.h" -#include "flutter/display_list/utils/dl_matrix_clip_tracker.h" -#include "flutter/fml/macros.h" - -namespace flutter { - -// The primary class used to build a display list. The list of methods -// here matches the list of methods invoked on a |DlOpReceiver| combined -// with the list of methods invoked on a |DlCanvas|. -class DisplayListBuilder final : public virtual DlCanvas, - public SkRefCnt, - virtual DlOpReceiver, - DisplayListOpFlags { - public: - static constexpr SkRect kMaxCullRect = - SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); - - explicit DisplayListBuilder(bool prepare_rtree) - : DisplayListBuilder(kMaxCullRect, prepare_rtree) {} - - explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect, - bool prepare_rtree = false); - - ~DisplayListBuilder(); - - // |DlCanvas| - SkISize GetBaseLayerSize() const override; - // |DlCanvas| - SkImageInfo GetImageInfo() const override; - - // |DlCanvas| - void Save() override; - - // |DlCanvas| - void SaveLayer(const SkRect* bounds, - const DlPaint* paint = nullptr, - const DlImageFilter* backdrop = nullptr) override; - // |DlCanvas| - void Restore() override; - // |DlCanvas| - int GetSaveCount() const override { return layer_stack_.size(); } - // |DlCanvas| - void RestoreToCount(int restore_count) override; - - // |DlCanvas| - void Translate(SkScalar tx, SkScalar ty) override; - // |DlCanvas| - void Scale(SkScalar sx, SkScalar sy) override; - // |DlCanvas| - void Rotate(SkScalar degrees) override; - // |DlCanvas| - void Skew(SkScalar sx, SkScalar sy) override; - - // clang-format off - // 2x3 2D affine subset of a 4x4 transform in row major order - // |DlCanvas| - void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) override; - // full 4x4 transform in row major order - // |DlCanvas| - void TransformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; - // clang-format on - // |DlCanvas| - void TransformReset() override; - // |DlCanvas| - void Transform(const SkMatrix* matrix) override; - // |DlCanvas| - void Transform(const SkM44* matrix44) override; - // |DlCanvas| - void SetTransform(const SkMatrix* matrix) override { - TransformReset(); - Transform(matrix); - } - // |DlCanvas| - void SetTransform(const SkM44* matrix44) override { - TransformReset(); - Transform(matrix44); - } - using DlCanvas::Transform; - - /// Returns the 4x4 full perspective transform representing all transform - /// operations executed so far in this DisplayList within the enclosing - /// save stack. - // |DlCanvas| - SkM44 GetTransformFullPerspective() const override { - return tracker_.matrix_4x4(); - } - /// Returns the 3x3 partial perspective transform representing all transform - /// operations executed so far in this DisplayList within the enclosing - /// save stack. - // |DlCanvas| - SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); } - - // |DlCanvas| - void ClipRect(const SkRect& rect, - ClipOp clip_op = ClipOp::kIntersect, - bool is_aa = false) override; - // |DlCanvas| - void ClipRRect(const SkRRect& rrect, - ClipOp clip_op = ClipOp::kIntersect, - bool is_aa = false) override; - // |DlCanvas| - void ClipPath(const SkPath& path, - ClipOp clip_op = ClipOp::kIntersect, - bool is_aa = false) override; - - /// Conservative estimate of the bounds of all outstanding clip operations - /// measured in the coordinate space within which this DisplayList will - /// be rendered. - // |DlCanvas| - SkRect GetDestinationClipBounds() const override { - return tracker_.device_cull_rect(); - } - /// Conservative estimate of the bounds of all outstanding clip operations - /// transformed into the local coordinate space in which currently - /// recorded rendering operations are interpreted. - // |DlCanvas| - SkRect GetLocalClipBounds() const override { - return tracker_.local_cull_rect(); - } - - /// Return true iff the supplied bounds are easily shown to be outside - /// of the current clip bounds. This method may conservatively return - /// false if it cannot make the determination. - // |DlCanvas| - bool QuickReject(const SkRect& bounds) const override; - - // |DlCanvas| - void DrawPaint(const DlPaint& paint) override; - // |DlCanvas| - void DrawColor(DlColor color, DlBlendMode mode) override; - // |DlCanvas| - void DrawLine(const SkPoint& p0, - const SkPoint& p1, - const DlPaint& paint) override; - // |DlCanvas| - void DrawRect(const SkRect& rect, const DlPaint& paint) override; - // |DlCanvas| - void DrawOval(const SkRect& bounds, const DlPaint& paint) override; - // |DlCanvas| - void DrawCircle(const SkPoint& center, - SkScalar radius, - const DlPaint& paint) override; - // |DlCanvas| - void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override; - // |DlCanvas| - void DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) override; - // |DlCanvas| - void DrawPath(const SkPath& path, const DlPaint& paint) override; - // |DlCanvas| - void DrawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter, - const DlPaint& paint) override; - // |DlCanvas| - void DrawPoints(PointMode mode, - uint32_t count, - const SkPoint pts[], - const DlPaint& paint) override; - // |DlCanvas| - void DrawVertices(const DlVertices* vertices, - DlBlendMode mode, - const DlPaint& paint) override; - using DlCanvas::DrawVertices; - // |DlCanvas| - void DrawImage(const sk_sp& image, - const SkPoint point, - DlImageSampling sampling, - const DlPaint* paint = nullptr) override; - // |DlCanvas| - void DrawImageRect( - const sk_sp& image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - const DlPaint* paint = nullptr, - SrcRectConstraint constraint = SrcRectConstraint::kFast) override; - using DlCanvas::DrawImageRect; - // |DlCanvas| - void DrawImageNine(const sk_sp& image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - const DlPaint* paint = nullptr) override; - // |DlCanvas| - void DrawAtlas(const sk_sp& atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cullRect, - const DlPaint* paint = nullptr) override; - // |DlCanvas| - void DrawDisplayList(const sk_sp display_list, - SkScalar opacity = SK_Scalar1) override; - // |DlCanvas| - void DrawTextBlob(const sk_sp& blob, - SkScalar x, - SkScalar y, - const DlPaint& paint) override; - // |DlCanvas| - void DrawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) override; - - // |DlCanvas| - void Flush() override {} - - sk_sp Build(); - - private: - // This method exposes the internal stateful DlOpReceiver implementation - // of the DisplayListBuilder, primarily for testing purposes. Its use - // is obsolete and forbidden in every other case and is only shared to a - // pair of "friend" accessors in the benchmark/unittest files. - DlOpReceiver& asReceiver() { return *this; } - - friend DlOpReceiver& DisplayListBuilderBenchmarkAccessor( - DisplayListBuilder& builder); - friend DlOpReceiver& DisplayListBuilderTestingAccessor( - DisplayListBuilder& builder); - friend DlPaint DisplayListBuilderTestingAttributes( - DisplayListBuilder& builder); - - void SetAttributesFromPaint(const DlPaint& paint, - const DisplayListAttributeFlags flags); - - // |DlOpReceiver| - void setAntiAlias(bool aa) override { - if (current_.isAntiAlias() != aa) { - onSetAntiAlias(aa); - } - } - // |DlOpReceiver| - void setDither(bool dither) override { - if (current_.isDither() != dither) { - onSetDither(dither); - } - } - // |DlOpReceiver| - void setInvertColors(bool invert) override { - if (current_.isInvertColors() != invert) { - onSetInvertColors(invert); - } - } - // |DlOpReceiver| - void setStrokeCap(DlStrokeCap cap) override { - if (current_.getStrokeCap() != cap) { - onSetStrokeCap(cap); - } - } - // |DlOpReceiver| - void setStrokeJoin(DlStrokeJoin join) override { - if (current_.getStrokeJoin() != join) { - onSetStrokeJoin(join); - } - } - // |DlOpReceiver| - void setDrawStyle(DlDrawStyle style) override { - if (current_.getDrawStyle() != style) { - onSetDrawStyle(style); - } - } - // |DlOpReceiver| - void setStrokeWidth(float width) override { - if (current_.getStrokeWidth() != width) { - onSetStrokeWidth(width); - } - } - // |DlOpReceiver| - void setStrokeMiter(float limit) override { - if (current_.getStrokeMiter() != limit) { - onSetStrokeMiter(limit); - } - } - // |DlOpReceiver| - void setColor(DlColor color) override { - if (current_.getColor() != color) { - onSetColor(color); - } - } - // |DlOpReceiver| - void setBlendMode(DlBlendMode mode) override { - if (current_.getBlendMode() != mode) { - onSetBlendMode(mode); - } - } - // |DlOpReceiver| - void setColorSource(const DlColorSource* source) override { - if (NotEquals(current_.getColorSource(), source)) { - onSetColorSource(source); - } - } - // |DlOpReceiver| - void setImageFilter(const DlImageFilter* filter) override { - if (NotEquals(current_.getImageFilter(), filter)) { - onSetImageFilter(filter); - } - } - // |DlOpReceiver| - void setColorFilter(const DlColorFilter* filter) override { - if (NotEquals(current_.getColorFilter(), filter)) { - onSetColorFilter(filter); - } - } - // |DlOpReceiver| - void setPathEffect(const DlPathEffect* effect) override { - if (NotEquals(current_.getPathEffect(), effect)) { - onSetPathEffect(effect); - } - } - // |DlOpReceiver| - void setMaskFilter(const DlMaskFilter* filter) override { - if (NotEquals(current_.getMaskFilter(), filter)) { - onSetMaskFilter(filter); - } - } - - DlPaint CurrentAttributes() const { return current_; } - - // |DlOpReceiver| - void save() override { Save(); } - // Only the |renders_with_attributes()| option will be accepted here. Any - // other flags will be ignored and calculated anew as the DisplayList is - // built. Alternatively, use the |saveLayer(SkRect, bool)| method. - // |DlOpReceiver| - void saveLayer(const SkRect* bounds, - const SaveLayerOptions options, - const DlImageFilter* backdrop) override; - // |DlOpReceiver| - void restore() override { Restore(); } - - // |DlOpReceiver| - void translate(SkScalar tx, SkScalar ty) override { Translate(tx, ty); } - // |DlOpReceiver| - void scale(SkScalar sx, SkScalar sy) override { Scale(sx, sy); } - // |DlOpReceiver| - void rotate(SkScalar degrees) override { Rotate(degrees); } - // |DlOpReceiver| - void skew(SkScalar sx, SkScalar sy) override { Skew(sx, sy); } - - // clang-format off - // |DlOpReceiver| - void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) override { - Transform2DAffine(mxx, mxy, mxt, myx, myy, myt); - } - // |DlOpReceiver| - void transformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override { - TransformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - } - // clang-format off - // |DlOpReceiver| - void transformReset() override { TransformReset(); } - - // |DlOpReceiver| - void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override { - ClipRect(rect, clip_op, is_aa); - } - // |DlOpReceiver| - void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override { - ClipRRect(rrect, clip_op, is_aa); - } - // |DlOpReceiver| - void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override { - ClipPath(path, clip_op, is_aa); - } - - // |DlOpReceiver| - void drawPaint() override; - // |DlOpReceiver| - void drawColor(DlColor color, DlBlendMode mode) override { - DrawColor(color, mode); - } - // |DlOpReceiver| - void drawLine(const SkPoint& p0, const SkPoint& p1) override; - // |DlOpReceiver| - void drawRect(const SkRect& rect) override; - // |DlOpReceiver| - void drawOval(const SkRect& bounds) override; - // |DlOpReceiver| - void drawCircle(const SkPoint& center, SkScalar radius) override; - // |DlOpReceiver| - void drawRRect(const SkRRect& rrect) override; - // |DlOpReceiver| - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; - // |DlOpReceiver| - void drawPath(const SkPath& path) override; - // |DlOpReceiver| - void drawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter) override; - // |DlOpReceiver| - void drawPoints(PointMode mode, uint32_t count, const SkPoint pts[]) override; - // |DlOpReceiver| - void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - - // |DlOpReceiver| - void drawImage(const sk_sp image, - const SkPoint point, - DlImageSampling sampling, - bool render_with_attributes) override; - // |DlOpReceiver| - void drawImageRect( - const sk_sp image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - bool render_with_attributes, - SrcRectConstraint constraint = SrcRectConstraint::kFast) override; - // |DlOpReceiver| - void drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - bool render_with_attributes) override; - // |DlOpReceiver| - void drawAtlas(const sk_sp atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cullRect, - bool render_with_attributes) override; - - // |DlOpReceiver| - void drawDisplayList(const sk_sp display_list, - SkScalar opacity) override { - DrawDisplayList(display_list, opacity); - } - // |DlOpReceiver| - void drawTextBlob(const sk_sp blob, - SkScalar x, - SkScalar y) override; - // |DlOpReceiver| - void drawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) override { - DrawShadow(path, color, elevation, transparent_occluder, dpr); - } - - void checkForDeferredSave(); - - DisplayListStorage storage_; - size_t used_ = 0; - size_t allocated_ = 0; - int render_op_count_ = 0; - int op_index_ = 0; - - // bytes and ops from |drawPicture| and |drawDisplayList| - size_t nested_bytes_ = 0; - int nested_op_count_ = 0; - - bool is_ui_thread_safe_ = true; - - template - void* Push(size_t extra, int op_inc, Args&&... args); - - void intersect(const SkRect& rect); - - // kInvalidSigma is used to indicate that no MaskBlur is currently set. - static constexpr SkScalar kInvalidSigma = 0.0; - static bool mask_sigma_valid(SkScalar sigma) { - return SkScalarIsFinite(sigma) && sigma > 0.0; - } - - class LayerInfo { - public: - explicit LayerInfo( - size_t save_offset = 0, - bool has_layer = false, - const std::shared_ptr& filter = nullptr) - : save_offset_(save_offset), - has_layer_(has_layer), - filter_(filter) {} - - // The offset into the memory buffer where the saveLayer DLOp record - // for this saveLayer() call is placed. This may be needed if the - // eventual restore() call has discovered important information about - // the records inside the saveLayer that may impact how the saveLayer - // is handled (e.g., |cannot_inherit_opacity| == false). - // This offset is only valid if |has_layer| is true. - size_t save_offset() const { return save_offset_; } - - bool has_layer() const { return has_layer_; } - bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; } - bool has_compatible_op() const { return has_compatible_op_; } - bool affects_transparent_layer() const { - return affects_transparent_layer_; - } - - bool is_group_opacity_compatible() const { - return !cannot_inherit_opacity_; - } - - void mark_incompatible() { cannot_inherit_opacity_ = true; } - - // For now this only allows a single compatible op to mark the - // layer as being compatible with group opacity. If we start - // computing bounds of ops in the Builder methods then we - // can upgrade this to checking for overlapping ops. - // See https://github.com/flutter/flutter/issues/93899 - void add_compatible_op() { - if (!cannot_inherit_opacity_) { - if (has_compatible_op_) { - cannot_inherit_opacity_ = true; - } else { - has_compatible_op_ = true; - } - } - } - - // Records that the current layer contains an op that produces visible - // output on a transparent surface. - void add_visible_op() { - affects_transparent_layer_ = true; - } - - // The filter to apply to the layer bounds when it is restored - std::shared_ptr filter() { return filter_; } - - // is_unbounded should be set to true if we ever encounter an operation - // on a layer that either is unrestricted (|drawColor| or |drawPaint|) - // or cannot compute its bounds (some effects and filters) and there - // was no outstanding clip op at the time. - // When the layer is restored, the outer layer may then process this - // unbounded state by accumulating its own clip or transferring the - // unbounded state to its own outer layer. - // Typically the DisplayList will have been constructed with a cull - // rect which will act as a default clip for the outermost layer and - // the unbounded state of all sub layers will eventually be caught by - // that cull rect so that the overall unbounded state of the entire - // DisplayList will never be true. - // - // For historical consistency it is worth noting that SkPicture used - // to treat these same conditions as a Nop (they accumulate the - // SkPicture cull rect, but if no cull rect was specified then it is - // an empty Rect and so has no effect on the bounds). - // - // Flutter is unlikely to ever run into this as the Dart mechanisms - // all supply a non-null cull rect for all Dart Picture objects, - // even if that cull rect is kGiantRect. - void set_unbounded() { is_unbounded_ = true; } - - // |is_unbounded| should be called after |getLayerBounds| in case - // a problem was found during the computation of those bounds, - // the layer will have one last chance to flag an unbounded state. - bool is_unbounded() const { return is_unbounded_; } - - private: - size_t save_offset_; - bool has_layer_; - bool cannot_inherit_opacity_ = false; - bool has_compatible_op_ = false; - std::shared_ptr filter_; - bool is_unbounded_ = false; - bool has_deferred_save_op_ = false; - bool is_nop_ = false; - bool affects_transparent_layer_ = false; - - friend class DisplayListBuilder; - }; - - std::vector layer_stack_; - LayerInfo* current_layer_; - DisplayListMatrixClipTracker tracker_; - std::unique_ptr accumulator_; - BoundsAccumulator* accumulator() { return accumulator_.get(); } - - // This flag indicates whether or not the current rendering attributes - // are compatible with rendering ops applying an inherited opacity. - bool current_opacity_compatibility_ = true; - - // Returns the compatibility of a given blend mode for applying an - // inherited opacity value to modulate the visibility of the op. - // For now we only accept SrcOver blend modes but this could be expanded - // in the future to include other (rarely used) modes that also modulate - // the opacity of a rendering operation at the cost of a switch statement - // or lookup table. - static bool IsOpacityCompatible(DlBlendMode mode) { - return (mode == DlBlendMode::kSrcOver); - } - - void UpdateCurrentOpacityCompatibility() { - current_opacity_compatibility_ = // - current_.getColorFilter() == nullptr && // - !current_.isInvertColors() && // - IsOpacityCompatible(current_.getBlendMode()); - } - - // Update the opacity compatibility flags of the current layer for an op - // that has determined its compatibility as indicated by |compatible|. - void UpdateLayerOpacityCompatibility(bool compatible) { - if (compatible) { - current_layer_->add_compatible_op(); - } else { - current_layer_->mark_incompatible(); - } - } - - // Check for opacity compatibility for an op that may or may not use the - // current rendering attributes as indicated by |uses_blend_attribute|. - // If the flag is false then the rendering op will be able to substitute - // a default Paint object with the opacity applied using the default SrcOver - // blend mode which is always compatible with applying an inherited opacity. - void CheckLayerOpacityCompatibility(bool uses_blend_attribute = true) { - UpdateLayerOpacityCompatibility(!uses_blend_attribute || - current_opacity_compatibility_); - } - - void CheckLayerOpacityHairlineCompatibility() { - UpdateLayerOpacityCompatibility( - current_opacity_compatibility_ && - (current_.getDrawStyle() == DlDrawStyle::kFill || - current_.getStrokeWidth() > 0)); - } - - // Check for opacity compatibility for an op that ignores the current - // attributes and uses the indicated blend |mode| to render to the layer. - // This is only used by |drawColor| currently. - void CheckLayerOpacityCompatibility(DlBlendMode mode) { - UpdateLayerOpacityCompatibility(IsOpacityCompatible(mode)); - } - - void onSetAntiAlias(bool aa); - void onSetDither(bool dither); - void onSetInvertColors(bool invert); - void onSetStrokeCap(DlStrokeCap cap); - void onSetStrokeJoin(DlStrokeJoin join); - void onSetDrawStyle(DlDrawStyle style); - void onSetStrokeWidth(SkScalar width); - void onSetStrokeMiter(SkScalar limit); - void onSetColor(DlColor color); - void onSetBlendMode(DlBlendMode mode); - void onSetColorSource(const DlColorSource* source); - void onSetImageFilter(const DlImageFilter* filter); - void onSetColorFilter(const DlColorFilter* filter); - void onSetPathEffect(const DlPathEffect* effect); - void onSetMaskFilter(const DlMaskFilter* filter); - - // The DisplayList had an unbounded call with no cull rect or clip - // to contain it. Should only be called after the stream is fully - // built. - // Unbounded operations are calls like |drawColor| which are defined - // to flood the entire surface, or calls that relied on a rendering - // attribute which is unable to compute bounds (should be rare). - // In those cases the bounds will represent only the accumulation - // of the bounded calls and this flag will be set to indicate that - // condition. - bool is_unbounded() const { - FML_DCHECK(layer_stack_.size() == 1); - return layer_stack_.front().is_unbounded(); - } - - SkRect bounds() const { - FML_DCHECK(layer_stack_.size() == 1); - if (is_unbounded()) { - FML_LOG(INFO) << "returning partial bounds for unbounded DisplayList"; - } - - return accumulator_->bounds(); - } - - sk_sp rtree() { - FML_DCHECK(layer_stack_.size() == 1); - if (is_unbounded()) { - FML_LOG(INFO) << "returning partial rtree for unbounded DisplayList"; - } - - return accumulator_->rtree(); - } - - static DisplayListAttributeFlags FlagsForPointMode(PointMode mode); - - enum class OpResult { - kNoEffect, - kPreservesTransparency, - kAffectsAll, - }; - - bool paint_nops_on_transparency(); - OpResult PaintResult(const DlPaint& paint, - DisplayListAttributeFlags flags = kDrawPaintFlags); - - void UpdateLayerResult(OpResult result) { - switch (result) { - case OpResult::kNoEffect: - case OpResult::kPreservesTransparency: - break; - case OpResult::kAffectsAll: - current_layer_->add_visible_op(); - break; - } - } - - // kAnyColor is a non-opaque and non-transparent color that will not - // trigger any short-circuit tests about the results of a blend. - static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlpha(0x80); - static_assert(!kAnyColor.isOpaque()); - static_assert(!kAnyColor.isTransparent()); - static DlColor GetEffectiveColor(const DlPaint& paint, - DisplayListAttributeFlags flags); - - // Computes the bounds of an operation adjusted for a given ImageFilter - // and returns whether the computation was possible. If the method - // returns false then the caller should assume the worst about the bounds. - static bool ComputeFilteredBounds(SkRect& bounds, - const DlImageFilter* filter); - - // Adjusts the indicated bounds for the given flags and returns true if - // the calculation was possible, or false if it could not be estimated. - bool AdjustBoundsForPaint(SkRect& bounds, DisplayListAttributeFlags flags); - - // Records the fact that we encountered an op that either could not - // estimate its bounds or that fills all of the destination space. - bool AccumulateUnbounded(); - - // Records the bounds for an op after modifying them according to the - // supplied attribute flags and transforming by the current matrix. - bool AccumulateOpBounds(const SkRect& bounds, - DisplayListAttributeFlags flags) { - SkRect safe_bounds = bounds; - return AccumulateOpBounds(safe_bounds, flags); - } - - // Records the bounds for an op after modifying them according to the - // supplied attribute flags and transforming by the current matrix - // and clipping against the current clip. - bool AccumulateOpBounds(SkRect& bounds, DisplayListAttributeFlags flags); - - // Records the given bounds after transforming by the current matrix - // and clipping against the current clip. - bool AccumulateBounds(SkRect& bounds); - - DlPaint current_; -}; - -} // namespace flutter - -#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h index f04645c157788..c3346d322ca6d 100644 --- a/display_list/dl_canvas.h +++ b/display_list/dl_canvas.h @@ -18,6 +18,10 @@ #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkTextBlob.h" +namespace impeller { +struct Picture; +} // namespace impeller + namespace flutter { //------------------------------------------------------------------------------ @@ -61,7 +65,12 @@ class DlCanvas { const DlImageFilter* backdrop = nullptr) = 0; virtual void Restore() = 0; virtual int GetSaveCount() const = 0; - virtual void RestoreToCount(int restore_count) = 0; + virtual void RestoreToCount(int restore_count) { + FML_DCHECK(restore_count <= GetSaveCount()); + while (restore_count < GetSaveCount() && GetSaveCount() > 1) { + Restore(); + } + } virtual void Translate(SkScalar tx, SkScalar ty) = 0; virtual void Scale(SkScalar sx, SkScalar sy) = 0; @@ -201,6 +210,9 @@ class DlCanvas { const DlPaint* paint = nullptr) = 0; virtual void DrawDisplayList(const sk_sp display_list, SkScalar opacity = SK_Scalar1) = 0; + virtual void DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity = SK_Scalar1) = 0; virtual void DrawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y, diff --git a/display_list/dl_canvas_to_receiver.cc b/display_list/dl_canvas_to_receiver.cc new file mode 100644 index 0000000000000..aed34a62c189d --- /dev/null +++ b/display_list/dl_canvas_to_receiver.cc @@ -0,0 +1,1158 @@ +// 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 "flutter/display_list/dl_canvas_to_receiver.h" + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/dl_blend_mode.h" +#include "flutter/display_list/dl_op_flags.h" +#include "flutter/display_list/dl_op_records.h" +#include "flutter/display_list/effects/dl_color_source.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "fml/logging.h" +#include "third_party/skia/include/core/SkScalar.h" + +namespace flutter { + +DlCanvasToReceiver::DlCanvasToReceiver( + std::shared_ptr receiver) + : receiver_(std::move(receiver)) { + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); +} + +SkISize DlCanvasToReceiver::GetBaseLayerSize() const { + CheckAlive(); + return receiver_->base_device_cull_rect().roundOut().size(); +} + +SkImageInfo DlCanvasToReceiver::GetImageInfo() const { + CheckAlive(); + SkISize size = GetBaseLayerSize(); + return SkImageInfo::MakeUnknown(size.width(), size.height()); +} + +bool DlCanvasToReceiver::SetAttributesFromPaint( + const DlPaint* paint, + const DisplayListAttributeFlags flags) { + if (paint == nullptr) { + return true; + } + + bool can_inherit_opacity = true; + if (flags.applies_anti_alias()) { + bool aa = paint->isAntiAlias(); + if (current_.isAntiAlias() != aa) { + current_.setAntiAlias(aa); + receiver_->setAntiAlias(aa); + } + } + if (flags.applies_dither()) { + bool dither = paint->isDither(); + if (current_.isDither() != dither) { + current_.setDither(dither); + receiver_->setDither(dither); + } + } + if (flags.applies_alpha_or_color()) { + DlColor color = paint->getColor(); + if (current_.getColor() != color) { + current_.setColor(color); + receiver_->setColor(color); + } + } + if (flags.applies_blend()) { + DlBlendMode mode = paint->getBlendMode(); + if (current_.getBlendMode() != mode) { + current_.setBlendMode(mode); + receiver_->setBlendMode(mode); + } + if (!IsOpacityCompatible(mode)) { + can_inherit_opacity = false; + } + } + if (flags.applies_style()) { + DlDrawStyle style = paint->getDrawStyle(); + if (current_.getDrawStyle() != style) { + current_.setDrawStyle(style); + receiver_->setDrawStyle(style); + } + } + if (flags.is_stroked(paint->getDrawStyle())) { + SkScalar width = paint->getStrokeWidth(); + if (current_.getStrokeWidth() != width) { + current_.setStrokeWidth(width); + receiver_->setStrokeWidth(width); + } + if (flags.may_have_trouble_with_hairlines() && width <= 0) { + can_inherit_opacity = false; + } + SkScalar miter = paint->getStrokeMiter(); + if (current_.getStrokeMiter() != miter) { + current_.setStrokeMiter(miter); + receiver_->setStrokeMiter(miter); + } + DlStrokeCap cap = paint->getStrokeCap(); + if (current_.getStrokeCap() != cap) { + current_.setStrokeCap(cap); + receiver_->setStrokeCap(cap); + } + DlStrokeJoin join = paint->getStrokeJoin(); + if (current_.getStrokeJoin() != join) { + current_.setStrokeJoin(join); + receiver_->setStrokeJoin(join); + } + } + if (flags.applies_shader()) { + auto source = paint->getColorSourcePtr(); + if (NotEquals(current_.getColorSourcePtr(), source)) { + current_.setColorSource(paint->getColorSource()); + receiver_->setColorSource(source); + } + } + if (flags.applies_color_filter()) { + bool invert = paint->isInvertColors(); + if (current_.isInvertColors() != invert) { + current_.setInvertColors(invert); + receiver_->setInvertColors(invert); + } + auto filter = paint->getColorFilterPtr(); + if (NotEquals(current_.getColorFilterPtr(), filter)) { + current_.setColorFilter(paint->getColorFilter()); + receiver_->setColorFilter(filter); + } + if (invert || filter) { + can_inherit_opacity = false; + } + } + if (flags.applies_image_filter()) { + auto filter = paint->getImageFilterPtr(); + if (NotEquals(current_.getImageFilterPtr(), filter)) { + current_.setImageFilter(paint->getImageFilter()); + receiver_->setImageFilter(filter); + } + } + if (flags.applies_path_effect()) { + auto effect = paint->getPathEffectPtr(); + if (NotEquals(current_.getPathEffectPtr(), effect)) { + current_.setPathEffect(paint->getPathEffect()); + receiver_->setPathEffect(effect); + } + } + if (flags.applies_mask_filter()) { + auto filter = paint->getMaskFilterPtr(); + if (NotEquals(current_.getMaskFilterPtr(), filter)) { + current_.setMaskFilter(paint->getMaskFilter()); + receiver_->setMaskFilter(filter); + } + } + return can_inherit_opacity; +} + +void DlCanvasToReceiver::Save() { + CheckAlive(); + bool is_nop = current_layer_->state_is_nop_; + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); + current_layer_->state_is_nop_ = is_nop; + receiver_->save(); +} +void DlCanvasToReceiver::SaveLayer(const SkRect* bounds, + const DlPaint* paint, + const DlImageFilter* backdrop) { + CheckAlive(); + SaveLayerOptions options = paint ? SaveLayerOptions::kWithAttributes // + : SaveLayerOptions::kNoAttributes; + DisplayListAttributeFlags flags = paint ? kSaveLayerWithPaintFlags // + : kSaveLayerFlags; + OpResult result = PaintResult(paint, flags); + if (result == OpResult::kNoEffect) { + Save(); + current_layer_->state_is_nop_ = true; + return; + } + + bool unbounded = false; + if (backdrop) { + // A backdrop will pre-fill up to the entire surface, bounded by + // the clip. + unbounded = true; + } else if (!paint_nops_on_transparency(paint)) { + // The actual flood of the outer layer clip based on the paint + // will occur after the (eventual) corresponding restore is called, + // but rather than remember this information in the LayerInfo + // until the restore method is processed, we just mark the unbounded + // state up front. Another reason to accumulate the clip here rather + // than in restore is so that this savelayer will be tagged in the + // rtree with its full bounds and the right op_index so that it doesn't + // get culled during rendering. + unbounded = true; + } + if (unbounded) { + // Accumulate should always return true here because if the clip + // was empty then that would have been caught up above when we + // tested the PaintResult. + [[maybe_unused]] bool unclipped = AccumulateUnbounded(); + FML_DCHECK(unclipped); + } + + auto image_filter = paint ? paint->getImageFilter() : nullptr; + + bool layer_can_inherit_opacity = SetAttributesFromPaint(paint, flags); + receiver_->saveLayer(bounds, options, backdrop); + current_layer_->Update(result, layer_can_inherit_opacity); + + layer_stack_.emplace_back(true, image_filter); + current_layer_ = &layer_stack_.back(); + + if (image_filter != nullptr || !layer_can_inherit_opacity) { + // The compatibility computed by SetAttributes does not take an + // ImageFilter into account because an individual primitive with + // an ImageFilter can apply opacity on top of it. But, if the layer + // is applying the ImageFilter then it cannot pass the opacity on + // to its children even if they are compatible. + // Also, if a layer cannot itself inherit opacity then it cannot + // pass it along to its children. Arguably this information is + // not interesting because the saveLayer will have informed its + // enclosing layers not to send it an opacity to apply in the + // first place. But some tests check to see if the layer has + // noticed this situation by testing the inheritance property on + // the layer itself. Better tests wouldn't expect this property + // to be recorded there, but we will mark our children incompatible + // to be explicit about this situation. + current_layer_->mark_incompatible(); + } + + if (image_filter != nullptr) { + // We use |resetCullRect| here because we will be accumulating bounds of + // primitives before applying the filter to those bounds. We might + // encounter a primitive whose bounds are clipped, but whose filtered + // bounds will not be clipped. If the individual rendering ops bounds + // are clipped, it will not contribute to the overall bounds which + // could lead to inaccurate (subset) bounds of the DisplayList. + // We need to reset the cull rect here to avoid this premature clipping. + // The filtered bounds will be clipped to the existing clip rect when + // this layer is restored. + // If bounds is null then the original cull_rect will be used. + receiver_->resetCullRect(bounds); + } else if (bounds != nullptr) { + // Even though Skia claims that the bounds are only a hint, they actually + // use them as the temporary layer bounds during rendering the layer, so + // we set them as if a clip operation were performed. + receiver_->intersectCullRect(*bounds); + } +} + +void DlCanvasToReceiver::Restore() { + CheckAlive(); + if (layer_stack_.size() > 1) { + // Grab the current layer info before we push the restore + // on the stack. + LayerInfo layer_info = layer_stack_.back(); + layer_stack_.pop_back(); + current_layer_ = &layer_stack_.back(); + + if (layer_info.has_layer()) { + bool can_distribute_opacity = + layer_info.has_layer() && layer_info.is_group_opacity_compatible(); + receiver_->restoreLayer(layer_info.filter().get(), + layer_info.is_unbounded(), + can_distribute_opacity); + } else { + receiver_->restore(); + + // For regular save() ops there was no protecting layer so we have to + // accumulate the opacity compatibility values into the enclosing layer. + if (layer_info.cannot_inherit_opacity()) { + current_layer_->mark_incompatible(); + } else if (layer_info.has_compatible_op()) { + current_layer_->add_compatible_op(); + } + } + } +} +void DlCanvasToReceiver::RestoreToCount(int restore_count) { + CheckAlive(); + FML_DCHECK(restore_count <= GetSaveCount()); + while (restore_count < GetSaveCount() && GetSaveCount() > 1) { + Restore(); + } +} + +void DlCanvasToReceiver::Translate(SkScalar tx, SkScalar ty) { + CheckAlive(); + if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && + (tx != 0.0 || ty != 0.0)) { + receiver_->translate(tx, ty); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } +} +void DlCanvasToReceiver::Scale(SkScalar sx, SkScalar sy) { + CheckAlive(); + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 1.0 || sy != 1.0)) { + receiver_->scale(sx, sy); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } +} +void DlCanvasToReceiver::Rotate(SkScalar degrees) { + CheckAlive(); + if (SkScalarMod(degrees, 360.0) != 0.0) { + receiver_->rotate(degrees); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } +} +void DlCanvasToReceiver::Skew(SkScalar sx, SkScalar sy) { + CheckAlive(); + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 0.0 || sy != 0.0)) { + receiver_->skew(sx, sy); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } +} + +// clang-format off + +// 2x3 2D affine subset of a 4x4 transform in row major order +void DlCanvasToReceiver::Transform2DAffine( + SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) { + CheckAlive(); + if (SkScalarsAreFinite(mxx, myx) && + SkScalarsAreFinite(mxy, myy) && + SkScalarsAreFinite(mxt, myt)) { + if (mxx == 1 && mxy == 0 && + myx == 0 && myy == 1) { + Translate(mxt, myt); + } else { + receiver_->transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } + } +} +// full 4x4 transform in row major order +void DlCanvasToReceiver::TransformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { + CheckAlive(); + if ( mxz == 0 && + myz == 0 && + mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 && + mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { + Transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && + SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && + SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && + SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { + receiver_->transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } +} +void DlCanvasToReceiver::TransformReset() { + CheckAlive(); + receiver_->transformReset(); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } +} +void DlCanvasToReceiver::Transform(const SkMatrix* matrix) { + CheckAlive(); + if (matrix != nullptr) { + if (matrix->hasPerspective()) { + Transform2DAffine( + matrix->getScaleX(), matrix->getSkewX(), matrix->getTranslateX(), + matrix->getSkewY(), matrix->getScaleY(), matrix->getTranslateY()); + } else { + TransformFullPerspective( + matrix->rc(0, 0), matrix->rc(0, 1), 0.0f, matrix->rc(0, 2), + matrix->rc(1, 0), matrix->rc(1, 1), 0.0f, matrix->rc(1, 2), + 0.0f, 0.0f, 1.0f, 0.0f, + matrix->rc(2, 0), matrix->rc(2, 1), 0.0f, matrix->rc(2, 2)); + } + } +} +void DlCanvasToReceiver::Transform(const SkM44* m44) { + CheckAlive(); + if (m44 != nullptr) { + TransformFullPerspective( + m44->rc(0, 0), m44->rc(0, 1), m44->rc(0, 2), m44->rc(0, 3), + m44->rc(1, 0), m44->rc(1, 1), m44->rc(1, 2), m44->rc(1, 3), + m44->rc(2, 0), m44->rc(2, 1), m44->rc(2, 2), m44->rc(2, 3), + m44->rc(3, 0), m44->rc(3, 1), m44->rc(3, 2), m44->rc(3, 3)); + } +} +// clang-format on + +void DlCanvasToReceiver::ClipRect(const SkRect& rect, + ClipOp clip_op, + bool is_aa) { + CheckAlive(); + if (!rect.isFinite()) { + return; + } + receiver_->clipRect(rect, clip_op, is_aa); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } +} +void DlCanvasToReceiver::ClipRRect(const SkRRect& rrect, + ClipOp clip_op, + bool is_aa) { + CheckAlive(); + if (rrect.isRect()) { + ClipRect(rrect.rect(), clip_op, is_aa); + } else { + receiver_->clipRRect(rrect, clip_op, is_aa); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } + } +} +void DlCanvasToReceiver::ClipPath(const SkPath& path, + ClipOp clip_op, + bool is_aa) { + CheckAlive(); + if (!path.isInverseFillType()) { + SkRect rect; + if (path.isRect(&rect)) { + this->ClipRect(rect, clip_op, is_aa); + return; + } + SkRRect rrect; + if (path.isOval(&rect)) { + rrect.setOval(rect); + this->ClipRRect(rrect, clip_op, is_aa); + return; + } + if (path.isRRect(&rrect)) { + this->ClipRRect(rrect, clip_op, is_aa); + return; + } + } + receiver_->clipPath(path, clip_op, is_aa); + if (receiver_->is_nop()) { + current_layer_->state_is_nop_ = true; + } +} + +bool DlCanvasToReceiver::QuickReject(const SkRect& bounds) const { + CheckAlive(); + return receiver_->content_culled(bounds); +} + +void DlCanvasToReceiver::DrawPaint(const DlPaint& paint) { + CheckAlive(); + OpResult result = PaintResult(paint, kDrawPaintFlags); + if (result != OpResult::kNoEffect && AccumulateUnbounded()) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, kDrawPaintFlags); + receiver_->drawPaint(); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawColor(DlColor color, DlBlendMode mode) { + CheckAlive(); + // We use DrawPaint flags here because the DrawColor flags indicate + // that they will ignore the values in the paint that we are creating. + // But we need the PaintResult to actually look at this paint object + // so DrawPaint flags tell it to check all the required values. + // Alternately, we could just call |DrawPaint(DlPaint(...));| which + // is the equivalent call and the same thing would happen. + OpResult result = + PaintResult(DlPaint(color).setBlendMode(mode), kDrawPaintFlags); + if (result != OpResult::kNoEffect && AccumulateUnbounded()) { + receiver_->drawColor(color, mode); + current_layer_->Update(result, IsOpacityCompatible(mode)); + } +} +void DlCanvasToReceiver::DrawLine(const SkPoint& p0, + const SkPoint& p1, + const DlPaint& paint) { + CheckAlive(); + SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); + DisplayListAttributeFlags flags = + (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags + : kDrawHVLineFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(bounds, &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawLine(p0, p1); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawRect(const SkRect& rect, const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawRectFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(rect.makeSorted(), &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawRect(rect); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawOval(const SkRect& bounds, const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawOvalFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(bounds.makeSorted(), &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawOval(bounds); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawCircle(const SkPoint& center, + SkScalar radius, + const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawCircleFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect) { + SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius, + center.fX + radius, center.fY + radius); + if (AccumulateOpBounds(bounds, &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawCircle(center, radius); + current_layer_->Update(result, can_inherit_opacity); + } + } +} +void DlCanvasToReceiver::DrawRRect(const SkRRect& rrect, const DlPaint& paint) { + CheckAlive(); + if (rrect.isRect()) { + DrawRect(rrect.rect(), paint); + } else if (rrect.isOval()) { + DrawOval(rrect.rect(), paint); + } else { + DisplayListAttributeFlags flags = kDrawRRectFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(rrect.getBounds(), &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawRRect(rrect); + current_layer_->Update(result, can_inherit_opacity); + } + } +} +void DlCanvasToReceiver::DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawDRRectFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(outer.getBounds(), &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawDRRect(outer, inner); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawPath(const SkPath& path, const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawPathFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect) { + bool is_visible = path.isInverseFillType() + ? AccumulateUnbounded() + : AccumulateOpBounds(path.getBounds(), &paint, flags); + if (is_visible) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawPath(path); + current_layer_->Update(result, can_inherit_opacity); + } + } +} + +void DlCanvasToReceiver::DrawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter, + const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = // + useCenter // + ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags; + OpResult result = PaintResult(paint, flags); + // This could be tighter if we compute where the start and end + // angles are and then also consider the quadrants swept and + // the center if specified. + if (result != OpResult::kNoEffect && + AccumulateOpBounds(bounds, &paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); + receiver_->drawArc(bounds, start, sweep, useCenter); + current_layer_->Update(result, can_inherit_opacity); + } +} + +DisplayListAttributeFlags DlCanvasToReceiver::FlagsForPointMode( + PointMode mode) { + switch (mode) { + case DlCanvas::PointMode::kPoints: + return kDrawPointsAsPointsFlags; + case PointMode::kLines: + return kDrawPointsAsLinesFlags; + case PointMode::kPolygon: + return kDrawPointsAsPolygonFlags; + } + FML_UNREACHABLE(); +} +void DlCanvasToReceiver::DrawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[], + const DlPaint& paint) { + CheckAlive(); + if (count == 0) { + return; + } + DisplayListAttributeFlags flags = FlagsForPointMode(mode); + OpResult result = PaintResult(paint, flags); + if (result == OpResult::kNoEffect) { + return; + } + + FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); + RectBoundsAccumulator ptBounds; + for (size_t i = 0; i < count; i++) { + ptBounds.accumulate(pts[i]); + } + SkRect point_bounds = ptBounds.bounds(); + if (!AccumulateOpBounds(point_bounds, &paint, flags)) { + return; + } + + (void)SetAttributesFromPaint(&paint, flags); + receiver_->drawPoints(mode, count, pts); + // drawPoints treats every point or line (or segment of a polygon) + // as a completely separate operation meaning we cannot ensure + // distribution of group opacity without analyzing the mode and the + // bounds of every sub-primitive. + // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c + current_layer_->Update(result, false); +} +void DlCanvasToReceiver::DrawVertices(const DlVertices* vertices, + DlBlendMode mode, + const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawVerticesFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(vertices->bounds(), &paint, flags)) { + (void)SetAttributesFromPaint(&paint, flags); + receiver_->drawVertices(vertices, mode); + // DrawVertices applies its colors to the paint so we have no way + // of controlling opacity using the current paint attributes. + // Although, examination of the |mode| might find some predictable + // cases. + current_layer_->Update(result, false); + } +} + +void DlCanvasToReceiver::DrawImage(const sk_sp& image, + const SkPoint point, + DlImageSampling sampling, + const DlPaint* paint) { + CheckAlive(); + DisplayListAttributeFlags flags = paint ? kDrawImageWithPaintFlags // + : kDrawImageFlags; + OpResult result = PaintResult(paint, flags); + if (result == OpResult::kNoEffect) { + return; + } + SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, // + image->width(), image->height()); + if (AccumulateOpBounds(bounds, paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(paint, flags); + receiver_->drawImage(image, point, sampling, paint != nullptr); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawImageRect(const sk_sp& image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + const DlPaint* paint, + SrcRectConstraint constraint) { + CheckAlive(); + DisplayListAttributeFlags flags = paint ? kDrawImageRectWithPaintFlags // + : kDrawImageRectFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(paint, flags); + receiver_->drawImageRect(image, src, dst, sampling, paint != nullptr, + constraint); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + const DlPaint* paint) { + CheckAlive(); + DisplayListAttributeFlags flags = paint ? kDrawImageNineWithPaintFlags // + : kDrawImageNineFlags; + OpResult result = PaintResult(paint, flags); + if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, paint, flags)) { + bool can_inherit_opacity = SetAttributesFromPaint(paint, flags); + receiver_->drawImageNine(image, center, dst, filter, paint != nullptr); + current_layer_->Update(result, can_inherit_opacity); + } +} +void DlCanvasToReceiver::DrawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cull_rect, + const DlPaint* paint) { + CheckAlive(); + DisplayListAttributeFlags flags = paint ? kDrawAtlasWithPaintFlags // + : kDrawAtlasFlags; + OpResult result = PaintResult(paint, flags); + if (result == OpResult::kNoEffect) { + return; + } + SkPoint quad[4]; + RectBoundsAccumulator atlasBounds; + for (int i = 0; i < count; i++) { + const SkRect& src = tex[i]; + xform[i].toQuad(src.width(), src.height(), quad); + for (int j = 0; j < 4; j++) { + atlasBounds.accumulate(quad[j]); + } + } + if (atlasBounds.is_empty() || + !AccumulateOpBounds(atlasBounds.bounds(), paint, flags)) { + return; + } + + (void)SetAttributesFromPaint(paint, flags); + receiver_->drawAtlas(atlas, xform, tex, colors, count, mode, sampling, + cull_rect, paint != nullptr); + // drawAtlas treats each image as a separate operation so we cannot rely + // on it to distribute the opacity without overlap without checking all + // of the transforms and texture rectangles. + current_layer_->Update(result, false); +} + +void DlCanvasToReceiver::DrawDisplayList(const sk_sp display_list, + SkScalar opacity) { + CheckAlive(); + if (!SkScalarIsFinite(opacity) || opacity <= SK_ScalarNearlyZero || + display_list->op_count() == 0 || display_list->bounds().isEmpty() || + current_layer_->state_is_nop_) { + return; + } + + const SkRect bounds = display_list->bounds(); + bool accumulated; + if (receiver_->wants_granular_bounds()) { + auto rtree = display_list->rtree(); + if (rtree) { + std::list rects = rtree->searchAndConsolidateRects(bounds, false); + accumulated = false; + for (const SkRect& rect : rects) { + // TODO (https://github.com/flutter/flutter/issues/114919): Attributes + // are not necessarily `kDrawDisplayListFlags`. + if (AccumulateOpBounds(rect, nullptr, kDrawDisplayListFlags)) { + accumulated = true; + } + } + } else { + accumulated = AccumulateOpBounds(bounds, nullptr, kDrawDisplayListFlags); + } + } else { + accumulated = AccumulateOpBounds(bounds, nullptr, kDrawDisplayListFlags); + } + if (!accumulated) { + return; + } + + receiver_->drawDisplayList(display_list, + opacity < SK_Scalar1 ? opacity : SK_Scalar1); + + // The non-nested op count accumulated in the |Push| method will include + // this call to |drawDisplayList| for non-nested op count metrics. + // But, for nested op count metrics we want the |drawDisplayList| call itself + // to be transparent. So we subtract 1 from our accumulated nested count to + // balance out against the 1 that was accumulated into the regular count. + // This behavior is identical to the way SkPicture computed nested op counts. + // nested_op_count_ += display_list->op_count(true) - 1; + // nested_bytes_ += display_list->bytes(true); + current_layer_->Update( + // Nop DisplayLists are eliminated above so we either affect transparent + // pixels or we do not. We should not have [kNoEffect]. + display_list->modifies_transparent_black() + ? OpResult::kAffectsAll + : OpResult::kPreservesTransparency, + display_list->can_apply_group_opacity()); +} +void DlCanvasToReceiver::DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity) { + FML_LOG(ERROR) << "Cannot draw Impeller Picture in to a a display list."; + FML_DCHECK(false); +} +void DlCanvasToReceiver::DrawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y, + const DlPaint& paint) { + CheckAlive(); + DisplayListAttributeFlags flags = kDrawTextBlobFlags; + OpResult result = PaintResult(paint, flags); + if (result == OpResult::kNoEffect) { + return; + } + bool unclipped = + AccumulateOpBounds(blob->bounds().makeOffset(x, y), &paint, flags); + // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the + // unit tests can use Fuchsia's font manager instead of the empty default. + // Until then we might encounter empty bounds for otherwise valid text and + // thus we ignore the results from AccumulateOpBounds. +#if defined(OS_FUCHSIA) + unclipped = true; +#endif // OS_FUCHSIA + if (unclipped) { + (void)SetAttributesFromPaint(&paint, flags); + receiver_->drawTextBlob(blob, x, y); + // There is no way to query if the glyphs of a text blob overlap and + // there are no current guarantees from either Skia or Impeller that + // they will protect overlapping glyphs from the effects of overdraw + // so we must make the conservative assessment that this DL layer is + // not compatible with group opacity inheritance. + current_layer_->Update(result, false); + } +} +void DlCanvasToReceiver::DrawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { + CheckAlive(); + OpResult result = PaintResult(DlPaint(color)); + if (result != OpResult::kNoEffect) { + SkRect shadow_bounds = + DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform()); + if (AccumulateOpBounds(shadow_bounds, nullptr, kDrawShadowFlags)) { + receiver_->drawShadow(path, color, elevation, transparent_occluder, dpr); + current_layer_->Update(result, false); + } + } +} + +bool DlCanvasToReceiver::ComputeFilteredBounds(SkRect& bounds, + const DlImageFilter* filter) { + if (filter) { + if (!filter->map_local_bounds(bounds, bounds)) { + return false; + } + } + return true; +} + +bool DlCanvasToReceiver::AdjustBoundsForPaint(SkRect& bounds, + const DlPaint* in_paint, + DisplayListAttributeFlags flags) { + if (flags.ignores_paint()) { + return true; + } + + const DlPaint& paint = in_paint ? *in_paint : kDefaultPaint_; + if (flags.is_geometric()) { + bool is_stroked = flags.is_stroked(paint.getDrawStyle()); + + // Path effect occurs before stroking... + DisplayListSpecialGeometryFlags special_flags = + flags.WithPathEffect(paint.getPathEffectPtr(), is_stroked); + if (paint.getPathEffect()) { + auto effect_bounds = paint.getPathEffect()->effect_bounds(bounds); + if (!effect_bounds.has_value()) { + return false; + } + bounds = effect_bounds.value(); + } + + if (is_stroked) { + // Determine the max multiplier to the stroke width first. + SkScalar pad = 1.0f; + if (paint.getStrokeJoin() == DlStrokeJoin::kMiter && + special_flags.may_have_acute_joins()) { + pad = std::max(pad, paint.getStrokeMiter()); + } + if (paint.getStrokeCap() == DlStrokeCap::kSquare && + special_flags.may_have_diagonal_caps()) { + pad = std::max(pad, SK_ScalarSqrt2); + } + SkScalar min_stroke_width = 0.01; + pad *= std::max(paint.getStrokeWidth() * 0.5f, min_stroke_width); + bounds.outset(pad, pad); + } + } + + if (flags.applies_mask_filter()) { + auto filter = paint.getMaskFilter(); + if (filter) { + switch (filter->type()) { + case DlMaskFilterType::kBlur: { + FML_DCHECK(filter->asBlur()); + SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0; + bounds.outset(mask_sigma_pad, mask_sigma_pad); + } + } + } + } + + if (flags.applies_image_filter()) { + return ComputeFilteredBounds(bounds, paint.getImageFilter().get()); + } + + return true; +} + +bool DlCanvasToReceiver::AccumulateUnbounded() { + return receiver_->accumulateUnboundedForNextOp(); +} + +bool DlCanvasToReceiver::AccumulateOpBounds(SkRect& bounds, + const DlPaint* paint, + DisplayListAttributeFlags flags) { + if (AdjustBoundsForPaint(bounds, paint, flags)) { + return AccumulateBounds(bounds); + } else { + return AccumulateUnbounded(); + } +} +bool DlCanvasToReceiver::AccumulateBounds(SkRect& bounds) { + return receiver_->accumulateLocalBoundsForNextOp(bounds); +} + +bool DlCanvasToReceiver::paint_nops_on_transparency(const DlPaint* paint) { + if (paint == nullptr) { + return true; + } + + // SkImageFilter::canComputeFastBounds tests for transparency behavior + // This test assumes that the blend mode checked down below will + // NOP on transparent black. + if (paint->getImageFilterPtr() && + paint->getImageFilterPtr()->modifies_transparent_black()) { + return false; + } + + // We filter the transparent black that is used for the background of a + // saveLayer and make sure it returns transparent black. If it does, then + // the color filter will leave all area surrounding the contents of the + // save layer untouched out to the edge of the output surface. + // This test assumes that the blend mode checked down below will + // NOP on transparent black. + if (paint->getColorFilterPtr() && + paint->getColorFilterPtr()->modifies_transparent_black()) { + return false; + } + + // Unusual blendmodes require us to process a saved layer + // even with operations outside the clip. + // For example, DstIn is used by masking layers. + // https://code.google.com/p/skia/issues/detail?id=1291 + // https://crbug.com/401593 + switch (paint->getBlendMode()) { + // For each of the following transfer modes, if the source + // alpha is zero (our transparent black), the resulting + // blended pixel is not necessarily equal to the original + // destination pixel. + // Mathematically, any time in the following equations where + // the result is not d assuming source is 0 + case DlBlendMode::kClear: // r = 0 + case DlBlendMode::kSrc: // r = s + case DlBlendMode::kSrcIn: // r = s * da + case DlBlendMode::kDstIn: // r = d * sa + case DlBlendMode::kSrcOut: // r = s * (1-da) + case DlBlendMode::kDstATop: // r = d*sa + s*(1-da) + case DlBlendMode::kModulate: // r = s*d + return false; + break; + + // And in these equations, the result must be d if the + // source is 0 + case DlBlendMode::kDst: // r = d + case DlBlendMode::kSrcOver: // r = s + (1-sa)*d + case DlBlendMode::kDstOver: // r = d + (1-da)*s + case DlBlendMode::kDstOut: // r = d * (1-sa) + case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa) + case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa) + case DlBlendMode::kPlus: // r = min(s + d, 1) + case DlBlendMode::kScreen: // r = s + d - s*d + case DlBlendMode::kOverlay: // multiply or screen, depending on dest + case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa), + // ra = kSrcOver + case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa), + // ra = kSrcOver + case DlBlendMode::kColorDodge: // brighten destination to reflect source + case DlBlendMode::kColorBurn: // darken destination to reflect source + case DlBlendMode::kHardLight: // multiply or screen, depending on source + case DlBlendMode::kSoftLight: // lighten or darken, depending on source + case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)), + // ra = kSrcOver + case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver + case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d + case DlBlendMode::kHue: // ra = kSrcOver + case DlBlendMode::kSaturation: // ra = kSrcOver + case DlBlendMode::kColor: // ra = kSrcOver + case DlBlendMode::kLuminosity: // ra = kSrcOver + return true; + break; + } +} + +DlColor DlCanvasToReceiver::GetEffectiveColor(const DlPaint& paint, + DisplayListAttributeFlags flags) { + DlColor color; + if (flags.applies_color()) { + const DlColorSource* source = paint.getColorSourcePtr(); + if (source) { + if (source->asColor()) { + color = source->asColor()->color(); + } else { + color = source->is_opaque() ? DlColor::kBlack() : kAnyColor; + } + } else { + color = paint.getColor(); + } + } else if (flags.applies_alpha()) { + // If the operation applies alpha, but not color, then the only impact + // of the alpha is to modulate the output towards transparency. + // We can not guarantee an opaque source even if the alpha is opaque + // since that would require knowing something about the colors that + // the alpha is modulating, but we can guarantee a transparent source + // if the alpha is 0. + color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor; + } else { + color = kAnyColor; + } + if (flags.applies_image_filter()) { + auto filter = paint.getImageFilterPtr(); + if (filter) { + if (!color.isTransparent() || filter->modifies_transparent_black()) { + color = kAnyColor; + } + } + } + if (flags.applies_color_filter()) { + auto filter = paint.getColorFilterPtr(); + if (filter) { + if (!color.isTransparent() || filter->modifies_transparent_black()) { + color = kAnyColor; + } + } + } + return color; +} + +DlCanvasToReceiver::OpResult DlCanvasToReceiver::PaintResult( + const DlPaint& paint, + DisplayListAttributeFlags flags) { + if (current_layer_->state_is_nop_) { + return OpResult::kNoEffect; + } + if (flags.applies_blend()) { + switch (paint.getBlendMode()) { + // Nop blend mode (singular, there is only one) + case DlBlendMode::kDst: + return OpResult::kNoEffect; + + // Always clears pixels blend mode (singular, there is only one) + case DlBlendMode::kClear: + return OpResult::kPreservesTransparency; + + case DlBlendMode::kHue: + case DlBlendMode::kSaturation: + case DlBlendMode::kColor: + case DlBlendMode::kLuminosity: + case DlBlendMode::kColorBurn: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kNoEffect + : OpResult::kAffectsAll; + + // kSrcIn modifies pixels towards transparency + case DlBlendMode::kSrcIn: + return OpResult::kPreservesTransparency; + + // These blend modes preserve destination alpha + case DlBlendMode::kSrcATop: + case DlBlendMode::kDstOut: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kNoEffect + : OpResult::kPreservesTransparency; + + // Always destructive blend modes, potentially not affecting transparency + case DlBlendMode::kSrc: + case DlBlendMode::kSrcOut: + case DlBlendMode::kDstATop: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kPreservesTransparency + : OpResult::kAffectsAll; + + // The kDstIn blend mode modifies the destination unless the + // source color is opaque. + case DlBlendMode::kDstIn: + return GetEffectiveColor(paint, flags).isOpaque() + ? OpResult::kNoEffect + : OpResult::kPreservesTransparency; + + // The next group of blend modes modifies the destination unless the + // source color is transparent. + case DlBlendMode::kSrcOver: + case DlBlendMode::kDstOver: + case DlBlendMode::kXor: + case DlBlendMode::kPlus: + case DlBlendMode::kScreen: + case DlBlendMode::kMultiply: + case DlBlendMode::kOverlay: + case DlBlendMode::kDarken: + case DlBlendMode::kLighten: + case DlBlendMode::kColorDodge: + case DlBlendMode::kHardLight: + case DlBlendMode::kSoftLight: + case DlBlendMode::kDifference: + case DlBlendMode::kExclusion: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kNoEffect + : OpResult::kAffectsAll; + + // Modulate only leaves the pixel alone when the source is white. + case DlBlendMode::kModulate: + return GetEffectiveColor(paint, flags) == DlColor::kWhite() + ? OpResult::kNoEffect + : OpResult::kPreservesTransparency; + } + } + return OpResult::kAffectsAll; +} + +DlPaint DlCanvasToReceiver::kDefaultPaint_; + +} // namespace flutter diff --git a/display_list/dl_canvas_to_receiver.h b/display_list/dl_canvas_to_receiver.h new file mode 100644 index 0000000000000..ea8acfaffad4c --- /dev/null +++ b/display_list/dl_canvas_to_receiver.h @@ -0,0 +1,573 @@ +// 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. + +#ifndef FLUTTER_DISPLAY_LIST_DL_CANVAS_TO_RECEIVER_H_ +#define FLUTTER_DISPLAY_LIST_DL_CANVAS_TO_RECEIVER_H_ + +#include "flutter/display_list/dl_canvas.h" +#include "flutter/display_list/dl_op_flags.h" +#include "flutter/display_list/dl_op_receiver.h" +#include "flutter/fml/macros.h" + +namespace impeller { +struct Picture; +} + +namespace flutter { + +class DlCanvasReceiver : public DlOpReceiver { + private: + using ClipOp = DlCanvas::ClipOp; + + public: + virtual ~DlCanvasReceiver() = default; + + virtual SkRect base_device_cull_rect() const = 0; + + virtual SkM44 matrix_4x4() const = 0; + virtual SkMatrix matrix_3x3() const { return matrix_4x4().asM33(); } + + virtual SkRect device_cull_rect() const = 0; + virtual SkRect local_cull_rect() const = 0; + virtual bool is_cull_rect_empty() const = 0; + virtual bool content_culled(const SkRect& content_bounds) const = 0; + + /*--------- Methods below here are optional optimizations --------*/ + + /// Optional - only needed if performing bounds culling via the accumulate + /// methods + /// Note: The bounds are only advisory for implementing bounds culling + /// and should not trigger an actual clip operation. + /// The default implementation ignores the advisory information. + /// + /// This method should almost never be used as it breaks the encapsulation + /// of the enclosing clips. However it is needed for practical purposes in + /// some rare cases - such as when a saveLayer is collecting rendering + /// operations prior to applying a filter on the entire layer bounds and + /// some of those operations fall outside the enclosing clip, but their + /// filtered content will spread out from where they were rendered on the + /// layer into the enclosing clipped area. + /// Omitting the |cull_rect| argument, or passing nullptr, will restore the + /// cull rect to the initial value it had when the tracker was constructed. + virtual void resetCullRect(const SkRect* cull_rect = nullptr) {} + + /// Optional - only needed if performing bounds culling via the accumulate + /// methods + /// Note: The bounds are only advisory for implementing bounds culling + /// and should not trigger an actual clip operation. + /// The default implementation ignores the advisory information. + /// + /// This method is used to add an additional culling bounds without + /// actually performing a clip on the destination. The bounds will + /// help avoid recording content of a saveLayer that lies entirely + /// outside the save layer bounds if it had them. + virtual void intersectCullRect(const SkRect& cull_rect) {} + + /// Optional - only needed if implementing the accumulate methods + /// The default implementation returns false to prevent extra work in + /// the adapter. + /// + /// If the receiver is accumulating bounds into, say, an rtree format, + /// or wants granular bounds for any other reason, this query will let + /// the adapter know that it should deliver the bounds of methods such + /// as DrawDisplayList granularly if possible (i.e. if the DL being + /// drawn itself has an RTree for its bounds). + /// + /// Returns: true if the implementation can make use of granular bounds + virtual bool wants_granular_bounds() const { return false; } + + /// Optional - only needed if accumulating bounds or implementing bounds + /// culling + /// The default implementation ignores the bounds and returns true to + /// prevent culling. + /// + /// The indicated bounds were calculated for the next rendering op call. + /// The receiver can ignore them if it is not accumulating the bounds, + /// and can tag them appropriately if recording the bounds per rendering op. + /// The receiver can also indicate if the bounds are clipped out with + /// the returned boolean. + /// + /// Returns: true if the bounds were not clipped + /// or false if there is no way for these bounds to be visible + virtual bool accumulateLocalBoundsForNextOp(const SkRect& r) { return true; } + + /// Optional - only needed if accumulating bounds or implementing bounds + /// culling + /// The default implementation ignores the condition and returns true to + /// prevent culling. + /// + /// The bounds for the next rendering op will be "unbounded" by anything + /// other than the current clip, whether because it is a |drawPaint| or + /// |drawColor| call or because it is rendered with a filter that modifies + /// all pixels (even transparent) out to infinity (or the clip). + /// The receiver can ignore the information if it is not accumulating + /// the bounds, and can tag them appropriately if recording the bounds + /// per rendering op. + /// The receiver can also indicate if the bounds are clipped out with + /// the returned boolean. + /// + /// Returns: true if the bounds were not clipped + /// or false if there is no way for these bounds to be visible + virtual bool accumulateUnboundedForNextOp() { return true; } + + /// Optional - only needed if the provided information is useful to the + /// implementation + /// By default this method will simply call the regular |restore| method + /// and the information will be ignored. + /// + /// Called in lieue of a call to |restore| when the upstream code calls + /// a restore on a |saveLayer| and there is information about the contents + /// of the layer that the receiver might find interesting. + virtual void restoreLayer(const DlImageFilter* filter, + bool layer_content_was_unbounded, + bool layer_could_distribute_opacity) { + restore(); + } + + /// Optional - useful to indicate non-renderable state such as an empty + /// clip or collapsed transform to suspend rendering calls + /// until the state is overridden or popped by a |restore|. + /// By default this method returns false to prevent state culling. + /// + /// The |is_nop| method will be called after any of the clip or + /// transform methods to detect if the current conditions are + /// now a NOP. The receiver will receive no more calls until the + /// associated restore() call which will be dispatched to it, or + /// a subsequent clip or transform "reset" operation. + /// + /// Returns: true if either the clip or transform prevent rendering + virtual bool is_nop() { return false; } +}; + +// The primary class used to build a display list. The list of methods +// here matches the list of methods invoked on a |DlOpReceiver| combined +// with the list of methods invoked on a |DlCanvas|. +class DlCanvasToReceiver : public virtual DlCanvas, // + DisplayListOpFlags { + public: + explicit DlCanvasToReceiver(std::shared_ptr receiver); + + ~DlCanvasToReceiver() = default; + + // |DlCanvas| + SkISize GetBaseLayerSize() const override; + // |DlCanvas| + SkImageInfo GetImageInfo() const override; + + // |DlCanvas| + void Save() override; + + // |DlCanvas| + void SaveLayer(const SkRect* bounds, + const DlPaint* paint = nullptr, + const DlImageFilter* backdrop = nullptr) override; + // |DlCanvas| + void Restore() override; + // |DlCanvas| + int GetSaveCount() const override { + CheckAlive(); + return layer_stack_.size(); + } + // |DlCanvas| + void RestoreToCount(int restore_count) override; + + // |DlCanvas| + void Translate(SkScalar tx, SkScalar ty) override; + // |DlCanvas| + void Scale(SkScalar sx, SkScalar sy) override; + // |DlCanvas| + void Rotate(SkScalar degrees) override; + // |DlCanvas| + void Skew(SkScalar sx, SkScalar sy) override; + + // clang-format off + // 2x3 2D affine subset of a 4x4 transform in row major order + // |DlCanvas| + void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) override; + // full 4x4 transform in row major order + // |DlCanvas| + void TransformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; + // clang-format on + // |DlCanvas| + void TransformReset() override; + // |DlCanvas| + void Transform(const SkMatrix* matrix) override; + // |DlCanvas| + void Transform(const SkM44* matrix44) override; + // |DlCanvas| + void SetTransform(const SkMatrix* matrix) override { + CheckAlive(); + TransformReset(); + Transform(matrix); + } + // |DlCanvas| + void SetTransform(const SkM44* matrix44) override { + CheckAlive(); + TransformReset(); + Transform(matrix44); + } + using DlCanvas::Transform; + + /// Returns the 4x4 full perspective transform representing all transform + /// operations executed so far in this DisplayList within the enclosing + /// save stack. + // |DlCanvas| + SkM44 GetTransformFullPerspective() const override { + CheckAlive(); + return receiver_->matrix_4x4(); + } + /// Returns the 3x3 partial perspective transform representing all transform + /// operations executed so far in this DisplayList within the enclosing + /// save stack. + // |DlCanvas| + SkMatrix GetTransform() const override { + CheckAlive(); + return receiver_->matrix_3x3(); + } + + // |DlCanvas| + void ClipRect(const SkRect& rect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| + void ClipRRect(const SkRRect& rrect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| + void ClipPath(const SkPath& path, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + + /// Conservative estimate of the bounds of all outstanding clip operations + /// measured in the coordinate space within which this DisplayList will + /// be rendered. + // |DlCanvas| + SkRect GetDestinationClipBounds() const override { + CheckAlive(); + return receiver_->device_cull_rect(); + } + /// Conservative estimate of the bounds of all outstanding clip operations + /// transformed into the local coordinate space in which currently + /// recorded rendering operations are interpreted. + // |DlCanvas| + SkRect GetLocalClipBounds() const override { + CheckAlive(); + return receiver_->local_cull_rect(); + } + + /// Return true iff the supplied bounds are easily shown to be outside + /// of the current clip bounds. This method may conservatively return + /// false if it cannot make the determination. + // |DlCanvas| + bool QuickReject(const SkRect& bounds) const override; + + // |DlCanvas| + void DrawPaint(const DlPaint& paint) override; + // |DlCanvas| + void DrawColor(DlColor color, DlBlendMode mode) override; + // |DlCanvas| + void DrawLine(const SkPoint& p0, + const SkPoint& p1, + const DlPaint& paint) override; + // |DlCanvas| + void DrawRect(const SkRect& rect, const DlPaint& paint) override; + // |DlCanvas| + void DrawOval(const SkRect& bounds, const DlPaint& paint) override; + // |DlCanvas| + void DrawCircle(const SkPoint& center, + SkScalar radius, + const DlPaint& paint) override; + // |DlCanvas| + void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override; + // |DlCanvas| + void DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const DlPaint& paint) override; + // |DlCanvas| + void DrawPath(const SkPath& path, const DlPaint& paint) override; + // |DlCanvas| + void DrawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter, + const DlPaint& paint) override; + // |DlCanvas| + void DrawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[], + const DlPaint& paint) override; + // |DlCanvas| + void DrawVertices(const DlVertices* vertices, + DlBlendMode mode, + const DlPaint& paint) override; + using DlCanvas::DrawVertices; + // |DlCanvas| + void DrawImage(const sk_sp& image, + const SkPoint point, + DlImageSampling sampling, + const DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawImageRect( + const sk_sp& image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + const DlPaint* paint = nullptr, + SrcRectConstraint constraint = SrcRectConstraint::kFast) override; + using DlCanvas::DrawImageRect; + // |DlCanvas| + void DrawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + const DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cullRect, + const DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawDisplayList(const sk_sp display_list, + SkScalar opacity = SK_Scalar1) override; + // |DlCanvas| + void DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity = SK_Scalar1) override; + // |DlCanvas| + void DrawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y, + const DlPaint& paint) override; + // |DlCanvas| + void DrawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override; + + // |DlCanvas| + void Flush() override { CheckAlive(); } + + protected: + inline void CheckAlive() const { FML_CHECK(receiver_ != nullptr); } + + std::shared_ptr receiver_; + + bool current_group_opacity_compatibility() { + return current_layer_->is_group_opacity_compatible(); + } + bool current_affects_transparent_layer() { + return current_layer_->affects_transparent_layer(); + } + + DlPaint CurrentAttributes() const { return current_; } + + private: + // Returns whether or not the paint was compatible with opacity inheritance + [[nodiscard]] bool SetAttributesFromPaint( + const DlPaint* paint, + const DisplayListAttributeFlags flags); + + enum class OpResult { + kNoEffect, + kPreservesTransparency, + kAffectsAll, + }; + + class LayerInfo { + public: + explicit LayerInfo( + bool has_layer = false, + const std::shared_ptr& filter = nullptr) + : has_layer_(has_layer), filter_(filter) {} + + bool has_layer() const { return has_layer_; } + bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; } + bool has_compatible_op() const { return has_compatible_op_; } + bool affects_transparent_layer() const { + return affects_transparent_layer_; + } + + void Update(OpResult result, bool can_inherit_opacity) { + switch (result) { + case OpResult::kNoEffect: + // We should have stopped processing the rendering operation + // well before we tried to update the layer information. + FML_DCHECK(result != OpResult::kNoEffect); + return; + + case OpResult::kPreservesTransparency: + break; + + case OpResult::kAffectsAll: + add_visible_op(); + break; + } + + if (can_inherit_opacity) { + add_compatible_op(); + } else { + mark_incompatible(); + } + } + + bool is_group_opacity_compatible() const { + return !cannot_inherit_opacity_; + } + + void mark_incompatible() { cannot_inherit_opacity_ = true; } + + // For now this only allows a single compatible op to mark the + // layer as being compatible with group opacity. If we start + // computing bounds of ops in the Builder methods then we + // can upgrade this to checking for overlapping ops. + // See https://github.com/flutter/flutter/issues/93899 + void add_compatible_op() { + if (!cannot_inherit_opacity_) { + if (has_compatible_op_) { + cannot_inherit_opacity_ = true; + } else { + has_compatible_op_ = true; + } + } + } + + // Records that the current layer contains an op that produces visible + // output on a transparent surface. + void add_visible_op() { affects_transparent_layer_ = true; } + + // The filter to apply to the layer bounds when it is restored + std::shared_ptr filter() { return filter_; } + + // is_unbounded should be set to true if we ever encounter an operation + // on a layer that either is unrestricted (|drawColor| or |drawPaint|) + // or cannot compute its bounds (some effects and filters) and there + // was no outstanding clip op at the time. + // When the layer is restored, the outer layer may then process this + // unbounded state by accumulating its own clip or transferring the + // unbounded state to its own outer layer. + // Typically the DisplayList will have been constructed with a cull + // rect which will act as a default clip for the outermost layer and + // the unbounded state of all sub layers will eventually be caught by + // that cull rect so that the overall unbounded state of the entire + // DisplayList will never be true. + // + // For historical consistency it is worth noting that SkPicture used + // to treat these same conditions as a Nop (they accumulate the + // SkPicture cull rect, but if no cull rect was specified then it is + // an empty Rect and so has no effect on the bounds). + // + // Flutter is unlikely to ever run into this as the Dart mechanisms + // all supply a non-null cull rect for all Dart Picture objects, + // even if that cull rect is kGiantRect. + void set_unbounded() { is_unbounded_ = true; } + + // |is_unbounded| should be called after |getLayerBounds| in case + // a problem was found during the computation of those bounds, + // the layer will have one last chance to flag an unbounded state. + bool is_unbounded() const { return is_unbounded_; } + + private: + bool has_layer_; + bool cannot_inherit_opacity_ = false; + bool has_compatible_op_ = false; + std::shared_ptr filter_; + bool is_unbounded_ = false; + bool state_is_nop_ = false; + bool affects_transparent_layer_ = false; + + friend class DlCanvasToReceiver; + }; + + std::vector layer_stack_; + LayerInfo* current_layer_; + + // Returns the compatibility of a given blend mode for applying an + // inherited opacity value to modulate the visibility of the op. + // For now we only accept SrcOver blend modes but this could be expanded + // in the future to include other (rarely used) modes that also modulate + // the opacity of a rendering operation at the cost of a switch statement + // or lookup table. + static inline bool IsOpacityCompatible(DlBlendMode mode) { + return (mode == DlBlendMode::kSrcOver); + } + + static DisplayListAttributeFlags FlagsForPointMode(PointMode mode); + + bool paint_nops_on_transparency(const DlPaint* paint); + OpResult PaintResult(const DlPaint& paint, + DisplayListAttributeFlags flags = kDrawPaintFlags); + OpResult PaintResult(const DlPaint* paint, + DisplayListAttributeFlags flags = kDrawPaintFlags) { + if (paint) { + return PaintResult(*paint, flags); + } else if (current_layer_->state_is_nop_) { + return OpResult::kNoEffect; + } else { + FML_DCHECK(PaintResult(kDefaultPaint_, flags) == OpResult::kAffectsAll); + return OpResult::kAffectsAll; + } + } + + // kAnyColor is a non-opaque and non-transparent color that will not + // trigger any short-circuit tests about the results of a blend. + static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlpha(0x80); + static_assert(!kAnyColor.isOpaque()); + static_assert(!kAnyColor.isTransparent()); + static DlColor GetEffectiveColor(const DlPaint& paint, + DisplayListAttributeFlags flags); + + // Computes the bounds of an operation adjusted for a given ImageFilter + // and returns whether the computation was possible. If the method + // returns false then the caller should assume the worst about the bounds. + static bool ComputeFilteredBounds(SkRect& bounds, + const DlImageFilter* filter); + + // Adjusts the indicated bounds for the given flags and returns true if + // the calculation was possible, or false if it could not be estimated. + bool AdjustBoundsForPaint(SkRect& bounds, + const DlPaint* paint, + DisplayListAttributeFlags flags); + + // Records the fact that we encountered an op that either could not + // estimate its bounds or that fills all of the destination space. + bool AccumulateUnbounded(); + + // Records the bounds for an op after modifying them according to the + // supplied attribute flags and transforming by the current matrix. + bool AccumulateOpBounds(const SkRect& bounds, + const DlPaint* paint, + DisplayListAttributeFlags flags) { + SkRect safe_bounds = bounds; + return AccumulateOpBounds(safe_bounds, paint, flags); + } + + // Records the bounds for an op after modifying them according to the + // supplied attribute flags and transforming by the current matrix + // and clipping against the current clip. + bool AccumulateOpBounds(SkRect& bounds, + const DlPaint* paint, + DisplayListAttributeFlags flags); + + // Records the given bounds after transforming by the current matrix + // and clipping against the current clip. + bool AccumulateBounds(SkRect& bounds); + + DlPaint current_; + static DlPaint kDefaultPaint_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DL_CANVAS_TO_RECEIVER_H_ diff --git a/display_list/dl_op_flags.h b/display_list/dl_op_flags.h index 9bb6c013f8203..df6aa23990649 100644 --- a/display_list/dl_op_flags.h +++ b/display_list/dl_op_flags.h @@ -75,6 +75,13 @@ class DisplayListFlags { // mitered extensions outside the pre-transformed bounding box. static constexpr int kMayHaveAcuteJoins_ = 1 << 8; + // Some primitives render with a different algorithm on Skia + // when the line width is a hairline and that algorithm does + // not apply inherited opacity appropriately. We mark these + // operations with a flag so that we only disallow opacity + // inheritance on hairlines for those primitives. + static constexpr int kMayHaveTroubleWithHairlines_ = 1 << 9; + static constexpr int kAnySpecialGeometryMask_ = // kMayHaveCaps_ | kMayHaveJoins_ | kButtCapIsSquare_ | // kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_; @@ -220,6 +227,12 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { (style != DlDrawStyle::kFill && has_any(kIsDrawnGeometry_))); } + /// The operation may use an implementation for hairlines that + /// does not inherit opacity properly + constexpr bool may_have_trouble_with_hairlines() const { + return has_any(kMayHaveTroubleWithHairlines_); + } + constexpr bool is_flood() const { return has_any(kFloodsSurface_); } constexpr bool operator==(DisplayListAttributeFlags const& other) const { @@ -336,12 +349,13 @@ class DisplayListOpFlags : DisplayListFlags { kBASE_StrokeOrFillFlags_ // }; static constexpr DisplayListAttributeFlags kDrawPathFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveCaps_ | // - kMayHaveDiagonalCaps_ | // - kMayHaveJoins_ | // - kMayHaveAcuteJoins_ // + kBASE_PaintFlags_ | // + kBASE_StrokeOrFillFlags_ | // + kMayHaveCaps_ | // + kMayHaveDiagonalCaps_ | // + kMayHaveJoins_ | // + kMayHaveAcuteJoins_ | // + kMayHaveTroubleWithHairlines_ // }; static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags{ kBASE_PaintFlags_ | // @@ -350,10 +364,11 @@ class DisplayListOpFlags : DisplayListFlags { kMayHaveDiagonalCaps_ // }; static constexpr DisplayListAttributeFlags kDrawArcWithCenterFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveJoins_ | // - kMayHaveAcuteJoins_ // + kBASE_PaintFlags_ | // + kBASE_StrokeOrFillFlags_ | // + kMayHaveJoins_ | // + kMayHaveAcuteJoins_ | // + kMayHaveTroubleWithHairlines_ // }; static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags{ kBASE_PaintFlags_ | // diff --git a/display_list/dl_op_receiver.h b/display_list/dl_op_receiver.h index 23a0aaf6fd97c..1fe13c6f84eda 100644 --- a/display_list/dl_op_receiver.h +++ b/display_list/dl_op_receiver.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ -#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ +#ifndef FLUTTER_DISPLAY_LIST_DL_OP_RECEIVER_H_ +#define FLUTTER_DISPLAY_LIST_DL_OP_RECEIVER_H_ #include "flutter/display_list/display_list.h" #include "flutter/display_list/dl_blend_mode.h" @@ -49,6 +49,8 @@ class DlOpReceiver { // MaxDrawPointsCount * sizeof(SkPoint) must be less than 1 << 32 static constexpr int kMaxDrawPointsCount = ((1 << 29) - 1); + virtual ~DlOpReceiver() = default; + // The following methods are nearly 1:1 with the methods on DlPaint and // carry the same meanings. Each method sets a persistent value for the // attribute for the rest of the display list or until it is reset by @@ -227,23 +229,23 @@ class DlOpReceiver { uint32_t count, const SkPoint points[]) = 0; virtual void drawVertices(const DlVertices* vertices, DlBlendMode mode) = 0; - virtual void drawImage(const sk_sp image, + virtual void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) = 0; virtual void drawImageRect( - const sk_sp image, + const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint = SrcRectConstraint::kFast) = 0; - virtual void drawImageNine(const sk_sp image, + virtual void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) = 0; - virtual void drawAtlas(const sk_sp atlas, + virtual void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -252,9 +254,9 @@ class DlOpReceiver { DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) = 0; - virtual void drawDisplayList(const sk_sp display_list, + virtual void drawDisplayList(const sk_sp& display_list, SkScalar opacity = SK_Scalar1) = 0; - virtual void drawTextBlob(const sk_sp blob, + virtual void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) = 0; virtual void drawShadow(const SkPath& path, @@ -266,4 +268,4 @@ class DlOpReceiver { } // namespace flutter -#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ +#endif // FLUTTER_DISPLAY_LIST_DL_OP_RECEIVER_H_ diff --git a/display_list/dl_op_recorder.cc b/display_list/dl_op_recorder.cc new file mode 100644 index 0000000000000..549f996a22087 --- /dev/null +++ b/display_list/dl_op_recorder.cc @@ -0,0 +1,691 @@ +// 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 "flutter/display_list/dl_op_recorder.h" + +#include "flutter/display_list/dl_attributes.h" +#include "flutter/display_list/dl_op_records.h" +#include "flutter/display_list/effects/dl_color_source.h" + +namespace flutter { + +#define DL_BUILDER_PAGE 4096 + +// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. +static void CopyV(void* dst) {} + +template +static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { + FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) + << "Expected " << dst << " to be aligned for at least " << alignof(S) + << " bytes."; + // If n is 0, there is nothing to copy into dst from src. + if (n > 0) { + memcpy(dst, src, n * sizeof(S)); + dst = reinterpret_cast(reinterpret_cast(dst) + + n * sizeof(S)); + } + // Repeat for the next items, if any + CopyV(dst, std::forward(rest)...); +} + +DlOpRecorder::DlOpRecorder(const SkRect& cull_rect, bool keep_rtree) { + tracker_ = + std::make_shared(cull_rect, SkMatrix::I()); + if (keep_rtree) { + accumulator_ = std::make_shared(); + } else { + accumulator_ = std::make_shared(); + } + save_infos_.push_back({ + .offset = 0u, + .deferred = false, + .is_layer = false, + }); +} + +template +void* DlOpRecorder::Push(size_t pod, int render_op_inc, Args&&... args) { + size_t size = SkAlignPtr(sizeof(T) + pod); + auto op = reinterpret_cast(storage_.alloc(size)); + new (op) T{std::forward(args)...}; + op->type = T::kType; + op->size = size; + render_op_count_ += render_op_inc; + op_index_++; + return op + 1; +} + +void DlOpRecorder::setAntiAlias(bool aa) { + Push(0, 0, aa); +} +void DlOpRecorder::setDither(bool dither) { + Push(0, 0, dither); +} +void DlOpRecorder::setInvertColors(bool invert) { + Push(0, 0, invert); +} +void DlOpRecorder::setStrokeCap(DlStrokeCap cap) { + Push(0, 0, cap); +} +void DlOpRecorder::setStrokeJoin(DlStrokeJoin join) { + Push(0, 0, join); +} +void DlOpRecorder::setDrawStyle(DlDrawStyle style) { + Push(0, 0, style); +} +void DlOpRecorder::setStrokeWidth(float width) { + Push(0, 0, width); +} +void DlOpRecorder::setStrokeMiter(float limit) { + Push(0, 0, limit); +} +void DlOpRecorder::setColor(DlColor color) { + Push(0, 0, color); +} +void DlOpRecorder::setBlendMode(DlBlendMode mode) { + Push(0, 0, mode); +} + +void DlOpRecorder::setColorSource(const DlColorSource* source) { + if (source == nullptr) { + Push(0, 0); + } else { + is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe(); + switch (source->type()) { + case DlColorSourceType::kColor: { + const DlColorColorSource* color_source = source->asColor(); + setColor(color_source->color()); + break; + } + case DlColorSourceType::kImage: { + const DlImageColorSource* image_source = source->asImage(); + FML_DCHECK(image_source); + Push(0, 0, image_source); + break; + } + case DlColorSourceType::kLinearGradient: { + const DlLinearGradientColorSource* linear = source->asLinearGradient(); + FML_DCHECK(linear); + void* pod = Push(linear->size(), 0); + new (pod) DlLinearGradientColorSource(linear); + break; + } + case DlColorSourceType::kRadialGradient: { + const DlRadialGradientColorSource* radial = source->asRadialGradient(); + FML_DCHECK(radial); + void* pod = Push(radial->size(), 0); + new (pod) DlRadialGradientColorSource(radial); + break; + } + case DlColorSourceType::kConicalGradient: { + const DlConicalGradientColorSource* conical = + source->asConicalGradient(); + FML_DCHECK(conical); + void* pod = Push(conical->size(), 0); + new (pod) DlConicalGradientColorSource(conical); + break; + } + case DlColorSourceType::kSweepGradient: { + const DlSweepGradientColorSource* sweep = source->asSweepGradient(); + FML_DCHECK(sweep); + void* pod = Push(sweep->size(), 0); + new (pod) DlSweepGradientColorSource(sweep); + break; + } + case DlColorSourceType::kRuntimeEffect: { + const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect(); + FML_DCHECK(effect); + Push(0, 0, effect); + break; + } +#ifdef IMPELLER_ENABLE_3D + case DlColorSourceType::kScene: { + const DlSceneColorSource* scene = source->asScene(); + FML_DCHECK(scene); + Push(0, 0, scene); + break; + } +#endif // IMPELLER_ENABLE_3D + } + } +} +void DlOpRecorder::setImageFilter(const DlImageFilter* filter) { + if (filter == nullptr) { + Push(0, 0); + } else { + switch (filter->type()) { + case DlImageFilterType::kBlur: { + const DlBlurImageFilter* blur_filter = filter->asBlur(); + FML_DCHECK(blur_filter); + void* pod = Push(blur_filter->size(), 0); + new (pod) DlBlurImageFilter(blur_filter); + break; + } + case DlImageFilterType::kDilate: { + const DlDilateImageFilter* dilate_filter = filter->asDilate(); + FML_DCHECK(dilate_filter); + void* pod = Push(dilate_filter->size(), 0); + new (pod) DlDilateImageFilter(dilate_filter); + break; + } + case DlImageFilterType::kErode: { + const DlErodeImageFilter* erode_filter = filter->asErode(); + FML_DCHECK(erode_filter); + void* pod = Push(erode_filter->size(), 0); + new (pod) DlErodeImageFilter(erode_filter); + break; + } + case DlImageFilterType::kMatrix: { + const DlMatrixImageFilter* matrix_filter = filter->asMatrix(); + FML_DCHECK(matrix_filter); + void* pod = Push(matrix_filter->size(), 0); + new (pod) DlMatrixImageFilter(matrix_filter); + break; + } + case DlImageFilterType::kCompose: + case DlImageFilterType::kLocalMatrix: + case DlImageFilterType::kColorFilter: { + Push(0, 0, filter); + break; + } + } + } +} +void DlOpRecorder::setColorFilter(const DlColorFilter* filter) { + if (filter == nullptr) { + Push(0, 0); + } else { + switch (filter->type()) { + case DlColorFilterType::kBlend: { + const DlBlendColorFilter* blend_filter = filter->asBlend(); + FML_DCHECK(blend_filter); + void* pod = Push(blend_filter->size(), 0); + new (pod) DlBlendColorFilter(blend_filter); + break; + } + case DlColorFilterType::kMatrix: { + const DlMatrixColorFilter* matrix_filter = filter->asMatrix(); + FML_DCHECK(matrix_filter); + void* pod = Push(matrix_filter->size(), 0); + new (pod) DlMatrixColorFilter(matrix_filter); + break; + } + case DlColorFilterType::kSrgbToLinearGamma: { + void* pod = Push(filter->size(), 0); + new (pod) DlSrgbToLinearGammaColorFilter(); + break; + } + case DlColorFilterType::kLinearToSrgbGamma: { + void* pod = Push(filter->size(), 0); + new (pod) DlLinearToSrgbGammaColorFilter(); + break; + } + } + } +} +void DlOpRecorder::setPathEffect(const DlPathEffect* effect) { + if (effect == nullptr) { + Push(0, 0); + } else { + switch (effect->type()) { + case DlPathEffectType::kDash: { + const DlDashPathEffect* dash_effect = effect->asDash(); + void* pod = Push(dash_effect->size(), 0); + new (pod) DlDashPathEffect(dash_effect); + break; + } + } + } +} +void DlOpRecorder::setMaskFilter(const DlMaskFilter* filter) { + if (filter == nullptr) { + Push(0, 0); + } else { + switch (filter->type()) { + case DlMaskFilterType::kBlur: { + const DlBlurMaskFilter* blur_filter = filter->asBlur(); + FML_DCHECK(blur_filter); + void* pod = Push(blur_filter->size(), 0); + new (pod) DlBlurMaskFilter(blur_filter); + break; + } + } + } +} + +void DlOpRecorder::save() { + save_infos_.push_back({ + .offset = 0u, + .deferred = true, + .is_layer = false, + }); + tracker_->save(); + accumulator_->save(); +} +void DlOpRecorder::ResolveDeferredSave() { + SaveInfo& save_info_ref = save_infos_.back(); + if (save_info_ref.deferred) { + FML_DCHECK(save_info_ref.is_layer == false); + FML_DCHECK(save_info_ref.offset == 0u); + save_info_ref.offset = storage_.used(); + save_info_ref.deferred = false; + Push(0, 1); + } +} +void DlOpRecorder::saveLayer(const SkRect* bounds, + const SaveLayerOptions options, + const DlImageFilter* backdrop) { + save_infos_.push_back({ + .offset = storage_.used(), + .deferred = false, + .is_layer = true, + }); + tracker_->save(); + accumulator_->save(); + if (backdrop) { + bounds // + ? Push(0, 1, options, *bounds, backdrop) + : Push(0, 1, options, backdrop); + } else { + bounds // + ? Push(0, 1, options, *bounds) + : Push(0, 1, options); + } +} + +void DlOpRecorder::restore() { + FML_DCHECK(!save_infos_.empty()); + { // Ensure all uses of save_info_ref occur before pop_back() + const SaveInfo& save_info_ref = save_infos_.back(); + if (save_info_ref.is_layer) { + // This should only happen when unrolling the save stack + // in the Build() method. + restoreLayer(nullptr, false, false); + return; + } + + if (!save_info_ref.deferred) { + SaveOpBase* op = + reinterpret_cast(storage_.get() + save_info_ref.offset); + FML_DCHECK(op->type == DisplayListOpType::kSave); + op->restore_index = op_index_; + + Push(0, 1); + } + } // save_info_ref no longer accessible + save_infos_.pop_back(); + + tracker_->restore(); + accumulator_->restore(); +} +void DlOpRecorder::restoreLayer(const DlImageFilter* filter, + bool layer_content_was_unbounded, + bool layer_could_distribute_opacity) { + FML_DCHECK(!save_infos_.empty()); + { // Ensure all uses of save_info_ref occur before pop_back() + SaveInfo& save_info_ref = save_infos_.back(); + FML_DCHECK(save_info_ref.is_layer == true); + FML_DCHECK(save_info_ref.deferred == false); + + SaveOpBase* op = + reinterpret_cast(storage_.get() + save_info_ref.offset); + FML_DCHECK(op->type == DisplayListOpType::kSaveLayer || + op->type == DisplayListOpType::kSaveLayerBounds || + op->type == DisplayListOpType::kSaveLayerBackdrop || + op->type == DisplayListOpType::kSaveLayerBackdropBounds); + op->restore_index = op_index_; + if (layer_could_distribute_opacity) { + op->options = op->options.with_can_distribute_opacity(); + } + } // save_info_ref no longer accessible + save_infos_.pop_back(); + + Push(0, 1); + + // Manage the layer bounds before we push the restore op so that any + // bounds we need to adjust get tagged on the RestoreOp rather than + // the rendering op that follows it. + + // Restore the tracker before we manage the layer bounds so that we use + // the enclosing cull_rect and transform for filtering bounds. + tracker_->restore(); + const SkRect clip = tracker_->device_cull_rect(); + + // As we pop the accumulator we will adjust the bounds associated with + // the layer content by the layer filter. + // We have already restored the tracker so that the cull_rect information + // we use in the adjustment is from the environment outside of the layer. + // If there is a failure in converting the bounds within the layer due + // to an issue with the layer filter, or if the content of the layer + // was already unbounded, we will propagate the unbounded status to the + // enclosing layer. + if (filter) { + const SkMatrix matrix = tracker_->matrix_3x3(); + if (!accumulator_->restore( + [filter, matrix](const SkRect& input, SkRect& output) { + SkIRect output_bounds; + bool ret = filter->map_device_bounds(input.roundOut(), matrix, + output_bounds); + output.set(output_bounds); + return ret; + }, + &clip)) { + layer_content_was_unbounded = true; + } + } else { + accumulator_->restore(); + } + + if (layer_content_was_unbounded) { + // Ideally we would insert this back in the list of rects with the + // OpID of the original SaveLayer... + if (!clip.isEmpty()) { + accumulator_->accumulate(clip, render_op_count_); + } + } +} + +void DlOpRecorder::translate(SkScalar tx, SkScalar ty) { + ResolveDeferredSave(); + tracker_->translate(tx, ty); + Push(0, 1, tx, ty); +} +void DlOpRecorder::scale(SkScalar sx, SkScalar sy) { + ResolveDeferredSave(); + tracker_->scale(sx, sy); + Push(0, 1, sx, sy); +} +void DlOpRecorder::rotate(SkScalar degrees) { + ResolveDeferredSave(); + tracker_->rotate(degrees); + Push(0, 1, degrees); +} +void DlOpRecorder::skew(SkScalar sx, SkScalar sy) { + ResolveDeferredSave(); + tracker_->skew(sx, sy); + Push(0, 1, sx, sy); +} + +// clang-format off + +// 2x3 2D affine subset of a 4x4 transform in row major order +void DlOpRecorder::transform2DAffine( + SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) { + ResolveDeferredSave(); + tracker_->transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + Push(0, 1, + mxx, mxy, mxt, + myx, myy, myt); +} +// full 4x4 transform in row major order +void DlOpRecorder::transformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { + ResolveDeferredSave(); + tracker_->transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + Push(0, 1, + mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); +} +// clang-format on +void DlOpRecorder::transformReset() { + ResolveDeferredSave(); + tracker_->setIdentity(); + Push(0, 0); +} + +void DlOpRecorder::clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) { + ResolveDeferredSave(); + tracker_->clipRect(rect, clip_op, is_aa); + if (!tracker_->is_cull_rect_empty()) { + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, 1, rect, is_aa); + break; + case ClipOp::kDifference: + Push(0, 1, rect, is_aa); + break; + } + } +} + +void DlOpRecorder::clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { + ResolveDeferredSave(); + tracker_->clipRRect(rrect, clip_op, is_aa); + if (!tracker_->is_cull_rect_empty()) { + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, 1, rrect, is_aa); + break; + case ClipOp::kDifference: + Push(0, 1, rrect, is_aa); + break; + } + } +} + +void DlOpRecorder::clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) { + ResolveDeferredSave(); + tracker_->clipPath(path, clip_op, is_aa); + if (!tracker_->is_cull_rect_empty()) { + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, 1, path, is_aa); + break; + case ClipOp::kDifference: + Push(0, 1, path, is_aa); + break; + } + } +} +void DlOpRecorder::resetCullRect(const SkRect* cull_rect) { + tracker_->resetCullRect(cull_rect); +} +void DlOpRecorder::intersectCullRect(const SkRect& cull_rect) { + tracker_->clipRect(cull_rect, DlCanvas::ClipOp::kIntersect, false); +} + +void DlOpRecorder::drawPaint() { + Push(0, 1); +} +void DlOpRecorder::drawColor(DlColor color, DlBlendMode mode) { + Push(0, 1, color, mode); +} +void DlOpRecorder::drawLine(const SkPoint& p0, const SkPoint& p1) { + Push(0, 1, p0, p1); +} +void DlOpRecorder::drawRect(const SkRect& rect) { + Push(0, 1, rect); +} +void DlOpRecorder::drawOval(const SkRect& bounds) { + Push(0, 1, bounds); +} +void DlOpRecorder::drawCircle(const SkPoint& center, SkScalar radius) { + Push(0, 1, center, radius); +} +void DlOpRecorder::drawRRect(const SkRRect& rrect) { + Push(0, 1, rrect); +} +void DlOpRecorder::drawDRRect(const SkRRect& outer, const SkRRect& inner) { + Push(0, 1, outer, inner); +} +void DlOpRecorder::drawPath(const SkPath& path) { + Push(0, 1, path); +} + +void DlOpRecorder::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + Push(0, 1, bounds, start, sweep, useCenter); +} + +void DlOpRecorder::drawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[]) { + FML_DCHECK(count > 0); + FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); + int bytes = count * sizeof(SkPoint); + void* data_ptr; + switch (mode) { + case PointMode::kPoints: + data_ptr = Push(bytes, 1, count); + break; + case PointMode::kLines: + data_ptr = Push(bytes, 1, count); + break; + case PointMode::kPolygon: + data_ptr = Push(bytes, 1, count); + break; + default: + FML_UNREACHABLE(); + return; + } + CopyV(data_ptr, pts, count); +} +void DlOpRecorder::drawVertices(const DlVertices* vertices, DlBlendMode mode) { + void* pod = Push(vertices->size(), 1, mode); + new (pod) DlVertices(vertices); +} + +void DlOpRecorder::drawImage(const sk_sp& image, + const SkPoint point, + DlImageSampling sampling, + bool render_with_attributes) { + render_with_attributes + ? Push(0, 1, image, point, sampling) + : Push(0, 1, image, point, sampling); + is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); +} +void DlOpRecorder::drawImageRect(const sk_sp& image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + bool render_with_attributes, + SrcRectConstraint constraint) { + Push(0, 1, image, src, dst, sampling, // + render_with_attributes, constraint); + is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); +} +void DlOpRecorder::drawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + bool render_with_attributes) { + render_with_attributes + ? Push(0, 1, image, center, dst, filter) + : Push(0, 1, image, center, dst, filter); + is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); +} +void DlOpRecorder::drawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cull_rect, + bool render_with_attributes) { + int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); + void* data_ptr; + if (colors != nullptr) { + bytes += count * sizeof(DlColor); + if (cull_rect != nullptr) { + data_ptr = + Push(bytes, 1, atlas, count, mode, sampling, true, + *cull_rect, render_with_attributes); + } else { + data_ptr = Push(bytes, 1, atlas, count, mode, sampling, true, + render_with_attributes); + } + CopyV(data_ptr, xform, count, tex, count, colors, count); + } else { + if (cull_rect != nullptr) { + data_ptr = + Push(bytes, 1, atlas, count, mode, sampling, false, + *cull_rect, render_with_attributes); + } else { + data_ptr = Push(bytes, 1, atlas, count, mode, sampling, + false, render_with_attributes); + } + CopyV(data_ptr, xform, count, tex, count); + } + is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe(); +} + +void DlOpRecorder::drawDisplayList(const sk_sp& display_list, + SkScalar opacity) { + Push(0, 1, display_list, opacity); + nested_op_count_ += display_list->op_count(true) - 1; + nested_bytes_ += display_list->bytes(true); + is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe(); +} +void DlOpRecorder::drawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y) { + Push(0, 1, blob, x, y); +} +void DlOpRecorder::drawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { + transparent_occluder // + ? Push(0, 1, path, color, elevation, dpr) + : Push(0, 1, path, color, elevation, dpr); +} + +bool DlOpRecorder::accumulateLocalBoundsForNextOp(const SkRect& r) { + if (!r.isEmpty()) { + SkRect bounds = r; + tracker_->mapRect(&bounds); + if (bounds.intersect(tracker_->device_cull_rect())) { + accumulator_->accumulate(bounds, render_op_count_); + return true; + } + } + return false; +} +bool DlOpRecorder::accumulateUnboundedForNextOp() { + SkRect clip = tracker_->device_cull_rect(); + if (!clip.isEmpty()) { + accumulator_->accumulate(clip, render_op_count_); + return true; + } + return false; +} + +sk_sp DlOpRecorder::Build(bool can_distribute_opacity, + bool affects_transparent_layer) { + if (!storage_.is_valid()) { + FML_DCHECK(storage_.is_valid()); + return nullptr; + } + + while (save_infos_.size() > 1u) { + restore(); + } + + auto rtree = accumulator_->rtree(); + // It is faster to ask the completed rtree for bounds than to ask + // the accumulator to run through all of its rects for the bounds. + auto bounds = rtree ? rtree->bounds() : accumulator_->bounds(); + + return sk_sp( + new DisplayList(storage_.take(), render_op_count_, nested_bytes_, + nested_op_count_, bounds, can_distribute_opacity, + is_ui_thread_safe_, affects_transparent_layer, rtree)); +} + +} // namespace flutter diff --git a/display_list/dl_op_recorder.h b/display_list/dl_op_recorder.h new file mode 100644 index 0000000000000..438fcfeebbb29 --- /dev/null +++ b/display_list/dl_op_recorder.h @@ -0,0 +1,210 @@ +// 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. + +#ifndef FLUTTER_DISPLAY_LIST_DL_OP_RECORDER_H_ +#define FLUTTER_DISPLAY_LIST_DL_OP_RECORDER_H_ + +#include "flutter/display_list/dl_canvas_to_receiver.h" +#include "flutter/display_list/dl_op_receiver.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/display_list/utils/dl_matrix_clip_tracker.h" + +namespace flutter { + +class DisplayList; + +//------------------------------------------------------------------------------ +/// @brief An implementation of DlOpReceiver that records the calls into +/// a buffer, typically driven from a DisplayListBuilder. +/// +class DlOpRecorder : public DlCanvasReceiver { + private: + using ClipOp = DlCanvas::ClipOp; + + public: + static constexpr SkRect kMaxCullRect = + SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); + + DlOpRecorder(const SkRect& cull_rect = kMaxCullRect, bool keep_rtree = false); + + ~DlOpRecorder() = default; + + // | DlCanvasReceiver| + SkRect base_device_cull_rect() const override { + return tracker_->base_device_cull_rect(); + } + SkRect device_cull_rect() const override { + return tracker_->device_cull_rect(); + } + SkRect local_cull_rect() const override { + return tracker_->local_cull_rect(); + } + bool is_cull_rect_empty() const override { + return tracker_->is_cull_rect_empty(); + } + bool content_culled(const SkRect& content_bounds) const override { + return tracker_->content_culled(content_bounds); + } + + // | DlCanvasReceiver| + SkM44 matrix_4x4() const override { return tracker_->matrix_4x4(); } + SkMatrix matrix_3x3() const override { return tracker_->matrix_3x3(); } + + // | DlCanvasReceiver| + void resetCullRect(const SkRect* cull_rect = nullptr) override; + void intersectCullRect(const SkRect& cull_rect) override; + + // | DlCanvasReceiver| + bool wants_granular_bounds() const override { + return accumulator_->type() == BoundsAccumulator::Type::kRTree; + } + + // |DlOpReceiver| all set methods + void setAntiAlias(bool aa) override; + void setDither(bool dither) override; + void setDrawStyle(DlDrawStyle style) override; + void setColor(DlColor color) override; + void setStrokeWidth(float width) override; + void setStrokeMiter(float limit) override; + void setStrokeCap(DlStrokeCap cap) override; + void setStrokeJoin(DlStrokeJoin join) override; + void setColorSource(const DlColorSource* source) override; + void setColorFilter(const DlColorFilter* filter) override; + void setInvertColors(bool invert) override; + void setBlendMode(DlBlendMode mode) override; + void setPathEffect(const DlPathEffect* effect) override; + void setMaskFilter(const DlMaskFilter* filter) override; + void setImageFilter(const DlImageFilter* filter) override; + + // |DlOpReceiver| + void save() override; + // |DlOpReceiver| + void saveLayer(const SkRect* bounds, + const SaveLayerOptions options, + const DlImageFilter* backdrop = nullptr) override; + + // |DlOpReceiver| + void restore() override; + // |DlCanvasReceiver| + void restoreLayer(const DlImageFilter*, + bool layer_content_was_unbounded, + bool layer_could_distribute_opacity) override; + + // |DlOpReceiver| all transform methods + void translate(SkScalar tx, SkScalar ty) override; + void scale(SkScalar sx, SkScalar sy) override; + void rotate(SkScalar degrees) override; + void skew(SkScalar sx, SkScalar sy) override; + + // clang-format off + // |DlOpReceiver| + void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) override; + // |DlOpReceiver| + void transformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; + // clang-format on + + // |DlOpReceiver| + void transformReset() override; + + // |DlOpReceiver| all clip methods + void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; + void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; + void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override; + + // |DlOpReceiver| all render methods + void drawColor(DlColor color, DlBlendMode mode) override; + void drawPaint() override; + void drawLine(const SkPoint& p0, const SkPoint& p1) override; + void drawRect(const SkRect& rect) override; + void drawOval(const SkRect& bounds) override; + void drawCircle(const SkPoint& center, SkScalar radius) override; + void drawRRect(const SkRRect& rrect) override; + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawPath(const SkPath& path) override; + void drawArc(const SkRect& oval_bounds, + SkScalar start_degrees, + SkScalar sweep_degrees, + bool use_center) override; + void drawPoints(PointMode mode, + uint32_t count, + const SkPoint points[]) override; + void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; + void drawImage(const sk_sp& image, + const SkPoint point, + DlImageSampling sampling, + bool render_with_attributes) override; + void drawImageRect( + const sk_sp& image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + bool render_with_attributes, + SrcRectConstraint constraint = SrcRectConstraint::kFast) override; + void drawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + bool render_with_attributes) override; + void drawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cull_rect, + bool render_with_attributes) override; + void drawDisplayList(const sk_sp& display_list, + SkScalar opacity = SK_Scalar1) override; + void drawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y) override; + void drawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override; + + bool accumulateLocalBoundsForNextOp(const SkRect& r) override; + bool accumulateUnboundedForNextOp() override; + + bool is_nop() override { return tracker_->is_cull_rect_empty(); } + + sk_sp Build(bool can_distribute_opacity = false, + bool affects_transparent_layer = true); + + private: + std::shared_ptr tracker_; + std::shared_ptr accumulator_; + DisplayList::DlStorage storage_; + + struct SaveInfo { + size_t offset; + bool deferred; + bool is_layer; + }; + std::vector save_infos_; + void ResolveDeferredSave(); + + int render_op_count_ = 0; + int op_index_ = 0; + + // bytes and ops from |drawPicture| and |drawDisplayList| + size_t nested_bytes_ = 0; + int nested_op_count_ = 0; + + bool is_ui_thread_safe_ = true; + + template + void* Push(size_t extra, int op_inc, Args&&... args); +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DL_OP_RECORDER_H_ diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index 47cdeb9d0e6fb..88876f3956141 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -19,7 +19,7 @@ namespace flutter { // Structure holding the information necessary to dispatch and // potentially cull the DLOps during playback. // -// Generally drawing ops will execute as long as |cur_index| +// Generally drawing ops will execute as long as |cur_render_index| // is at or after |next_render_index|, so setting the latter // to 0 will render all primitives and setting it to MAX_INT // will skip all remaining rendering primitives. @@ -40,7 +40,7 @@ namespace flutter { struct DispatchContext { DlOpReceiver& receiver; - int cur_index; + int cur_render_index; int next_render_index; int next_restore_index; @@ -328,6 +328,7 @@ struct SaveOpBase : DLOp { bool needed = ctx.next_render_index <= restore_index; ctx.save_infos.emplace_back(ctx.next_restore_index, needed); ctx.next_restore_index = restore_index; + ctx.cur_render_index++; return needed; } }; @@ -417,25 +418,44 @@ struct SaveLayerBackdropBoundsOp final : SaveOpBase { : DisplayListCompare::kNotEqual; } }; +// The base object for the restore() op // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) -struct RestoreOp final : DLOp { +struct RestoreOpBase : DLOp { + RestoreOpBase() {} + + inline bool restore_needed(DispatchContext& ctx) const { + bool restore_needed; + { + // ensure all use of save_infos.back happens before the pop + DispatchContext::SaveInfo& info = ctx.save_infos.back(); + restore_needed = info.save_was_needed; + ctx.next_restore_index = info.previous_restore_index; + } + ctx.cur_render_index++; + ctx.save_infos.pop_back(); + return restore_needed; + } +}; +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct RestoreOp final : RestoreOpBase { static const auto kType = DisplayListOpType::kRestore; RestoreOp() {} void dispatch(DispatchContext& ctx) const { - DispatchContext::SaveInfo& info = ctx.save_infos.back(); - if (info.save_was_needed) { + if (restore_needed(ctx)) { ctx.receiver.restore(); } - ctx.next_restore_index = info.previous_restore_index; - ctx.save_infos.pop_back(); } }; struct TransformClipOpBase : DLOp { - inline bool op_needed(const DispatchContext& context) const { - return context.next_render_index <= context.next_restore_index; + inline bool tx_clip_needed(DispatchContext& ctx) const { + // We only dispatch a transform or clip if we are going to render + // something before it gets erased by the next restore. + bool tx_clip_needed = (ctx.next_render_index <= ctx.next_restore_index); + ctx.cur_render_index++; + return tx_clip_needed; } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes @@ -449,7 +469,7 @@ struct TranslateOp final : TransformClipOpBase { const SkScalar ty; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.translate(tx, ty); } } @@ -465,7 +485,7 @@ struct ScaleOp final : TransformClipOpBase { const SkScalar sy; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.scale(sx, sy); } } @@ -479,7 +499,7 @@ struct RotateOp final : TransformClipOpBase { const SkScalar degrees; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.rotate(degrees); } } @@ -495,7 +515,7 @@ struct SkewOp final : TransformClipOpBase { const SkScalar sy; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.skew(sx, sy); } } @@ -515,7 +535,7 @@ struct Transform2DAffineOp final : TransformClipOpBase { const SkScalar myx, myy, myt; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.transform2DAffine(mxx, mxy, mxt, // myx, myy, myt); } @@ -544,7 +564,7 @@ struct TransformFullPerspectiveOp final : TransformClipOpBase { const SkScalar mwx, mwy, mwz, mwt; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.transformFullPerspective(mxx, mxy, mxz, mxt, // myx, myy, myz, myt, // mzx, mzy, mzz, mzt, // @@ -560,7 +580,7 @@ struct TransformResetOp final : TransformClipOpBase { TransformResetOp() = default; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (tx_clip_needed(ctx)) { ctx.receiver.transformReset(); } } @@ -587,7 +607,7 @@ struct TransformResetOp final : TransformClipOpBase { const Sk##shapetype shape; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (tx_clip_needed(ctx)) { \ ctx.receiver.clip##shapetype(shape, DlCanvas::ClipOp::k##clipop, \ is_aa); \ } \ @@ -610,7 +630,7 @@ DEFINE_CLIP_SHAPE_OP(RRect, Difference) const SkPath path; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (tx_clip_needed(ctx)) { \ ctx.receiver.clipPath(path, DlCanvas::ClipOp::k##clipop, is_aa); \ } \ } \ @@ -626,8 +646,8 @@ DEFINE_CLIP_PATH_OP(Difference) #undef DEFINE_CLIP_PATH_OP struct DrawOpBase : DLOp { - inline bool op_needed(const DispatchContext& ctx) const { - return ctx.cur_index >= ctx.next_render_index; + inline bool draw_needed(DispatchContext& ctx) const { + return ctx.cur_render_index++ >= ctx.next_render_index; } }; @@ -638,7 +658,7 @@ struct DrawPaintOp final : DrawOpBase { DrawPaintOp() {} void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawPaint(); } } @@ -654,7 +674,7 @@ struct DrawColorOp final : DrawOpBase { const DlBlendMode mode; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawColor(color, mode); } } @@ -674,7 +694,7 @@ struct DrawColorOp final : DrawOpBase { const arg_type arg_name; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (draw_needed(ctx)) { \ ctx.receiver.draw##op_name(arg_name); \ } \ } \ @@ -694,7 +714,7 @@ struct DrawPathOp final : DrawOpBase { const SkPath path; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawPath(path); } } @@ -722,7 +742,7 @@ struct DrawPathOp final : DrawOpBase { const type2 name2; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (draw_needed(ctx)) { \ ctx.receiver.draw##op_name(name1, name2); \ } \ } \ @@ -745,7 +765,7 @@ struct DrawArcOp final : DrawOpBase { const bool center; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawArc(bounds, start, sweep, center); } } @@ -766,7 +786,7 @@ struct DrawArcOp final : DrawOpBase { const uint32_t count; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (draw_needed(ctx)) { \ const SkPoint* pts = reinterpret_cast(this + 1); \ ctx.receiver.drawPoints(DlCanvas::PointMode::mode, count, pts); \ } \ @@ -792,7 +812,7 @@ struct DrawVerticesOp final : DrawOpBase { const DlBlendMode mode; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { const DlVertices* vertices = reinterpret_cast(this + 1); ctx.receiver.drawVertices(vertices, mode); @@ -816,7 +836,7 @@ struct DrawVerticesOp final : DrawOpBase { const sk_sp image; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (draw_needed(ctx)) { \ ctx.receiver.drawImage(image, point, sampling, with_attributes); \ } \ } \ @@ -858,7 +878,7 @@ struct DrawImageRectOp final : DrawOpBase { const sk_sp image; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawImageRect(image, src, dst, sampling, render_with_attributes, constraint); } @@ -891,7 +911,7 @@ struct DrawImageRectOp final : DrawOpBase { const sk_sp image; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (draw_needed(ctx)) { \ ctx.receiver.drawImageNine(image, center, dst, mode, \ render_with_attributes); \ } \ @@ -973,7 +993,7 @@ struct DrawAtlasOp final : DrawAtlasBaseOp { render_with_attributes) {} void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { const SkRSXform* xform = reinterpret_cast(this + 1); const SkRect* tex = reinterpret_cast(xform + count); const DlColor* colors = @@ -1018,7 +1038,7 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp { const SkRect cull_rect; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { const SkRSXform* xform = reinterpret_cast(this + 1); const SkRect* tex = reinterpret_cast(xform + count); const DlColor* colors = @@ -1052,7 +1072,7 @@ struct DrawDisplayListOp final : DrawOpBase { const sk_sp display_list; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawDisplayList(display_list, opacity); } } @@ -1078,7 +1098,7 @@ struct DrawTextBlobOp final : DrawOpBase { const sk_sp blob; void dispatch(DispatchContext& ctx) const { - if (op_needed(ctx)) { + if (draw_needed(ctx)) { ctx.receiver.drawTextBlob(blob, x, y); } } @@ -1101,7 +1121,7 @@ struct DrawTextBlobOp final : DrawOpBase { const SkPath path; \ \ void dispatch(DispatchContext& ctx) const { \ - if (op_needed(ctx)) { \ + if (draw_needed(ctx)) { \ ctx.receiver.drawShadow(path, color, elevation, transparent_occluder, \ dpr); \ } \ diff --git a/shell/common/dl_op_spy.cc b/display_list/dl_op_spy.cc similarity index 91% rename from shell/common/dl_op_spy.cc rename to display_list/dl_op_spy.cc index 15910fc2e85de..993fd09c17ced 100644 --- a/shell/common/dl_op_spy.cc +++ b/display_list/dl_op_spy.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/common/dl_op_spy.h" +#include "flutter/display_list/dl_op_spy.h" namespace flutter { @@ -81,13 +81,13 @@ void DlOpSpy::drawVertices(const DlVertices* vertices, DlBlendMode mode) { // transparent needs examine all the pixels in the image object, which is slow. // Drawing a completely transparent image is not a valid use case, thus, such // case is ignored. -void DlOpSpy::drawImage(const sk_sp image, +void DlOpSpy::drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { did_draw_ = true; } -void DlOpSpy::drawImageRect(const sk_sp image, +void DlOpSpy::drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -95,14 +95,14 @@ void DlOpSpy::drawImageRect(const sk_sp image, SrcRectConstraint constraint) { did_draw_ = true; } -void DlOpSpy::drawImageNine(const sk_sp image, +void DlOpSpy::drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) { did_draw_ = true; } -void DlOpSpy::drawAtlas(const sk_sp atlas, +void DlOpSpy::drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -113,7 +113,7 @@ void DlOpSpy::drawAtlas(const sk_sp atlas, bool render_with_attributes) { did_draw_ = true; } -void DlOpSpy::drawDisplayList(const sk_sp display_list, +void DlOpSpy::drawDisplayList(const sk_sp& display_list, SkScalar opacity) { if (did_draw_ || opacity == 0) { return; @@ -122,7 +122,7 @@ void DlOpSpy::drawDisplayList(const sk_sp display_list, display_list->Dispatch(receiver); did_draw_ |= receiver.did_draw(); } -void DlOpSpy::drawTextBlob(const sk_sp blob, +void DlOpSpy::drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) { did_draw_ |= will_draw_; diff --git a/shell/common/dl_op_spy.h b/display_list/dl_op_spy.h similarity index 90% rename from shell/common/dl_op_spy.h rename to display_list/dl_op_spy.h index 1cb29ff7219aa..501fe37dbc262 100644 --- a/shell/common/dl_op_spy.h +++ b/display_list/dl_op_spy.h @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_DISPLAY_LIST_DL_OP_SPY_H_ -#define FLUTTER_DISPLAY_LIST_DL_OP_SPY_H_ +#pragma once #include "flutter/display_list/dl_op_receiver.h" #include "flutter/display_list/utils/dl_receiver_utils.h" @@ -60,23 +59,23 @@ class DlOpSpy final : public virtual DlOpReceiver, uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; void drawImageRect( - const sk_sp image, + const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint = SrcRectConstraint::kFast) override; - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -85,9 +84,9 @@ class DlOpSpy final : public virtual DlOpReceiver, DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity = SK_Scalar1) override; - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, @@ -105,5 +104,3 @@ class DlOpSpy final : public virtual DlOpReceiver, }; } // namespace flutter - -#endif // FLUTTER_DISPLAY_LIST_DL_OP_SPY_H_ diff --git a/shell/common/dl_op_spy_unittests.cc b/display_list/dl_op_spy_unittests.cc similarity index 99% rename from shell/common/dl_op_spy_unittests.cc rename to display_list/dl_op_spy_unittests.cc index 7aaac1cbe52a8..a976e2cde80a6 100644 --- a/shell/common/dl_op_spy_unittests.cc +++ b/display_list/dl_op_spy_unittests.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_builder.h" -#include "flutter/shell/common/dl_op_spy.h" +#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_op_spy.h" #include "flutter/testing/testing.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkRSXform.h" diff --git a/display_list/dl_paint.h b/display_list/dl_paint.h index 3d9220f57e3d6..88be6ccea9638 100644 --- a/display_list/dl_paint.h +++ b/display_list/dl_paint.h @@ -176,7 +176,7 @@ class DlPaint { return maskFilter_; } const DlMaskFilter* getMaskFilterPtr() const { return maskFilter_.get(); } - DlPaint& setMaskFilter(std::shared_ptr filter) { + DlPaint& setMaskFilter(std::shared_ptr filter) { maskFilter_ = filter; return *this; } @@ -189,7 +189,7 @@ class DlPaint { return pathEffect_; } const DlPathEffect* getPathEffectPtr() const { return pathEffect_.get(); } - DlPaint& setPathEffect(std::shared_ptr pathEffect) { + DlPaint& setPathEffect(std::shared_ptr pathEffect) { pathEffect_ = pathEffect; return *this; } diff --git a/display_list/dl_vertices.h b/display_list/dl_vertices.h index 7c3ed34253713..b6749fcfe960c 100644 --- a/display_list/dl_vertices.h +++ b/display_list/dl_vertices.h @@ -279,7 +279,7 @@ class DlVertices { return static_cast(base) + offset; } - friend class DisplayListBuilder; + friend class DlOpRecorder; }; } // namespace flutter diff --git a/display_list/dl_vertices_unittests.cc b/display_list/dl_vertices_unittests.cc index f30f473b1c31f..4a18db7d2f903 100644 --- a/display_list/dl_vertices_unittests.cc +++ b/display_list/dl_vertices_unittests.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_vertices.h" #include "flutter/display_list/testing/dl_test_equality.h" #include "flutter/display_list/utils/dl_comparable.h" diff --git a/display_list/effects/dl_color_source.h b/display_list/effects/dl_color_source.h index 11a141da9c905..343e36031f462 100644 --- a/display_list/effects/dl_color_source.h +++ b/display_list/effects/dl_color_source.h @@ -418,7 +418,7 @@ class DlLinearGradientColorSource final : public DlGradientColorSourceBase { SkPoint end_point_; friend class DlColorSource; - friend class DisplayListBuilder; + friend class DlOpRecorder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlLinearGradientColorSource); }; @@ -481,7 +481,7 @@ class DlRadialGradientColorSource final : public DlGradientColorSourceBase { SkScalar radius_; friend class DlColorSource; - friend class DisplayListBuilder; + friend class DlOpRecorder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlRadialGradientColorSource); }; @@ -557,7 +557,7 @@ class DlConicalGradientColorSource final : public DlGradientColorSourceBase { SkScalar end_radius_; friend class DlColorSource; - friend class DisplayListBuilder; + friend class DlOpRecorder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlConicalGradientColorSource); }; @@ -625,7 +625,7 @@ class DlSweepGradientColorSource final : public DlGradientColorSourceBase { SkScalar end_; friend class DlColorSource; - friend class DisplayListBuilder; + friend class DlOpRecorder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlSweepGradientColorSource); }; diff --git a/display_list/effects/dl_path_effect.h b/display_list/effects/dl_path_effect.h index 0f9d0be60fa54..5d27ada061655 100644 --- a/display_list/effects/dl_path_effect.h +++ b/display_list/effects/dl_path_effect.h @@ -114,7 +114,7 @@ class DlDashPathEffect final : public DlPathEffect { int count_; SkScalar phase_; - friend class DisplayListBuilder; + friend class DlOpRecorder; friend class DlPathEffect; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlDashPathEffect); diff --git a/display_list/geometry/dl_rtree.h b/display_list/geometry/dl_rtree.h index f12486edcce76..31f22ee477104 100644 --- a/display_list/geometry/dl_rtree.h +++ b/display_list/geometry/dl_rtree.h @@ -25,7 +25,7 @@ namespace flutter { /// - Query for a set of non-overlapping rectangles that are joined /// from the original rectangles that intersect a query rect /// @see |searchAndConsolidateRects| -class DlRTree : public SkRefCnt { +class DlRTree { private: static constexpr int kMaxChildren = 11; diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc index 9299c9a6ff9d7..0c46912813b02 100644 --- a/display_list/skia/dl_sk_canvas.cc +++ b/display_list/skia/dl_sk_canvas.cc @@ -294,6 +294,13 @@ void DlSkCanvasAdapter::DrawAtlas(const sk_sp& atlas, ToSk(sampling), cullRect, sk_paint()); } +void DlSkCanvasAdapter::DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity) { + FML_LOG(ERROR) << "Cannot draw Impeller Picture in to a Skia canvas."; + FML_DCHECK(false); +} + void DlSkCanvasAdapter::DrawDisplayList(const sk_sp display_list, SkScalar opacity) { const int restore_count = delegate_->getSaveCount(); diff --git a/display_list/skia/dl_sk_canvas.h b/display_list/skia/dl_sk_canvas.h index 0377b7de75e30..578766b4f3492 100644 --- a/display_list/skia/dl_sk_canvas.h +++ b/display_list/skia/dl_sk_canvas.h @@ -138,6 +138,9 @@ class DlSkCanvasAdapter final : public virtual DlCanvas { DlImageSampling sampling, const SkRect* cullRect, const DlPaint* paint = nullptr) override; + void DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity = SK_Scalar1) override; void DrawDisplayList(const sk_sp display_list, SkScalar opacity = SK_Scalar1) override; void DrawTextBlob(const sk_sp& blob, diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc index def08fa8886a3..41bb175296325 100644 --- a/display_list/skia/dl_sk_dispatcher.cc +++ b/display_list/skia/dl_sk_dispatcher.cc @@ -182,14 +182,14 @@ void DlSkCanvasDispatcher::drawVertices(const DlVertices* vertices, DlBlendMode mode) { canvas_->drawVertices(ToSk(vertices), ToSk(mode), paint()); } -void DlSkCanvasDispatcher::drawImage(const sk_sp image, +void DlSkCanvasDispatcher::drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { canvas_->drawImage(image ? image->skia_image() : nullptr, point.fX, point.fY, ToSk(sampling), safe_paint(render_with_attributes)); } -void DlSkCanvasDispatcher::drawImageRect(const sk_sp image, +void DlSkCanvasDispatcher::drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -199,7 +199,7 @@ void DlSkCanvasDispatcher::drawImageRect(const sk_sp image, ToSk(sampling), safe_paint(render_with_attributes), ToSk(constraint)); } -void DlSkCanvasDispatcher::drawImageNine(const sk_sp image, +void DlSkCanvasDispatcher::drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -214,7 +214,7 @@ void DlSkCanvasDispatcher::drawImageNine(const sk_sp image, canvas_->drawImageNine(skia_image.get(), center, dst, ToSk(filter), safe_paint(render_with_attributes)); } -void DlSkCanvasDispatcher::drawAtlas(const sk_sp atlas, +void DlSkCanvasDispatcher::drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -236,7 +236,7 @@ void DlSkCanvasDispatcher::drawAtlas(const sk_sp atlas, safe_paint(render_with_attributes)); } void DlSkCanvasDispatcher::drawDisplayList( - const sk_sp display_list, + const sk_sp& display_list, SkScalar opacity) { const int restore_count = canvas_->getSaveCount(); @@ -264,7 +264,7 @@ void DlSkCanvasDispatcher::drawDisplayList( // Restore canvas state to what it was before dispatching. canvas_->restoreToCount(restore_count); } -void DlSkCanvasDispatcher::drawTextBlob(const sk_sp blob, +void DlSkCanvasDispatcher::drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) { canvas_->drawTextBlob(blob, x, y, paint()); diff --git a/display_list/skia/dl_sk_dispatcher.h b/display_list/skia/dl_sk_dispatcher.h index a7fc73bbe2a54..a5356f5075610 100644 --- a/display_list/skia/dl_sk_dispatcher.h +++ b/display_list/skia/dl_sk_dispatcher.h @@ -69,22 +69,22 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, bool useCenter) override; void drawPoints(PointMode mode, uint32_t count, const SkPoint pts[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageRect(const sk_sp image, + void drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint) override; - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -93,9 +93,9 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, DlImageSampling sampling, const SkRect* cullRect, bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/testing/dl_rendering_unittests.cc b/display_list/testing/dl_rendering_unittests.cc index 23a80c2f13d92..a5a95b6d85004 100644 --- a/display_list/testing/dl_rendering_unittests.cc +++ b/display_list/testing/dl_rendering_unittests.cc @@ -5,7 +5,7 @@ #include #include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_op_flags.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/skia/dl_sk_canvas.h" diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc index 0a53f0337afd9..bd7cb1a288e7c 100644 --- a/display_list/testing/dl_test_snippets.cc +++ b/display_list/testing/dl_test_snippets.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/display_list/testing/dl_test_snippets.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_op_receiver.h" namespace flutter { @@ -46,17 +46,17 @@ std::vector CreateAllAttributesOps() { {"SetAntiAlias", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(true); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(false); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(false); }}, }}, {"SetDither", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setDither(true); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setDither(false); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setDither(false); }}, }}, {"SetInvertColors", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(true); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(false); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(false); }}, }}, {"SetStrokeCap", { @@ -64,7 +64,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kRound); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kSquare); }}, - {0, 0, 0, 0, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kButt); }}, }}, {"SetStrokeJoin", @@ -73,7 +73,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kBevel); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kRound); }}, - {0, 0, 0, 0, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kMiter); }}, }}, {"SetStyle", @@ -84,26 +84,26 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setDrawStyle(DlDrawStyle::kStrokeAndFill); }}, - {0, 0, 0, 0, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setDrawStyle(DlDrawStyle::kFill); }}, }}, {"SetStrokeWidth", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(1.0); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(5.0); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(0.0); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(0.0); }}, }}, {"SetStrokeMiter", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(0.0); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(5.0); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(4.0); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(4.0); }}, }}, {"SetColor", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorGREEN); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLUE); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLACK); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLACK); }}, }}, {"SetBlendMode", { @@ -111,7 +111,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kSrcIn); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kDstIn); }}, - {0, 0, 0, 0, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kSrcOver); }}, }}, {"SetColorSource", @@ -127,7 +127,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setColorSource(kTestSource4.get()); }}, {0, 80 + 6 * 4, 0, 0, [](DlOpReceiver& r) { r.setColorSource(kTestSource5.get()); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorSource(nullptr); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColorSource(nullptr); }}, }}, {"SetImageFilter", { @@ -185,7 +185,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setImageFilter(&kTestCFImageFilter1); }}, {0, 24, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(&kTestCFImageFilter2); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(nullptr); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(nullptr); }}, {0, 24, 0, 0, [](DlOpReceiver& r) { r.setImageFilter( @@ -218,7 +218,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setColorFilter(DlLinearToSrgbGammaColorFilter::instance.get()); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorFilter(nullptr); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColorFilter(nullptr); }}, }}, {"SetPathEffect", { @@ -227,7 +227,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setPathEffect(kTestPathEffect1.get()); }}, {0, 32, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(kTestPathEffect2.get()); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(nullptr); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(nullptr); }}, }}, {"SetMaskFilter", { @@ -241,7 +241,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter4); }}, {0, 32, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter5); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(nullptr); }}, + {0, 8, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(nullptr); }}, }}, }; } @@ -354,7 +354,7 @@ std::vector CreateAllTransformOps() { {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(10, 10); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(10, 15); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(15, 10); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.translate(0, 0); }}, + {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(0, 0); }}, }}, {"Scale", { @@ -362,15 +362,15 @@ std::vector CreateAllTransformOps() { {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(2, 2); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(2, 3); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(3, 2); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.scale(1, 1); }}, + {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(1, 1); }}, }}, {"Rotate", { // cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix) {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(30); }}, {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(45); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.rotate(0); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.rotate(360); }}, + {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(0); }}, + {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(360); }}, }}, {"Skew", { @@ -379,14 +379,14 @@ std::vector CreateAllTransformOps() { {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.1, 0.1); }}, {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.1, 0.2); }}, {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.2, 0.1); }}, - {0, 0, 0, 0, [](DlOpReceiver& r) { r.skew(0, 0); }}, + {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0, 0); }}, }}, {"Transform2DAffine", { {1, 32, 1, 32, [](DlOpReceiver& r) { r.transform2DAffine(0, 1, 12, 1, 0, 33); }}, // r.transform(identity) is ignored - {0, 0, 0, 0, + {1, 32, 1, 32, [](DlOpReceiver& r) { r.transform2DAffine(1, 0, 0, 0, 1, 0); }}, }}, {"TransformFullPerspective", @@ -397,13 +397,13 @@ std::vector CreateAllTransformOps() { 0, 0, 0, 12); }}, // r.transform(2D affine) is reduced to 2x3 - {1, 32, 1, 32, + {1, 72, 1, 72, [](DlOpReceiver& r) { r.transformFullPerspective(2, 1, 0, 4, 1, 3, 0, 5, 0, 0, 1, 0, 0, 0, 0, 1); }}, // r.transform(identity) is ignored - {0, 0, 0, 0, + {1, 72, 1, 72, [](DlOpReceiver& r) { r.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); @@ -488,13 +488,11 @@ std::vector CreateAllClipOps() { [](DlOpReceiver& r) { r.clipPath(kTestPath1, DlCanvas::ClipOp::kDifference, false); }}, - // clipPath(rect) becomes clipRect {1, 24, 1, 24, [](DlOpReceiver& r) { r.clipPath(kTestPathRect, DlCanvas::ClipOp::kIntersect, true); }}, - // clipPath(oval) becomes clipRRect - {1, 64, 1, 64, + {1, 24, 1, 24, [](DlOpReceiver& r) { r.clipPath(kTestPathOval, DlCanvas::ClipOp::kIntersect, true); }}, diff --git a/display_list/testing/dl_test_snippets.h b/display_list/testing/dl_test_snippets.h index 536cb224e8ef9..b9f9f52b606e8 100644 --- a/display_list/testing/dl_test_snippets.h +++ b/display_list/testing/dl_test_snippets.h @@ -6,7 +6,7 @@ #define FLUTTER_DISPLAY_LIST_TESTING_DL_TEST_SNIPPETS_H_ #include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" diff --git a/display_list/utils/dl_bounds_accumulator.cc b/display_list/utils/dl_bounds_accumulator.cc index 34750ed4bd529..e609dc43baf4d 100644 --- a/display_list/utils/dl_bounds_accumulator.cc +++ b/display_list/utils/dl_bounds_accumulator.cc @@ -125,10 +125,11 @@ SkRect RTreeBoundsAccumulator::bounds() const { return accumulator.bounds(); } -sk_sp RTreeBoundsAccumulator::rtree() const { +std::shared_ptr RTreeBoundsAccumulator::rtree() const { FML_DCHECK(saved_offsets_.empty()); - return sk_make_sp(rects_.data(), rects_.size(), rect_indices_.data(), - [](int id) { return id >= 0; }); + return std::make_shared(rects_.data(), rects_.size(), + rect_indices_.data(), + [](int id) { return id >= 0; }); } } // namespace flutter diff --git a/display_list/utils/dl_bounds_accumulator.h b/display_list/utils/dl_bounds_accumulator.h index db4ceda2698b6..e741f11277c7e 100644 --- a/display_list/utils/dl_bounds_accumulator.h +++ b/display_list/utils/dl_bounds_accumulator.h @@ -7,6 +7,7 @@ #include +#include "flutter/display_list/dl_canvas_to_receiver.h" #include "flutter/display_list/geometry/dl_rtree.h" #include "flutter/fml/logging.h" @@ -21,13 +22,13 @@ namespace flutter { -enum class BoundsAccumulatorType { - kRect, - kRTree, -}; - class BoundsAccumulator { public: + enum class Type { + kRect, + kRTree, + }; + /// function definition for modifying the bounds of a rectangle /// during a restore operation. The function is used primarily /// to account for the bounds impact of an ImageFilter on a @@ -48,6 +49,8 @@ class BoundsAccumulator { virtual ~BoundsAccumulator() = default; + virtual Type type() const = 0; + virtual void accumulate(const SkRect& r, int index = 0) = 0; /// Save aside the rects/bounds currently being accumulated and start @@ -84,9 +87,7 @@ class BoundsAccumulator { virtual SkRect bounds() const = 0; - virtual sk_sp rtree() const = 0; - - virtual BoundsAccumulatorType type() const = 0; + virtual std::shared_ptr rtree() const = 0; }; class RectBoundsAccumulator final : public virtual BoundsAccumulator { @@ -108,11 +109,9 @@ class RectBoundsAccumulator final : public virtual BoundsAccumulator { return rect_.bounds(); } - BoundsAccumulatorType type() const override { - return BoundsAccumulatorType::kRect; - } + Type type() const override { return Type::kRect; } - sk_sp rtree() const override { return nullptr; } + std::shared_ptr rtree() const override { return nullptr; } private: class AccumulationRect { @@ -151,11 +150,9 @@ class RTreeBoundsAccumulator final : public virtual BoundsAccumulator { SkRect bounds() const override; - sk_sp rtree() const override; + std::shared_ptr rtree() const override; - BoundsAccumulatorType type() const override { - return BoundsAccumulatorType::kRTree; - } + Type type() const override { return Type::kRTree; } private: std::vector rects_; diff --git a/display_list/utils/dl_matrix_clip_tracker.cc b/display_list/utils/dl_matrix_clip_tracker.cc index 2dd0e8fa31a39..f9e30590303d2 100644 --- a/display_list/utils/dl_matrix_clip_tracker.cc +++ b/display_list/utils/dl_matrix_clip_tracker.cc @@ -4,7 +4,7 @@ #include "flutter/display_list/utils/dl_matrix_clip_tracker.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/fml/logging.h" namespace flutter { diff --git a/display_list/utils/dl_receiver_utils.h b/display_list/utils/dl_receiver_utils.h index e2d07310368ae..26c3db8ef774b 100644 --- a/display_list/utils/dl_receiver_utils.h +++ b/display_list/utils/dl_receiver_utils.h @@ -100,22 +100,22 @@ class IgnoreDrawDispatchHelper : public virtual DlOpReceiver { uint32_t count, const SkPoint points[]) override {} void drawVertices(const DlVertices* vertices, DlBlendMode mode) override {} - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override {} - void drawImageRect(const sk_sp image, + void drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint) override {} - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override {} - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -124,9 +124,9 @@ class IgnoreDrawDispatchHelper : public virtual DlOpReceiver { DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) override {} - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity) override {} - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override {} void drawShadow(const SkPath& path, diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 53de13474e316..59633671b0eab 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -20,6 +20,8 @@ source_set("flow") { "frame_timings.h", "layer_snapshot_store.cc", "layer_snapshot_store.h", + "layers/aiks_layer.cc", + "layers/aiks_layer.h", "layers/backdrop_filter_layer.cc", "layers/backdrop_filter_layer.h", "layers/cacheable_layer.cc", @@ -192,6 +194,11 @@ if (enable_unittests) { "//third_party/skia", ] + if (impeller_supports_rendering) { + sources += [ "layers/aiks_layer_unittests.cc" ] + deps += [ "//flutter/impeller" ] + } + if (!defined(defines)) { defines = [] } diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 086882b76adf4..90ba06b73e9f5 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -3,9 +3,45 @@ // found in the LICENSE file. #include "flutter/flow/embedded_views.h" +#include "flutter/display_list/dl_op_spy.h" namespace flutter { +#if IMPELLER_SUPPORTS_RENDERING +ImpellerEmbedderViewSlice::ImpellerEmbedderViewSlice(SkRect view_bounds) { + canvas_ = std::make_unique( + /*bounds=*/view_bounds); +} + +DlCanvas* ImpellerEmbedderViewSlice::canvas() { + return canvas_ ? canvas_.get() : nullptr; +} + +void ImpellerEmbedderViewSlice::end_recording() { + picture_ = + std::make_shared(canvas_->EndRecordingAsPicture()); + canvas_.reset(); +} + +std::list ImpellerEmbedderViewSlice::searchNonOverlappingDrawnRects( + const SkRect& query) const { + FML_DCHECK(picture_); + return picture_->rtree->searchAndConsolidateRects(query); +} + +void ImpellerEmbedderViewSlice::render_into(DlCanvas* canvas) { + canvas->DrawImpellerPicture(picture_); +} + +bool ImpellerEmbedderViewSlice::recording_ended() { + return canvas_ == nullptr; +} + +bool ImpellerEmbedderViewSlice::renders_anything() { + return !picture_->rtree->bounds().isEmpty(); +} +#endif // IMPELLER_SUPPORTS_RENDERING + DisplayListEmbedderViewSlice::DisplayListEmbedderViewSlice(SkRect view_bounds) { builder_ = std::make_unique( /*bounds=*/view_bounds, @@ -30,10 +66,6 @@ void DisplayListEmbedderViewSlice::render_into(DlCanvas* canvas) { canvas->DrawDisplayList(display_list_); } -void DisplayListEmbedderViewSlice::dispatch(DlOpReceiver& receiver) { - display_list_->Dispatch(receiver); -} - bool DisplayListEmbedderViewSlice::is_empty() { return display_list_->bounds().isEmpty(); } @@ -42,6 +74,12 @@ bool DisplayListEmbedderViewSlice::recording_ended() { return builder_ == nullptr; } +bool DisplayListEmbedderViewSlice::renders_anything() { + DlOpSpy dl_op_spy; + display_list_->Dispatch(dl_op_spy); + return dl_op_spy.did_draw() && !is_empty(); +} + void ExternalViewEmbedder::SubmitFrame( GrDirectContext* context, const std::shared_ptr& aiks_context, diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 6985c37e5b8c0..9e0f2291743e7 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -8,7 +8,7 @@ #include #include -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/skia/dl_sk_canvas.h" #include "flutter/flow/surface_frame.h" #include "flutter/fml/memory/ref_counted.h" @@ -340,11 +340,33 @@ class EmbedderViewSlice { virtual std::list searchNonOverlappingDrawnRects( const SkRect& query) const = 0; virtual void render_into(DlCanvas* canvas) = 0; + virtual bool recording_ended() = 0; + virtual bool renders_anything() = 0; }; +#if IMPELLER_SUPPORTS_RENDERING +class ImpellerEmbedderViewSlice : public EmbedderViewSlice { + public: + explicit ImpellerEmbedderViewSlice(SkRect view_bounds); + ~ImpellerEmbedderViewSlice() override = default; + + DlCanvas* canvas() override; + void end_recording() override; + std::list searchNonOverlappingDrawnRects( + const SkRect& query) const override; + void render_into(DlCanvas* canvas) override; + bool recording_ended() override; + bool renders_anything() override; + + private: + std::unique_ptr canvas_; + std::shared_ptr picture_; +}; +#endif + class DisplayListEmbedderViewSlice : public EmbedderViewSlice { public: - DisplayListEmbedderViewSlice(SkRect view_bounds); + explicit DisplayListEmbedderViewSlice(SkRect view_bounds); ~DisplayListEmbedderViewSlice() override = default; DlCanvas* canvas() override; @@ -352,9 +374,9 @@ class DisplayListEmbedderViewSlice : public EmbedderViewSlice { std::list searchNonOverlappingDrawnRects( const SkRect& query) const override; void render_into(DlCanvas* canvas) override; - void dispatch(DlOpReceiver& receiver); bool is_empty(); - bool recording_ended(); + bool recording_ended() override; + bool renders_anything() override; private: std::unique_ptr builder_; diff --git a/flow/layers/aiks_layer.cc b/flow/layers/aiks_layer.cc new file mode 100644 index 0000000000000..74d884ae21327 --- /dev/null +++ b/flow/layers/aiks_layer.cc @@ -0,0 +1,64 @@ +// 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 "flutter/flow/layers/aiks_layer.h" + +#include + +namespace flutter { + +AiksLayer::AiksLayer(const SkPoint& offset, + const std::shared_ptr& picture) + : offset_(offset), picture_(picture) { +#if IMPELLER_SUPPORTS_RENDERING + if (picture_) { + FML_DCHECK(picture_->rtree); + bounds_ = picture_->rtree->bounds(); + } +#endif +} + +bool AiksLayer::IsReplacing(DiffContext* context, const Layer* layer) const { + auto old_layer = layer->as_aiks_layer(); + return old_layer != nullptr && offset_ == old_layer->offset_ && + old_layer->picture_ == picture_; +} + +void AiksLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + context->PushTransform(SkMatrix::Translate(offset_.x(), offset_.y())); + context->AddLayerBounds(bounds_); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +void AiksLayer::Preroll(PrerollContext* frame) { + // There is no opacity peepholing to do here because Impeller handles that + // in the Entities, and this layer will never participate in raster caching. + FML_DCHECK(!frame->raster_cache); + set_paint_bounds(bounds_); +} + +void AiksLayer::Paint(PaintContext& context) const { + FML_DCHECK(needs_painting(context)); + + auto mutator = context.state_stack.save(); + mutator.translate(offset_.x(), offset_.y()); + + FML_DCHECK(!context.raster_cache); + + SkScalar opacity = context.state_stack.outstanding_opacity(); + + if (context.enable_leaf_layer_tracing) { + // TODO(dnfield): Decide if we need to capture this for Impeller. + // We can't do this the same way as on the Skia backend, because Impeller + // does not expose primitives for flushing things down to the GPU without + // also allocating a texture. + // https://github.com/flutter/flutter/issues/131941 + FML_LOG(ERROR) << "Leaf layer tracing unsupported for Impeller."; + } + + context.canvas->DrawImpellerPicture(picture_, opacity); +} + +} // namespace flutter diff --git a/flow/layers/aiks_layer.h b/flow/layers/aiks_layer.h new file mode 100644 index 0000000000000..eda42fc5e63cd --- /dev/null +++ b/flow/layers/aiks_layer.h @@ -0,0 +1,44 @@ +// 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 "flutter/flow/layers/layer.h" + +#if IMPELLER_SUPPORTS_RENDERING +#include "impeller/aiks/picture.h" // nogncheck +#else // IMPELLER_SUPPORTS_RENDERING +namespace impeller { +struct Picture; +} // namespace impeller +#endif // !IMPELLER_SUPPORTS_RENDERING + +namespace flutter { + +class AiksLayer : public Layer { + public: + AiksLayer(const SkPoint& offset, + const std::shared_ptr& picture); + + const AiksLayer* as_aiks_layer() const override { return this; } + + bool IsReplacing(DiffContext* context, const Layer* layer) const override; + + void Diff(DiffContext* context, const Layer* old_layer) override; + + void Preroll(PrerollContext* frame) override; + + void Paint(PaintContext& context) const override; + + private: + SkPoint offset_; + SkRect bounds_; + std::shared_ptr picture_; + + FML_DISALLOW_COPY_AND_ASSIGN(AiksLayer); +}; + +} // namespace flutter diff --git a/flow/layers/aiks_layer_unittests.cc b/flow/layers/aiks_layer_unittests.cc new file mode 100644 index 0000000000000..10b56073773de --- /dev/null +++ b/flow/layers/aiks_layer_unittests.cc @@ -0,0 +1,49 @@ +// 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. + +#define FML_USED_ON_EMBEDDER + +#include "flow/layers/layer.h" +#include "flutter/flow/layers/aiks_layer.h" +#include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/testing/layer_test.h" +#include "impeller/aiks/canvas.h" + +namespace flutter { +namespace testing { + +using AiksLayerTest = LayerTest; + +TEST_F(AiksLayerTest, InfiniteBounds) { + impeller::Canvas canvas; + canvas.DrawPaint(impeller::Paint{.color = impeller::Color::Red()}); + auto picture = + std::make_shared(canvas.EndRecordingAsPicture()); + AiksLayer layer(SkPoint::Make(0.0f, 0.0f), picture); + + const FixedRefreshRateStopwatch unused_stopwatch; + LayerStateStack state_stack; + state_stack.set_preroll_delegate(kGiantRect, SkMatrix::I()); + PrerollContext preroll_context{ + .state_stack = state_stack, + .raster_time = unused_stopwatch, + .ui_time = unused_stopwatch, + .texture_registry = nullptr, + }; + PaintContext context{ + // clang-format off + .state_stack = state_stack, + .raster_time = unused_stopwatch, + .ui_time = unused_stopwatch, + .texture_registry = nullptr, + // clang-format on + }; + + EXPECT_FALSE(layer.needs_painting(context)); + layer.Preroll(&preroll_context); + EXPECT_TRUE(layer.needs_painting(context)); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index 61489a6ad38c8..32f8396536512 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -68,7 +68,6 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } - reset_display_list(); layer->Paint(checkerboard_context()); { DisplayListBuilder expected_builder; @@ -89,8 +88,8 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { } expected_builder.Restore(); } - EXPECT_TRUE( - DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); + EXPECT_TRUE(DisplayListsEQ_Verbose(checkerboard_display_list(), + expected_builder.Build())); } } @@ -145,7 +144,6 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } - reset_display_list(); layer->Paint(checkerboard_context()); { DisplayListBuilder expected_builder; @@ -166,8 +164,8 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { } expected_builder.Restore(); } - EXPECT_TRUE( - DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); + EXPECT_TRUE(DisplayListsEQ_Verbose(checkerboard_display_list(), + expected_builder.Build())); } } @@ -221,7 +219,6 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } - reset_display_list(); layer->Paint(checkerboard_context()); { DisplayListBuilder expected_builder; @@ -242,8 +239,8 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { } expected_builder.Restore(); } - EXPECT_TRUE( - DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); + EXPECT_TRUE(DisplayListsEQ_Verbose(checkerboard_display_list(), + expected_builder.Build())); } } diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index 03eaf0c672239..e74daca596c94 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -6,7 +6,6 @@ #include -#include "flutter/display_list/dl_builder.h" #include "flutter/flow/layer_snapshot_store.h" #include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/offscreen_surface.h" diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 286021e028ac8..2b215b23d059c 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -6,7 +6,7 @@ #include "flutter/flow/layers/display_list_layer.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/fml/macros.h" diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index 64046f5461f92..823bad96f2fa7 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -561,9 +561,11 @@ TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { } // frame 2. + reset_display_list(); layer->Preroll(preroll_context()); layer->Paint(display_list_paint_context()); // frame 3. + reset_display_list(); layer->Preroll(preroll_context()); layer->Paint(display_list_paint_context()); diff --git a/flow/layers/layer.h b/flow/layers/layer.h index d9864e82dcb3d..05475ca3802db 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -41,6 +41,7 @@ class MockLayer; } // namespace testing class ContainerLayer; +class AiksLayer; class DisplayListLayer; class PerformanceOverlayLayer; class TextureLayer; @@ -255,6 +256,7 @@ class Layer { return RasterCacheKeyID(unique_id_, RasterCacheKeyType::kLayer); } virtual const ContainerLayer* as_container_layer() const { return nullptr; } + virtual const AiksLayer* as_aiks_layer() const { return nullptr; } virtual const DisplayListLayer* as_display_list_layer() const { return nullptr; } diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index e8aea74efd78b..68ca2bdc6091d 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -155,6 +155,69 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, } } +std::shared_ptr LayerTree::FlattenToImpellerPicture( + const SkRect& bounds, + const std::shared_ptr& texture_registry) { +#if IMPELLER_SUPPORTS_RENDERING + TRACE_EVENT0("flutter", "LayerTree::FlattenToImpellerPicture"); + + impeller::DlAiksCanvas canvas(bounds); + + const FixedRefreshRateStopwatch unused_stopwatch; + + LayerStateStack preroll_state_stack; + // No root surface transformation. So assume identity. + preroll_state_stack.set_preroll_delegate(bounds); + PrerollContext preroll_context{ + // clang-format off + .raster_cache = nullptr, + .gr_context = nullptr, + .view_embedder = nullptr, + .state_stack = preroll_state_stack, + .dst_color_space = nullptr, + .surface_needs_readback = false, + .raster_time = unused_stopwatch, + .ui_time = unused_stopwatch, + .texture_registry = texture_registry, + // clang-format on + }; + + LayerStateStack paint_state_stack; + paint_state_stack.set_delegate(&canvas); + PaintContext paint_context = { + // clang-format off + .state_stack = paint_state_stack, + .canvas = &canvas, + .gr_context = nullptr, + .dst_color_space = nullptr, + .view_embedder = nullptr, + .raster_time = unused_stopwatch, + .ui_time = unused_stopwatch, + .texture_registry = texture_registry, + .raster_cache = nullptr, + .layer_snapshot_store = nullptr, + .enable_leaf_layer_tracing = false, + // clang-format on + }; + + // Even if we don't have a root layer, we still need to create an empty + // picture. + if (root_layer_) { + root_layer_->Preroll(&preroll_context); + + // The needs painting flag may be set after the preroll. So check it after. + if (root_layer_->needs_painting(paint_context)) { + root_layer_->Paint(paint_context); + } + } + + return std::make_shared( + canvas.EndRecordingAsPicture()); +#else // IMPELLER_SUPPORTS_RENDERING + return nullptr; +#endif // !IMPELLER_SUPPORTS_RENDERING +} + sk_sp LayerTree::Flatten( const SkRect& bounds, const std::shared_ptr& texture_registry, diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 5f573da2099a0..c4f204afae117 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -54,6 +54,10 @@ class LayerTree { const std::shared_ptr& texture_registry = nullptr, GrDirectContext* gr_context = nullptr); + std::shared_ptr FlattenToImpellerPicture( + const SkRect& bounds, + const std::shared_ptr& texture_registry); + Layer* root_layer() const { return root_layer_.get(); } const SkISize& frame_size() const { return frame_size_; } diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 92f22f8123cfc..9932ac5f966b4 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -27,7 +27,7 @@ namespace flutter { RasterCacheResult::RasterCacheResult(sk_sp image, const SkRect& logical_rect, const char* type, - sk_sp rtree) + std::shared_ptr rtree) : image_(std::move(image)), logical_rect_(logical_rect), flow_(type), @@ -77,7 +77,7 @@ RasterCache::RasterCache(size_t access_threshold, /// @note Procedure doesn't copy all closures. std::unique_ptr RasterCache::Rasterize( const RasterCache::Context& context, - sk_sp rtree, + std::shared_ptr rtree, const std::function& draw_function, const std::function& draw_checkerboard) const { @@ -119,7 +119,7 @@ bool RasterCache::UpdateCacheEntry( const RasterCacheKeyID& id, const Context& raster_cache_context, const std::function& render_function, - sk_sp rtree) const { + std::shared_ptr rtree) const { RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix); Entry& entry = cache_[key]; if (!entry.image) { diff --git a/flow/raster_cache.h b/flow/raster_cache.h index a00508f84d0af..acda6269f15e6 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -29,7 +29,7 @@ class RasterCacheResult { RasterCacheResult(sk_sp image, const SkRect& logical_rect, const char* type, - sk_sp rtree = nullptr); + std::shared_ptr rtree = nullptr); virtual ~RasterCacheResult() = default; @@ -49,7 +49,7 @@ class RasterCacheResult { sk_sp image_; SkRect logical_rect_; fml::tracing::TraceFlow flow_; - sk_sp rtree_; + std::shared_ptr rtree_; }; class Layer; @@ -129,7 +129,7 @@ class RasterCache { std::unique_ptr Rasterize( const RasterCache::Context& context, - sk_sp rtree, + std::shared_ptr rtree, const std::function& draw_function, const std::function& draw_checkerboard) const; @@ -244,7 +244,7 @@ class RasterCache { bool UpdateCacheEntry(const RasterCacheKeyID& id, const Context& raster_cache_context, const std::function& render_function, - sk_sp rtree = nullptr) const; + std::shared_ptr rtree = nullptr) const; private: struct Entry { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index e0dfda6593c8c..41604b681f215 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -4,7 +4,7 @@ #include "flutter/display_list/benchmarking/dl_complexity.h" #include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/testing/dl_test_snippets.h" #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/display_list_layer.h" diff --git a/flow/surface_frame.cc b/flow/surface_frame.cc index dd1b98d33b8b2..874152571a119 100644 --- a/flow/surface_frame.cc +++ b/flow/surface_frame.cc @@ -5,6 +5,7 @@ #include "flutter/flow/surface_frame.h" #include +#include #include #include "flutter/fml/logging.h" @@ -31,9 +32,13 @@ SurfaceFrame::SurfaceFrame(sk_sp surface, canvas_ = &adapter_; } else if (display_list_fallback) { FML_DCHECK(!frame_size.isEmpty()); - dl_builder_ = - sk_make_sp(SkRect::Make(frame_size), true); - canvas_ = dl_builder_.get(); +#if IMPELLER_SUPPORTS_RENDERING + aiks_canvas_ = + std::make_shared(SkRect::Make(frame_size)); + canvas_ = aiks_canvas_.get(); +#else + FML_DCHECK(false); +#endif // IMPELLER_SUPPORTS_RENDERING } } @@ -72,9 +77,14 @@ bool SurfaceFrame::PerformSubmit() { return false; } -sk_sp SurfaceFrame::BuildDisplayList() { - TRACE_EVENT0("impeller", "SurfaceFrame::BuildDisplayList"); - return dl_builder_ ? dl_builder_->Build() : nullptr; +std::shared_ptr SurfaceFrame::GetImpellerPicture() { +#if IMPELLER_SUPPORTS_RENDERING + return std::make_shared( + aiks_canvas_->EndRecordingAsPicture()); +#else + FML_DCHECK(false); + return nullptr; +#endif // IMPELLER_SUPPORTS_RENDERING } } // namespace flutter diff --git a/flow/surface_frame.h b/flow/surface_frame.h index 1abf75e573223..c1187588465eb 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -9,11 +9,19 @@ #include #include "flutter/common/graphics/gl_context_switch.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/skia/dl_sk_canvas.h" #include "flutter/fml/macros.h" #include "flutter/fml/time/time_point.h" +#if IMPELLER_SUPPORTS_RENDERING +#include "impeller/display_list/dl_aiks_canvas.h" // nogncheck +#else // IMPELLER_SUPPORTS_RENDERINGÆ’ +namespace impeller { +class DlAiksCanvas; +} // namespace impeller +#endif // !IMPELLER_SUPPORTS_RENDERING + #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" @@ -97,14 +105,14 @@ class SurfaceFrame { } const SubmitInfo& submit_info() const { return submit_info_; } - sk_sp BuildDisplayList(); + std::shared_ptr GetImpellerPicture(); private: bool submitted_ = false; DlSkCanvasAdapter adapter_; - sk_sp dl_builder_; sk_sp surface_; + std::shared_ptr aiks_canvas_; DlCanvas* canvas_ = nullptr; FramebufferInfo framebuffer_info_; SubmitInfo submit_info_; diff --git a/flow/surface_frame_unittests.cc b/flow/surface_frame_unittests.cc index 0f6f9ba3081fc..540981bfaf8f9 100644 --- a/flow/surface_frame_unittests.cc +++ b/flow/surface_frame_unittests.cc @@ -22,6 +22,7 @@ TEST(FlowTest, SurfaceFrameDoesNotSubmitInDtor) { surface_frame.reset(); } +#if IMPELLER_SUPPORTS_RENDERING TEST(FlowTest, SurfaceFrameDoesNotHaveEmptyCanvas) { SurfaceFrame::FramebufferInfo framebuffer_info; SurfaceFrame frame( @@ -33,5 +34,6 @@ TEST(FlowTest, SurfaceFrameDoesNotHaveEmptyCanvas) { EXPECT_FALSE(frame.Canvas()->GetLocalClipBounds().isEmpty()); EXPECT_FALSE(frame.Canvas()->QuickReject(SkRect::MakeLTRB(10, 10, 50, 50))); } +#endif // IMPELLER_SUPPORTS_RENDERING } // namespace flutter diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc index 26b2a67f28d09..74ba1b6746e88 100644 --- a/flow/testing/diff_context_test.cc +++ b/flow/testing/diff_context_test.cc @@ -5,7 +5,8 @@ #include "diff_context_test.h" #include -#include "flutter/display_list/dl_builder.h" + +#include "flutter/display_list/display_list_builder.h" namespace flutter { namespace testing { diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index 178c307426c19..a85dd6f936559 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -72,11 +72,11 @@ class LayerTestBase : public CanvasTestBase { .raster_cache = nullptr, // clang-format on }, - display_list_builder_(kDlBounds), display_list_paint_context_{ // clang-format off .state_stack = display_list_state_stack_, - .canvas = &display_list_builder_, + // canvas is set below by resetting the display_list + .canvas = nullptr, .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, @@ -88,7 +88,8 @@ class LayerTestBase : public CanvasTestBase { checkerboard_context_{ // clang-format off .state_stack = checkerboard_state_stack_, - .canvas = &display_list_builder_, + // canvas is set below by resetting the display_list + .canvas = nullptr, .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, @@ -100,8 +101,8 @@ class LayerTestBase : public CanvasTestBase { use_null_raster_cache(); preroll_state_stack_.set_preroll_delegate(kGiantRect, SkMatrix::I()); paint_state_stack_.set_delegate(&TestT::mock_canvas()); - display_list_state_stack_.set_delegate(&display_list_builder_); - checkerboard_state_stack_.set_delegate(&display_list_builder_); + reset_display_list(); + reset_checkerboard_display_list(); checkerboard_state_stack_.set_checkerboard_func(draw_checkerboard); checkerboard_paint_.setColor(checkerboard_color_); } @@ -170,19 +171,44 @@ class LayerTestBase : public CanvasTestBase { PaintContext& checkerboard_context() { return checkerboard_context_; } LayerSnapshotStore& layer_snapshot_store() { return snapshot_store_; } + sk_sp checkerboard_display_list() { + if (checkerboard_display_list_ == nullptr) { + if (checkerboard_builder_.get()) { + checkerboard_state_stack_.clear_delegate(); + checkerboard_display_list_ = checkerboard_builder_->Build(); + checkerboard_builder_.reset(); + checkerboard_context_.canvas = nullptr; + } + } + return checkerboard_display_list_; + } + sk_sp display_list() { if (display_list_ == nullptr) { - display_list_ = display_list_builder_.Build(); + if (display_list_builder_.get()) { + display_list_state_stack_.clear_delegate(); + display_list_ = display_list_builder_->Build(); + display_list_builder_.reset(); + display_list_paint_context_.canvas = nullptr; + } } return display_list_; } + void reset_checkerboard_display_list() { + checkerboard_display_list_ = nullptr; + checkerboard_state_stack_.clear_delegate(); + checkerboard_builder_.reset(new DisplayListBuilder(kDlBounds)); + checkerboard_state_stack_.set_delegate(checkerboard_builder_.get()); + checkerboard_context_.canvas = checkerboard_builder_.get(); + } + void reset_display_list() { display_list_ = nullptr; - // Build() will leave the builder in a state to start recording a new DL - display_list_builder_.Build(); - // Make sure we are starting from a fresh state stack - FML_DCHECK(display_list_state_stack_.is_empty()); + display_list_state_stack_.clear_delegate(); + display_list_builder_.reset(new DisplayListBuilder(kDlBounds)); + display_list_state_stack_.set_delegate(display_list_builder_.get()); + display_list_paint_context_.canvas = display_list_builder_.get(); } void enable_leaf_layer_tracing() { @@ -220,6 +246,7 @@ class LayerTestBase : public CanvasTestBase { } LayerStateStack preroll_state_stack_; + LayerStateStack display_list_state_stack_; LayerStateStack paint_state_stack_; LayerStateStack checkerboard_state_stack_; FixedRefreshRateStopwatch raster_time_; @@ -229,10 +256,11 @@ class LayerTestBase : public CanvasTestBase { std::unique_ptr raster_cache_; PrerollContext preroll_context_; PaintContext paint_context_; - DisplayListBuilder display_list_builder_; - LayerStateStack display_list_state_stack_; + std::unique_ptr display_list_builder_; sk_sp display_list_; PaintContext display_list_paint_context_; + std::unique_ptr checkerboard_builder_; + sk_sp checkerboard_display_list_; DlPaint checkerboard_paint_; PaintContext checkerboard_context_; LayerSnapshotStore snapshot_store_; diff --git a/flow/testing/mock_texture_unittests.cc b/flow/testing/mock_texture_unittests.cc index 5f5bab254dd6e..6226446a5fd90 100644 --- a/flow/testing/mock_texture_unittests.cc +++ b/flow/testing/mock_texture_unittests.cc @@ -4,7 +4,7 @@ #include "flutter/flow/testing/mock_texture.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/testing/display_list_testing.h" #include "gtest/gtest.h" diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 5761cab4a7c1a..2f63caa31faf5 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -30,6 +30,7 @@ impeller_component("aiks") { "../base", "../entity", "../geometry", + "//flutter/display_list", ] deps = [ "//flutter/fml" ] diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 3929d8fdc3ad6..5e278b717c945 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -8,6 +8,7 @@ #include #include +#include "display_list/geometry/dl_rtree.h" #include "flutter/fml/logging.h" #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/entity/contents/atlas_contents.h" @@ -18,6 +19,7 @@ #include "impeller/entity/contents/vertices_contents.h" #include "impeller/entity/geometry/geometry.h" #include "impeller/geometry/path_builder.h" +#include "include/core/SkRect.h" namespace impeller { @@ -402,7 +404,7 @@ void Canvas::RestoreClip() { GetCurrentPass().AddEntity(entity); } -void Canvas::DrawPoints(std::vector points, +void Canvas::DrawPoints(const std::vector& points, Scalar radius, const Paint& paint, PointStyle point_style) { @@ -415,7 +417,7 @@ void Canvas::DrawPoints(std::vector points, entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters(paint.CreateContentsForGeometry( - Geometry::MakePointField(std::move(points), radius, + Geometry::MakePointField(points, radius, /*round=*/point_style == PointStyle::kRound)))); GetCurrentPass().AddEntity(entity); @@ -501,6 +503,19 @@ Picture Canvas::EndRecordingAsPicture() { Picture picture; picture.pass = std::move(base_pass_); + std::vector rects; + picture.pass->IterateAllEntities([&rects](const Entity& entity) { + auto coverage = entity.GetCoverage(); + if (coverage.has_value()) { + rects.push_back(SkRect::MakeLTRB(coverage->GetLeft(), coverage->GetTop(), + coverage->GetRight(), + coverage->GetBottom())); + } + return true; + }); + picture.rtree = + std::make_shared(rects.data(), rects.size()); + Reset(); Initialize(initial_cull_rect_); diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 84f1c486bc30f..0d31d02d34363 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -64,6 +64,8 @@ class Canvas { ~Canvas(); + std::optional BaseCullRect() const { return initial_cull_rect_; } + void Save(); void SaveLayer(const Paint& paint, @@ -108,7 +110,7 @@ class Canvas { void DrawCircle(Point center, Scalar radius, const Paint& paint); - void DrawPoints(std::vector, + void DrawPoints(const std::vector&, Scalar radius, const Paint& paint, PointStyle point_style); diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index f4e8a640efa9a..bb769000a10ae 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -8,6 +8,7 @@ #include #include +#include "display_list/geometry/dl_rtree.h" #include "flutter/fml/macros.h" #include "impeller/aiks/aiks_context.h" #include "impeller/aiks/image.h" @@ -18,6 +19,7 @@ namespace impeller { struct Picture { std::unique_ptr pass; + std::shared_ptr rtree; std::optional Snapshot(AiksContext& context); diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index 24a1980f59aac..0f7e5c42f3cf4 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -22,6 +22,8 @@ impeller_component("skia_conversions") { impeller_component("display_list") { sources = [ + "dl_aiks_canvas.cc", + "dl_aiks_canvas.h", "dl_dispatcher.cc", "dl_dispatcher.h", "dl_image_impeller.cc", diff --git a/impeller/display_list/dl_aiks_canvas.cc b/impeller/display_list/dl_aiks_canvas.cc new file mode 100644 index 0000000000000..78c972c56ee21 --- /dev/null +++ b/impeller/display_list/dl_aiks_canvas.cc @@ -0,0 +1,1204 @@ +// 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 "impeller/display_list/dl_aiks_canvas.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "display_list/dl_paint.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" +#include "impeller/aiks/color_filter.h" +#include "impeller/core/formats.h" +#include "impeller/display_list/dl_image_impeller.h" +#include "impeller/display_list/dl_vertices_geometry.h" +#include "impeller/display_list/nine_patch_converter.h" +#include "impeller/display_list/skia_conversions.h" +#include "impeller/entity/contents/conical_gradient_contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" +#include "impeller/entity/contents/linear_gradient_contents.h" +#include "impeller/entity/contents/radial_gradient_contents.h" +#include "impeller/entity/contents/runtime_effect_contents.h" +#include "impeller/entity/contents/sweep_gradient_contents.h" +#include "impeller/entity/contents/tiled_texture_contents.h" +#include "impeller/entity/entity.h" +#include "impeller/entity/geometry/geometry.h" +#include "impeller/geometry/path.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/sigma.h" +#include "impeller/typographer/backends/skia/text_frame_skia.h" + +#if IMPELLER_ENABLE_3D +#include "impeller/entity/contents/scene_contents.h" // nogncheck +#endif // IMPELLER_ENABLE_3D + +namespace impeller { + +#define UNIMPLEMENTED \ + FML_DLOG(ERROR) << "Unimplemented detail in " << __FUNCTION__; + +DlAiksCanvas::DlAiksCanvas(const SkRect& cull_rect, bool prepare_rtree) + : canvas_(skia_conversions::ToRect(cull_rect)) { + if (prepare_rtree) { + accumulator_ = std::make_unique(); + } +} + +DlAiksCanvas::DlAiksCanvas(const SkIRect& cull_rect, bool prepare_rtree) + : DlAiksCanvas(SkRect::Make(cull_rect), prepare_rtree) {} + +DlAiksCanvas::~DlAiksCanvas() = default; + +static Paint::Style ToStyle(flutter::DlDrawStyle style) { + switch (style) { + case flutter::DlDrawStyle::kFill: + return Paint::Style::kFill; + case flutter::DlDrawStyle::kStroke: + return Paint::Style::kStroke; + case flutter::DlDrawStyle::kStrokeAndFill: + UNIMPLEMENTED; + break; + } + return Paint::Style::kFill; +} + +static Cap ToStrokeCap(flutter::DlStrokeCap cap) { + switch (cap) { + case flutter::DlStrokeCap::kButt: + return Cap::kButt; + case flutter::DlStrokeCap::kRound: + return Cap::kRound; + case flutter::DlStrokeCap::kSquare: + return Cap::kSquare; + } + FML_UNREACHABLE(); +} + +static Matrix ToMatrix(const SkMatrix& m) { + return Matrix{ + // clang-format off + m[0], m[3], 0, m[6], + m[1], m[4], 0, m[7], + 0, 0, 1, 0, + m[2], m[5], 0, m[8], + // clang-format on + }; +} + +static Matrix ToMatrix(const SkM44& m) { + return Matrix{ + // clang-format off + m.rc(0, 0), m.rc(1, 0), m.rc(2, 0), m.rc(3, 0), + m.rc(0, 1), m.rc(1, 1), m.rc(2, 1), m.rc(3, 1), + m.rc(0, 2), m.rc(1, 2), m.rc(2, 2), m.rc(3, 2), + m.rc(0, 3), m.rc(1, 3), m.rc(2, 3), m.rc(3, 3), + // clang-format on + }; +} + +static Join ToStrokeJoin(flutter::DlStrokeJoin join) { + switch (join) { + case flutter::DlStrokeJoin::kMiter: + return Join::kMiter; + case flutter::DlStrokeJoin::kRound: + return Join::kRound; + case flutter::DlStrokeJoin::kBevel: + return Join::kBevel; + } + FML_UNREACHABLE(); +} + +static impeller::SamplerDescriptor ToSamplerDescriptor( + const flutter::DlImageSampling options) { + impeller::SamplerDescriptor desc; + switch (options) { + case flutter::DlImageSampling::kNearestNeighbor: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kNearest; + desc.label = "Nearest Sampler"; + break; + case flutter::DlImageSampling::kLinear: + // Impeller doesn't support cubic sampling, but linear is closer to correct + // than nearest for this case. + case flutter::DlImageSampling::kCubic: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear; + desc.label = "Linear Sampler"; + break; + case flutter::DlImageSampling::kMipmapLinear: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear; + desc.mip_filter = impeller::MipFilter::kLinear; + desc.label = "Mipmap Linear Sampler"; + break; + } + return desc; +} + +static impeller::SamplerDescriptor ToSamplerDescriptor( + const flutter::DlFilterMode options) { + impeller::SamplerDescriptor desc; + switch (options) { + case flutter::DlFilterMode::kNearest: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kNearest; + desc.label = "Nearest Sampler"; + break; + case flutter::DlFilterMode::kLinear: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear; + desc.label = "Linear Sampler"; + break; + default: + break; + } + return desc; +} + +static FilterContents::BlurStyle ToBlurStyle(flutter::DlBlurStyle blur_style) { + switch (blur_style) { + case flutter::DlBlurStyle::kNormal: + return FilterContents::BlurStyle::kNormal; + case flutter::DlBlurStyle::kSolid: + return FilterContents::BlurStyle::kSolid; + case flutter::DlBlurStyle::kOuter: + return FilterContents::BlurStyle::kOuter; + case flutter::DlBlurStyle::kInner: + return FilterContents::BlurStyle::kInner; + } +} + +static std::optional ToMaskBlurDescriptor( + const flutter::DlMaskFilter* filter) { + if (filter == nullptr) { + return std::nullopt; + } + switch (filter->type()) { + case flutter::DlMaskFilterType::kBlur: { + auto blur = filter->asBlur(); + + return Paint::MaskBlurDescriptor{ + .style = ToBlurStyle(blur->style()), + .sigma = Sigma(blur->sigma()), + }; + } + } + + return std::nullopt; +} + +static BlendMode ToBlendMode(flutter::DlBlendMode mode) { + switch (mode) { + case flutter::DlBlendMode::kClear: + return BlendMode::kClear; + case flutter::DlBlendMode::kSrc: + return BlendMode::kSource; + case flutter::DlBlendMode::kDst: + return BlendMode::kDestination; + case flutter::DlBlendMode::kSrcOver: + return BlendMode::kSourceOver; + case flutter::DlBlendMode::kDstOver: + return BlendMode::kDestinationOver; + case flutter::DlBlendMode::kSrcIn: + return BlendMode::kSourceIn; + case flutter::DlBlendMode::kDstIn: + return BlendMode::kDestinationIn; + case flutter::DlBlendMode::kSrcOut: + return BlendMode::kSourceOut; + case flutter::DlBlendMode::kDstOut: + return BlendMode::kDestinationOut; + case flutter::DlBlendMode::kSrcATop: + return BlendMode::kSourceATop; + case flutter::DlBlendMode::kDstATop: + return BlendMode::kDestinationATop; + case flutter::DlBlendMode::kXor: + return BlendMode::kXor; + case flutter::DlBlendMode::kPlus: + return BlendMode::kPlus; + case flutter::DlBlendMode::kModulate: + return BlendMode::kModulate; + case flutter::DlBlendMode::kScreen: + return BlendMode::kScreen; + case flutter::DlBlendMode::kOverlay: + return BlendMode::kOverlay; + case flutter::DlBlendMode::kDarken: + return BlendMode::kDarken; + case flutter::DlBlendMode::kLighten: + return BlendMode::kLighten; + case flutter::DlBlendMode::kColorDodge: + return BlendMode::kColorDodge; + case flutter::DlBlendMode::kColorBurn: + return BlendMode::kColorBurn; + case flutter::DlBlendMode::kHardLight: + return BlendMode::kHardLight; + case flutter::DlBlendMode::kSoftLight: + return BlendMode::kSoftLight; + case flutter::DlBlendMode::kDifference: + return BlendMode::kDifference; + case flutter::DlBlendMode::kExclusion: + return BlendMode::kExclusion; + case flutter::DlBlendMode::kMultiply: + return BlendMode::kMultiply; + case flutter::DlBlendMode::kHue: + return BlendMode::kHue; + case flutter::DlBlendMode::kSaturation: + return BlendMode::kSaturation; + case flutter::DlBlendMode::kColor: + return BlendMode::kColor; + case flutter::DlBlendMode::kLuminosity: + return BlendMode::kLuminosity; + } + FML_UNREACHABLE(); +} + +static Entity::TileMode ToTileMode(flutter::DlTileMode tile_mode) { + switch (tile_mode) { + case flutter::DlTileMode::kClamp: + return Entity::TileMode::kClamp; + case flutter::DlTileMode::kRepeat: + return Entity::TileMode::kRepeat; + case flutter::DlTileMode::kMirror: + return Entity::TileMode::kMirror; + case flutter::DlTileMode::kDecal: + return Entity::TileMode::kDecal; + } +} + +static Color ToColor(const flutter::DlColor& color) { + return { + color.getRedF(), + color.getGreenF(), + color.getBlueF(), + color.getAlphaF(), + }; +} + +static std::vector ToColors(const flutter::DlColor colors[], int count) { + auto result = std::vector(); + if (colors == nullptr) { + return result; + } + for (int i = 0; i < count; i++) { + result.push_back(skia_conversions::ToColor(colors[i])); + } + return result; +} + +// Convert display list colors + stops into impeller colors and stops, taking +// care to ensure that the stops always start with 0.0 and end with 1.0. +template +static void ConvertStops(T* gradient, + std::vector* colors, + std::vector* stops) { + FML_DCHECK(gradient->stop_count() >= 2); + + auto* dl_colors = gradient->colors(); + auto* dl_stops = gradient->stops(); + if (dl_stops[0] != 0.0) { + colors->emplace_back(skia_conversions::ToColor(dl_colors[0])); + stops->emplace_back(0); + } + for (auto i = 0; i < gradient->stop_count(); i++) { + colors->emplace_back(skia_conversions::ToColor(dl_colors[i])); + stops->emplace_back(dl_stops[i]); + } + if (stops->back() != 1.0) { + colors->emplace_back(colors->back()); + stops->emplace_back(1.0); + } +} + +static std::optional ToColorSourceType( + flutter::DlColorSourceType type) { + switch (type) { + case flutter::DlColorSourceType::kColor: + return ColorSource::Type::kColor; + case flutter::DlColorSourceType::kImage: + return ColorSource::Type::kImage; + case flutter::DlColorSourceType::kLinearGradient: + return ColorSource::Type::kLinearGradient; + case flutter::DlColorSourceType::kRadialGradient: + return ColorSource::Type::kRadialGradient; + case flutter::DlColorSourceType::kConicalGradient: + return ColorSource::Type::kConicalGradient; + case flutter::DlColorSourceType::kSweepGradient: + return ColorSource::Type::kSweepGradient; + case flutter::DlColorSourceType::kRuntimeEffect: + return ColorSource::Type::kRuntimeEffect; +#ifdef IMPELLER_ENABLE_3D + case flutter::DlColorSourceType::kScene: + return ColorSource::Type::kScene; +#endif // IMPELLER_ENABLE_3D + } +} + +ColorSource ToColorSource(const flutter::DlColorSource* source, Paint* paint) { + if (!source) { + return ColorSource::MakeColor(); + } + + std::optional type = ToColorSourceType(source->type()); + + if (!type.has_value()) { + FML_LOG(ERROR) << "Requested ColorSourceType::kUnknown"; + return ColorSource::MakeColor(); + } + + switch (type.value()) { + case ColorSource::Type::kColor: { + const flutter::DlColorColorSource* color = source->asColor(); + FML_DCHECK(color); + FML_DCHECK(paint); + paint->color = ToColor(color->color()); + return ColorSource::MakeColor(); + } + case ColorSource::Type::kLinearGradient: { + const flutter::DlLinearGradientColorSource* linear = + source->asLinearGradient(); + FML_DCHECK(linear); + auto start_point = skia_conversions::ToPoint(linear->start_point()); + auto end_point = skia_conversions::ToPoint(linear->end_point()); + std::vector colors; + std::vector stops; + ConvertStops(linear, &colors, &stops); + + auto tile_mode = ToTileMode(linear->tile_mode()); + auto matrix = ToMatrix(linear->matrix()); + + return ColorSource::MakeLinearGradient( + start_point, end_point, std::move(colors), std::move(stops), + tile_mode, matrix); + } + case ColorSource::Type::kConicalGradient: { + const flutter::DlConicalGradientColorSource* conical_gradient = + source->asConicalGradient(); + FML_DCHECK(conical_gradient); + Point center = skia_conversions::ToPoint(conical_gradient->end_center()); + SkScalar radius = conical_gradient->end_radius(); + Point focus_center = + skia_conversions::ToPoint(conical_gradient->start_center()); + SkScalar focus_radius = conical_gradient->start_radius(); + std::vector colors; + std::vector stops; + ConvertStops(conical_gradient, &colors, &stops); + + auto tile_mode = ToTileMode(conical_gradient->tile_mode()); + auto matrix = ToMatrix(conical_gradient->matrix()); + + return ColorSource::MakeConicalGradient(center, radius, std::move(colors), + std::move(stops), focus_center, + focus_radius, tile_mode, matrix); + } + case ColorSource::Type::kRadialGradient: { + const flutter::DlRadialGradientColorSource* radialGradient = + source->asRadialGradient(); + FML_DCHECK(radialGradient); + auto center = skia_conversions::ToPoint(radialGradient->center()); + auto radius = radialGradient->radius(); + std::vector colors; + std::vector stops; + ConvertStops(radialGradient, &colors, &stops); + + auto tile_mode = ToTileMode(radialGradient->tile_mode()); + auto matrix = ToMatrix(radialGradient->matrix()); + return ColorSource::MakeRadialGradient(center, radius, std::move(colors), + std::move(stops), tile_mode, + matrix); + } + case ColorSource::Type::kSweepGradient: { + const flutter::DlSweepGradientColorSource* sweepGradient = + source->asSweepGradient(); + FML_DCHECK(sweepGradient); + + auto center = skia_conversions::ToPoint(sweepGradient->center()); + auto start_angle = Degrees(sweepGradient->start()); + auto end_angle = Degrees(sweepGradient->end()); + std::vector colors; + std::vector stops; + ConvertStops(sweepGradient, &colors, &stops); + + auto tile_mode = ToTileMode(sweepGradient->tile_mode()); + auto matrix = ToMatrix(sweepGradient->matrix()); + return ColorSource::MakeSweepGradient(center, start_angle, end_angle, + std::move(colors), std::move(stops), + tile_mode, matrix); + } + case ColorSource::Type::kImage: { + const flutter::DlImageColorSource* image_color_source = source->asImage(); + FML_DCHECK(image_color_source && + image_color_source->image()->impeller_texture()); + auto texture = image_color_source->image()->impeller_texture(); + auto x_tile_mode = ToTileMode(image_color_source->horizontal_tile_mode()); + auto y_tile_mode = ToTileMode(image_color_source->vertical_tile_mode()); + auto desc = ToSamplerDescriptor(image_color_source->sampling()); + auto matrix = ToMatrix(image_color_source->matrix()); + return ColorSource::MakeImage(texture, x_tile_mode, y_tile_mode, desc, + matrix); + } + case ColorSource::Type::kRuntimeEffect: { + const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source = + source->asRuntimeEffect(); + auto runtime_stage = + runtime_effect_color_source->runtime_effect()->runtime_stage(); + auto uniform_data = runtime_effect_color_source->uniform_data(); + auto samplers = runtime_effect_color_source->samplers(); + + std::vector texture_inputs; + + for (auto& sampler : samplers) { + if (sampler == nullptr) { + return ColorSource::MakeColor(); + } + auto* image = sampler->asImage(); + if (!sampler->asImage()) { + UNIMPLEMENTED; + return ColorSource::MakeColor(); + } + FML_DCHECK(image->image()->impeller_texture()); + texture_inputs.push_back({ + .sampler_descriptor = ToSamplerDescriptor(image->sampling()), + .texture = image->image()->impeller_texture(), + }); + } + + return ColorSource::MakeRuntimeEffect(runtime_stage, uniform_data, + texture_inputs); + } + case ColorSource::Type::kScene: { +#ifdef IMPELLER_ENABLE_3D + const flutter::DlSceneColorSource* scene_color_source = source->asScene(); + std::shared_ptr scene_node = + scene_color_source->scene_node(); + Matrix camera_transform = scene_color_source->camera_matrix(); + + return ColorSource::MakeScene(scene_node, camera_transform); +#else // IMPELLER_ENABLE_3D + FML_LOG(ERROR) << "ColorSourceType::kScene can only be used if Impeller " + "Scene is enabled."; + return ColorSource::MakeColor(); +#endif // IMPELLER_ENABLE_3D + } + } +} + +static std::shared_ptr ToColorFilter( + const flutter::DlColorFilter* filter) { + if (filter == nullptr) { + return nullptr; + } + switch (filter->type()) { + case flutter::DlColorFilterType::kBlend: { + auto dl_blend = filter->asBlend(); + auto blend_mode = ToBlendMode(dl_blend->mode()); + auto color = skia_conversions::ToColor(dl_blend->color()); + return ColorFilter::MakeBlend(blend_mode, color); + } + case flutter::DlColorFilterType::kMatrix: { + const flutter::DlMatrixColorFilter* dl_matrix = filter->asMatrix(); + impeller::ColorMatrix color_matrix; + dl_matrix->get_matrix(color_matrix.array); + return ColorFilter::MakeMatrix(color_matrix); + } + case flutter::DlColorFilterType::kSrgbToLinearGamma: + return ColorFilter::MakeSrgbToLinear(); + case flutter::DlColorFilterType::kLinearToSrgbGamma: + return ColorFilter::MakeLinearToSrgb(); + } + return nullptr; +} + +static Paint::ImageFilterProc ToImageFilterProc( + const flutter::DlImageFilter* filter) { + if (filter == nullptr) { + return nullptr; + } + + switch (filter->type()) { + case flutter::DlImageFilterType::kBlur: { + auto blur = filter->asBlur(); + auto sigma_x = Sigma(blur->sigma_x()); + auto sigma_y = Sigma(blur->sigma_y()); + auto tile_mode = ToTileMode(blur->tile_mode()); + + return [sigma_x, sigma_y, tile_mode](const FilterInput::Ref& input, + const Matrix& effect_transform, + bool is_subpass) { + return FilterContents::MakeGaussianBlur( + input, sigma_x, sigma_y, FilterContents::BlurStyle::kNormal, + tile_mode, effect_transform); + }; + } + case flutter::DlImageFilterType::kDilate: { + auto dilate = filter->asDilate(); + FML_DCHECK(dilate); + if (dilate->radius_x() < 0 || dilate->radius_y() < 0) { + return nullptr; + } + auto radius_x = Radius(dilate->radius_x()); + auto radius_y = Radius(dilate->radius_y()); + return [radius_x, radius_y](FilterInput::Ref input, + const Matrix& effect_transform, + bool is_subpass) { + return FilterContents::MakeMorphology( + std::move(input), radius_x, radius_y, + FilterContents::MorphType::kDilate, effect_transform); + }; + } + case flutter::DlImageFilterType::kErode: { + auto erode = filter->asErode(); + FML_DCHECK(erode); + if (erode->radius_x() < 0 || erode->radius_y() < 0) { + return nullptr; + } + auto radius_x = Radius(erode->radius_x()); + auto radius_y = Radius(erode->radius_y()); + return [radius_x, radius_y](FilterInput::Ref input, + const Matrix& effect_transform, + bool is_subpass) { + return FilterContents::MakeMorphology( + std::move(input), radius_x, radius_y, + FilterContents::MorphType::kErode, effect_transform); + }; + } + case flutter::DlImageFilterType::kMatrix: { + auto matrix_filter = filter->asMatrix(); + FML_DCHECK(matrix_filter); + auto matrix = ToMatrix(matrix_filter->matrix()); + auto desc = ToSamplerDescriptor(matrix_filter->sampling()); + return [matrix, desc](FilterInput::Ref input, + const Matrix& effect_transform, bool is_subpass) { + return FilterContents::MakeMatrixFilter(std::move(input), matrix, desc, + effect_transform, is_subpass); + }; + } + case flutter::DlImageFilterType::kCompose: { + auto compose = filter->asCompose(); + FML_DCHECK(compose); + auto outer = compose->outer(); + auto inner = compose->inner(); + auto outer_proc = ToImageFilterProc(outer.get()); + auto inner_proc = ToImageFilterProc(inner.get()); + if (!outer_proc) { + return inner_proc; + } + if (!inner_proc) { + return outer_proc; + } + FML_DCHECK(outer_proc && inner_proc); + return [outer_filter = outer_proc, inner_filter = inner_proc]( + FilterInput::Ref input, const Matrix& effect_transform, + bool is_subpass) { + auto contents = + inner_filter(std::move(input), effect_transform, is_subpass); + contents = outer_filter(FilterInput::Make(contents), effect_transform, + is_subpass); + return contents; + }; + } + case flutter::DlImageFilterType::kColorFilter: { + auto color_filter_image_filter = filter->asColorFilter(); + FML_DCHECK(color_filter_image_filter); + auto color_filter = + ToColorFilter(color_filter_image_filter->color_filter().get()); + if (!color_filter) { + return nullptr; + } + return [color_filter](FilterInput::Ref input, + const Matrix& effect_transform, bool is_subpass) { + // When color filters are used as image filters, set the color filter's + // "absorb opacity" flag to false. For image filters, the snapshot + // opacity needs to be deferred until the result of the filter chain is + // being blended with the layer. + return color_filter->WrapWithGPUColorFilter(std::move(input), false); + }; + } + case flutter::DlImageFilterType::kLocalMatrix: { + auto local_matrix_filter = filter->asLocalMatrix(); + FML_DCHECK(local_matrix_filter); + auto internal_filter = local_matrix_filter->image_filter(); + FML_DCHECK(internal_filter); + + auto image_filter_proc = ToImageFilterProc(internal_filter.get()); + if (!image_filter_proc) { + return nullptr; + } + + auto matrix = ToMatrix(local_matrix_filter->matrix()); + + return [matrix, filter_proc = image_filter_proc]( + FilterInput::Ref input, const Matrix& effect_transform, + bool is_subpass) { + std::shared_ptr filter = + filter_proc(std::move(input), effect_transform, is_subpass); + return FilterContents::MakeLocalMatrixFilter(FilterInput::Make(filter), + matrix); + }; + } + } +} + +static Paint ToPaint(const flutter::DlPaint& dl_paint) { + Paint paint; + paint.style = ToStyle(dl_paint.getDrawStyle()); + paint.color = ToColor(dl_paint.getColor()); + paint.stroke_width = dl_paint.getStrokeWidth(); + paint.stroke_miter = dl_paint.getStrokeMiter(); + paint.stroke_cap = ToStrokeCap(dl_paint.getStrokeCap()); + paint.stroke_join = ToStrokeJoin(dl_paint.getStrokeJoin()); + paint.color_source = ToColorSource(dl_paint.getColorSourcePtr(), &paint); + paint.color_filter = ToColorFilter(dl_paint.getColorFilterPtr()); + paint.invert_colors = dl_paint.isInvertColors(); + paint.blend_mode = ToBlendMode(dl_paint.getBlendMode()); + if (dl_paint.getPathEffect()) { + UNIMPLEMENTED; + } + paint.mask_blur_descriptor = + ToMaskBlurDescriptor(dl_paint.getMaskFilterPtr()); + paint.image_filter = ToImageFilterProc(dl_paint.getImageFilterPtr()); + return paint; +} + +static Paint ToPaint(const flutter::DlPaint* dl_paint) { + if (!dl_paint) { + return Paint(); + } + return ToPaint(*dl_paint); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Save() { + canvas_.Save(); + if (accumulator_) { + accumulator_->save(); + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::SaveLayer(const SkRect* bounds, + const flutter::DlPaint* paint, + const flutter::DlImageFilter* backdrop) { + Paint impeller_paint = paint ? ToPaint(paint) : Paint(); + std::optional impeller_bounds = + bounds ? skia_conversions::ToRect(bounds) : std::nullopt; + Paint::ImageFilterProc proc = + backdrop ? ToImageFilterProc(backdrop) : nullptr; + + canvas_.SaveLayer(impeller_paint, impeller_bounds, proc); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Restore() { + canvas_.Restore(); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Translate(SkScalar tx, SkScalar ty) { + canvas_.Translate({tx, ty, 0.0}); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Scale(SkScalar sx, SkScalar sy) { + canvas_.Scale({sx, sy, 1.0}); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Rotate(SkScalar degrees) { + canvas_.Rotate(Degrees{degrees}); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Skew(SkScalar sx, SkScalar sy) { + canvas_.Skew(sx, sy); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Transform2DAffine(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + // clang-format off + TransformFullPerspective( + mxx, mxy, 0, mxt, + myx, myy, 0, myt, + 0 , 0, 1, 0, + 0 , 0, 0, 1 + ); + // clang-format on +} + +// |flutter::DlCanvas| +void DlAiksCanvas::TransformFullPerspective(SkScalar mxx, + SkScalar mxy, + SkScalar mxz, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myz, + SkScalar myt, + SkScalar mzx, + SkScalar mzy, + SkScalar mzz, + SkScalar mzt, + SkScalar mwx, + SkScalar mwy, + SkScalar mwz, + SkScalar mwt) { + // The order of arguments is row-major but Impeller matrices are + // column-major. + // clang-format off + auto xformation = Matrix{ + mxx, myx, mzx, mwx, + mxy, myy, mzy, mwy, + mxz, myz, mzz, mwz, + mxt, myt, mzt, mwt + }; + // clang-format on + canvas_.Transform(xformation); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::TransformReset() { + canvas_.ResetTransform(); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::Transform(const SkMatrix* matrix) { + if (!matrix) { + return; + } + canvas_.Transform(ToMatrix(*matrix)); +} + +void DlAiksCanvas::Transform(const SkM44* matrix44) { + if (!matrix44) { + return; + } + canvas_.Transform(ToMatrix(*matrix44)); +} + +static Entity::ClipOperation ToClipOperation( + flutter::DlCanvas::ClipOp clip_op) { + switch (clip_op) { + case flutter::DlCanvas::ClipOp::kDifference: + return Entity::ClipOperation::kDifference; + case flutter::DlCanvas::ClipOp::kIntersect: + return Entity::ClipOperation::kIntersect; + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::ClipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) { + canvas_.ClipRect(skia_conversions::ToRect(rect), ToClipOperation(clip_op)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::ClipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { + if (rrect.isSimple()) { + canvas_.ClipRRect(skia_conversions::ToRect(rrect.rect()), + rrect.getSimpleRadii().fX, ToClipOperation(clip_op)); + } else { + canvas_.ClipPath(skia_conversions::ToPath(rrect), ToClipOperation(clip_op)); + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::ClipPath(const SkPath& path, ClipOp clip_op, bool is_aa) { + canvas_.ClipPath(skia_conversions::ToPath(path), ToClipOperation(clip_op)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawColor(flutter::DlColor color, + flutter::DlBlendMode dl_mode) { + Paint paint; + paint.color = skia_conversions::ToColor(color); + paint.blend_mode = ToBlendMode(dl_mode); + canvas_.DrawPaint(paint); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawPaint(const flutter::DlPaint& paint) { + canvas_.DrawPaint(ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawLine(const SkPoint& p0, + const SkPoint& p1, + const flutter::DlPaint& paint) { + auto path = + PathBuilder{} + .AddLine(skia_conversions::ToPoint(p0), skia_conversions::ToPoint(p1)) + .SetConvexity(Convexity::kConvex) + .TakePath(); + auto aiks_paint = ToPaint(paint); + aiks_paint.style = Paint::Style::kStroke; + canvas_.DrawPath(path, aiks_paint); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawRect(const SkRect& rect, const flutter::DlPaint& paint) { + canvas_.DrawRect(skia_conversions::ToRect(rect), ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawOval(const SkRect& bounds, + const flutter::DlPaint& paint) { + if (bounds.width() == bounds.height()) { + canvas_.DrawCircle(skia_conversions::ToPoint(bounds.center()), + bounds.width() * 0.5, ToPaint(paint)); + } else { + auto path = PathBuilder{} + .AddOval(skia_conversions::ToRect(bounds)) + .SetConvexity(Convexity::kConvex) + .TakePath(); + canvas_.DrawPath(path, ToPaint(paint)); + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawCircle(const SkPoint& center, + SkScalar radius, + const flutter::DlPaint& paint) { + canvas_.DrawCircle(skia_conversions::ToPoint(center), radius, ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawRRect(const SkRRect& rrect, + const flutter::DlPaint& paint) { + if (rrect.isSimple()) { + canvas_.DrawRRect(skia_conversions::ToRect(rrect.rect()), + rrect.getSimpleRadii().fX, ToPaint(paint)); + } else { + canvas_.DrawPath(skia_conversions::ToPath(rrect), ToPaint(paint)); + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const flutter::DlPaint& paint) { + PathBuilder builder; + builder.AddPath(skia_conversions::ToPath(outer)); + builder.AddPath(skia_conversions::ToPath(inner)); + canvas_.DrawPath(builder.TakePath(FillType::kOdd), ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawPath(const SkPath& path, const flutter::DlPaint& paint) { + SkRect rect; + SkRRect rrect; + SkRect oval; + if (path.isRect(&rect)) { + canvas_.DrawRect(skia_conversions::ToRect(rect), ToPaint(paint)); + } else if (path.isRRect(&rrect) && rrect.isSimple()) { + canvas_.DrawRRect(skia_conversions::ToRect(rrect.rect()), + rrect.getSimpleRadii().fX, ToPaint(paint)); + } else if (path.isOval(&oval) && oval.width() == oval.height()) { + canvas_.DrawCircle(skia_conversions::ToPoint(oval.center()), + oval.width() * 0.5, ToPaint(paint)); + } else { + canvas_.DrawPath(skia_conversions::ToPath(path), ToPaint(paint)); + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawArc(const SkRect& oval_bounds, + SkScalar start_degrees, + SkScalar sweep_degrees, + bool use_center, + const flutter::DlPaint& paint) { + PathBuilder builder; + builder.AddArc(skia_conversions::ToRect(oval_bounds), Degrees(start_degrees), + Degrees(sweep_degrees), use_center); + canvas_.DrawPath(builder.TakePath(), ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawPoints(PointMode mode, + uint32_t count, + const SkPoint points[], + const flutter::DlPaint& paint) { + auto aiks_paint = ToPaint(paint); + switch (mode) { + case flutter::DlCanvas::PointMode::kPoints: { + // Cap::kButt is also treated as a square. + auto point_style = aiks_paint.stroke_cap == Cap::kRound + ? PointStyle::kRound + : PointStyle::kSquare; + auto radius = aiks_paint.stroke_width; + if (radius > 0) { + radius /= 2.0; + } + canvas_.DrawPoints(skia_conversions::ToPoints(points, count), radius, + aiks_paint, point_style); + } break; + case flutter::DlCanvas::PointMode::kLines: + for (uint32_t i = 1; i < count; i += 2) { + Point p0 = skia_conversions::ToPoint(points[i - 1]); + Point p1 = skia_conversions::ToPoint(points[i]); + auto path = PathBuilder{}.AddLine(p0, p1).TakePath(); + canvas_.DrawPath(path, aiks_paint); + } + break; + case flutter::DlCanvas::PointMode::kPolygon: + if (count > 1) { + Point p0 = skia_conversions::ToPoint(points[0]); + for (uint32_t i = 1; i < count; i++) { + Point p1 = skia_conversions::ToPoint(points[i]); + auto path = PathBuilder{}.AddLine(p0, p1).TakePath(); + canvas_.DrawPath(path, aiks_paint); + p0 = p1; + } + } + break; + } +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawVertices(const flutter::DlVertices* vertices, + flutter::DlBlendMode dl_mode, + const flutter::DlPaint& paint) { + canvas_.DrawVertices(DlVerticesGeometry::MakeVertices(vertices), + ToBlendMode(dl_mode), ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawImage(const sk_sp& image, + const SkPoint point, + flutter::DlImageSampling sampling, + const flutter::DlPaint* paint) { + if (!image) { + return; + } + + auto texture = image->impeller_texture(); + if (!texture) { + return; + } + + const auto size = texture->GetSize(); + const auto src = SkRect::MakeWH(size.width, size.height); + const auto dest = + SkRect::MakeXYWH(point.fX, point.fY, size.width, size.height); + + DrawImageRect(image, // image + src, // source rect + dest, // destination rect + sampling, // sampling options + paint, // paint + SrcRectConstraint::kStrict // constraint + ); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawImageRect(const sk_sp& image, + const SkRect& src, + const SkRect& dst, + flutter::DlImageSampling sampling, + const flutter::DlPaint* paint, + SrcRectConstraint constraint) { + canvas_.DrawImageRect( + std::make_shared(image->impeller_texture()), // image + skia_conversions::ToRect(src), // source rect + skia_conversions::ToRect(dst), // destination rect + ToPaint(paint), // paint + ToSamplerDescriptor(sampling) // sampling + ); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + flutter::DlFilterMode filter, + const flutter::DlPaint* paint) { + NinePatchConverter converter = {}; + auto aiks_paint = ToPaint(paint); + converter.DrawNinePatch( + std::make_shared(image->impeller_texture()), + Rect::MakeLTRB(center.fLeft, center.fTop, center.fRight, center.fBottom), + skia_conversions::ToRect(dst), ToSamplerDescriptor(filter), &canvas_, + &aiks_paint); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const flutter::DlColor colors[], + int count, + flutter::DlBlendMode mode, + flutter::DlImageSampling sampling, + const SkRect* cull_rect, + const flutter::DlPaint* paint) { + canvas_.DrawAtlas(std::make_shared(atlas->impeller_texture()), + skia_conversions::ToRSXForms(xform, count), + skia_conversions::ToRects(tex, count), + ToColors(colors, count), ToBlendMode(mode), + ToSamplerDescriptor(sampling), + skia_conversions::ToRect(cull_rect), ToPaint(paint)); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawDisplayList( + const sk_sp display_list, + SkScalar opacity) { + FML_DCHECK(false); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity) { + if (!picture) { + return; + } + FML_DCHECK(opacity == 1); + canvas_.DrawPicture(*picture); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y, + const flutter::DlPaint& paint) { + const auto maybe_text_frame = MakeTextFrameFromTextBlobSkia(blob); + if (!maybe_text_frame.has_value()) { + return; + } + auto text_frame = maybe_text_frame.value(); + if (paint.getDrawStyle() == flutter::DlDrawStyle::kStroke) { + auto path = skia_conversions::PathDataFromTextBlob(blob); + auto bounds = text_frame.GetBounds(); + canvas_.Save(); + canvas_.Translate({x + bounds.origin.x, y + bounds.origin.y, 0.0}); + canvas_.DrawPath(path, ToPaint(paint)); + canvas_.Restore(); + return; + } + + canvas_.DrawTextFrame(text_frame, // + impeller::Point{x, y}, // + ToPaint(paint) // + ); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::DrawShadow(const SkPath& path, + const flutter::DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { + Color spot_color = skia_conversions::ToColor(color); + spot_color.alpha *= 0.25; + + // Compute the spot color -- ported from SkShadowUtils::ComputeTonalColors. + { + Scalar max = + std::max(std::max(spot_color.red, spot_color.green), spot_color.blue); + Scalar min = + std::min(std::min(spot_color.red, spot_color.green), spot_color.blue); + Scalar luminance = (min + max) * 0.5; + + Scalar alpha_adjust = + (2.6f + (-2.66667f + 1.06667f * spot_color.alpha) * spot_color.alpha) * + spot_color.alpha; + Scalar color_alpha = + (3.544762f + (-4.891428f + 2.3466f * luminance) * luminance) * + luminance; + color_alpha = std::clamp(alpha_adjust * color_alpha, 0.0f, 1.0f); + + Scalar greyscale_alpha = + std::clamp(spot_color.alpha * (1 - 0.4f * luminance), 0.0f, 1.0f); + + Scalar color_scale = color_alpha * (1 - greyscale_alpha); + Scalar tonal_alpha = color_scale + greyscale_alpha; + Scalar unpremul_scale = tonal_alpha != 0 ? color_scale / tonal_alpha : 0; + spot_color = Color(unpremul_scale * spot_color.red, + unpremul_scale * spot_color.green, + unpremul_scale * spot_color.blue, tonal_alpha); + } + + Vector3 light_position(0, -1, 1); + Scalar occluder_z = dpr * elevation; + + constexpr Scalar kLightRadius = 800 / 600; // Light radius / light height + + Paint paint; + paint.style = Paint::Style::kFill; + paint.color = spot_color; + paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{ + .style = FilterContents::BlurStyle::kNormal, + .sigma = Radius{kLightRadius * occluder_z / + canvas_.GetCurrentTransformation().GetScale().y}, + }; + + canvas_.Save(); + canvas_.PreConcat( + Matrix::MakeTranslation(Vector2(0, -occluder_z * light_position.y))); + + SkRect rect; + SkRRect rrect; + SkRect oval; + if (path.isRect(&rect)) { + canvas_.DrawRect(skia_conversions::ToRect(rect), paint); + } else if (path.isRRect(&rrect) && rrect.isSimple()) { + canvas_.DrawRRect(skia_conversions::ToRect(rrect.rect()), + rrect.getSimpleRadii().fX, paint); + } else if (path.isOval(&oval) && oval.width() == oval.height()) { + canvas_.DrawCircle(skia_conversions::ToPoint(oval.center()), + oval.width() * 0.5, paint); + } else { + canvas_.DrawPath(skia_conversions::ToPath(path), paint); + } + + canvas_.Restore(); +} + +// |flutter::DlCanvas| +bool DlAiksCanvas::QuickReject(const SkRect& bounds) const { + auto maybe_cull_rect = canvas_.GetCurrentLocalCullingBounds(); + if (!maybe_cull_rect.has_value()) { + return false; + } + auto cull_rect = maybe_cull_rect.value(); + if (cull_rect.IsEmpty() || bounds.isEmpty()) { + return true; + } + auto transform = canvas_.GetCurrentTransformation(); + // There are no fast paths right now to checking whther impeller::Matrix can + // be inverted. Skip that check. + if (transform.HasPerspective()) { + return false; + } + + return !skia_conversions::ToRect(bounds).IntersectsWithRect(cull_rect); +} + +// |flutter::DlCanvas| +void DlAiksCanvas::RestoreToCount(int restore_count) { + canvas_.RestoreToCount(restore_count); +} + +// |flutter::DlCanvas| +SkISize DlAiksCanvas::GetBaseLayerSize() const { + auto size = canvas_.BaseCullRect().value_or(Rect::Giant()).size.Round(); + return SkISize::Make(size.width, size.height); +} + +// |flutter::DlCanvas| +SkImageInfo DlAiksCanvas::GetImageInfo() const { + SkISize size = GetBaseLayerSize(); + return SkImageInfo::MakeUnknown(size.width(), size.height()); +} + +Picture DlAiksCanvas::EndRecordingAsPicture() { + return canvas_.EndRecordingAsPicture(); +} + +} // namespace impeller diff --git a/impeller/display_list/dl_aiks_canvas.h b/impeller/display_list/dl_aiks_canvas.h new file mode 100644 index 0000000000000..e4878c521fa0c --- /dev/null +++ b/impeller/display_list/dl_aiks_canvas.h @@ -0,0 +1,242 @@ +// 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 "flutter/display_list/dl_canvas.h" +#include "flutter/display_list/geometry/dl_rtree.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/fml/macros.h" +#include "impeller/aiks/canvas.h" +#include "impeller/aiks/paint.h" +#include "impeller/display_list/skia_conversions.h" + +namespace impeller { + +class DlAiksCanvas final : public flutter::DlCanvas { + public: + DlAiksCanvas(); + + explicit DlAiksCanvas(const SkRect& cull_rect, bool prepare_tree = false); + + explicit DlAiksCanvas(const SkIRect& cull_rect, bool prepare_rtree = false); + + ~DlAiksCanvas(); + + Picture EndRecordingAsPicture(); + + // |DlCanvas| + SkISize GetBaseLayerSize() const override; + // |DlCanvas| + SkImageInfo GetImageInfo() const override; + + // |DlCanvas| + void Save() override; + + // |DlCanvas| + void SaveLayer(const SkRect* bounds, + const flutter::DlPaint* paint = nullptr, + const flutter::DlImageFilter* backdrop = nullptr) override; + // |DlCanvas| + void Restore() override; + // |DlCanvas| + int GetSaveCount() const override { return canvas_.GetSaveCount(); } + // |DlCanvas| + void RestoreToCount(int restore_count) override; + + // |DlCanvas| + void Translate(SkScalar tx, SkScalar ty) override; + // |DlCanvas| + void Scale(SkScalar sx, SkScalar sy) override; + // |DlCanvas| + void Rotate(SkScalar degrees) override; + // |DlCanvas| + void Skew(SkScalar sx, SkScalar sy) override; + + // clang-format off + // 2x3 2D affine subset of a 4x4 transform in row major order + // |DlCanvas| + void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) override; + // full 4x4 transform in row major order + // |DlCanvas| + void TransformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; + // clang-format on + // |DlCanvas| + void TransformReset() override; + // |DlCanvas| + void Transform(const SkMatrix* matrix) override; + // |DlCanvas| + void Transform(const SkM44* matrix44) override; + // |DlCanvas| + void SetTransform(const SkMatrix* matrix) override { + TransformReset(); + Transform(matrix); + } + // |DlCanvas| + void SetTransform(const SkM44* matrix44) override { + TransformReset(); + Transform(matrix44); + } + using flutter::DlCanvas::Transform; + + /// Returns the 4x4 full perspective transform representing all transform + /// operations executed so far in this DisplayList within the enclosing + /// save stack. + // |DlCanvas| + SkM44 GetTransformFullPerspective() const override { + return skia_conversions::ToSkM44(canvas_.GetCurrentTransformation()); + } + /// Returns the 3x3 partial perspective transform representing all transform + /// operations executed so far in this DisplayList within the enclosing + /// save stack. + // |DlCanvas| + SkMatrix GetTransform() const override { + return skia_conversions::ToSkMatrix(canvas_.GetCurrentTransformation()); + } + + // |DlCanvas| + void ClipRect(const SkRect& rect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| + void ClipRRect(const SkRRect& rrect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| + void ClipPath(const SkPath& path, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + + /// Conservative estimate of the bounds of all outstanding clip operations + /// measured in the coordinate space within which this DisplayList will + /// be rendered. + // |DlCanvas| + SkRect GetDestinationClipBounds() const override { + auto rect = canvas_.GetCurrentLocalCullingBounds().value_or(Rect::Giant()); + return SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), rect.GetRight(), + rect.GetBottom()); + } + /// Conservative estimate of the bounds of all outstanding clip operations + /// transformed into the local coordinate space in which currently + /// recorded rendering operations are interpreted. + // |DlCanvas| + SkRect GetLocalClipBounds() const override { + auto rect = canvas_.GetCurrentLocalCullingBounds().value_or(Rect::Giant()); + return SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), rect.GetRight(), + rect.GetBottom()); + } + + /// Return true iff the supplied bounds are easily shown to be outside + /// of the current clip bounds. This method may conservatively return + /// false if it cannot make the determination. + // |DlCanvas| + bool QuickReject(const SkRect& bounds) const override; + + // |DlCanvas| + void DrawPaint(const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawColor(flutter::DlColor color, flutter::DlBlendMode mode) override; + // |DlCanvas| + void DrawLine(const SkPoint& p0, + const SkPoint& p1, + const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawRect(const SkRect& rect, const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawOval(const SkRect& bounds, const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawCircle(const SkPoint& center, + SkScalar radius, + const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawRRect(const SkRRect& rrect, const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawPath(const SkPath& path, const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter, + const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[], + const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawVertices(const flutter::DlVertices* vertices, + flutter::DlBlendMode mode, + const flutter::DlPaint& paint) override; + using flutter::DlCanvas::DrawVertices; + // |DlCanvas| + void DrawImage(const sk_sp& image, + const SkPoint point, + flutter::DlImageSampling sampling, + const flutter::DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawImageRect( + const sk_sp& image, + const SkRect& src, + const SkRect& dst, + flutter::DlImageSampling sampling, + const flutter::DlPaint* paint = nullptr, + SrcRectConstraint constraint = SrcRectConstraint::kFast) override; + using flutter::DlCanvas::DrawImageRect; + // |DlCanvas| + void DrawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + flutter::DlFilterMode filter, + const flutter::DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const flutter::DlColor colors[], + int count, + flutter::DlBlendMode mode, + flutter::DlImageSampling sampling, + const SkRect* cullRect, + const flutter::DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawDisplayList(const sk_sp display_list, + SkScalar opacity = SK_Scalar1) override; + + // |DlCanvas| + void DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity = SK_Scalar1) override; + + // |DlCanvas| + void DrawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y, + const flutter::DlPaint& paint) override; + // |DlCanvas| + void DrawShadow(const SkPath& path, + const flutter::DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override; + + // |DlCanvas| + void Flush() override {} + + private: + Canvas canvas_; + std::unique_ptr accumulator_; + + FML_DISALLOW_COPY_AND_ASSIGN(DlAiksCanvas); +}; + +} // namespace impeller diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 44d93adb4be13..545d26c69dc7d 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -972,7 +972,7 @@ void DlDispatcher::drawVertices(const flutter::DlVertices* vertices, } // |flutter::DlOpReceiver| -void DlDispatcher::drawImage(const sk_sp image, +void DlDispatcher::drawImage(const sk_sp& image, const SkPoint point, flutter::DlImageSampling sampling, bool render_with_attributes) { @@ -1001,7 +1001,7 @@ void DlDispatcher::drawImage(const sk_sp image, // |flutter::DlOpReceiver| void DlDispatcher::drawImageRect( - const sk_sp image, + const sk_sp& image, const SkRect& src, const SkRect& dst, flutter::DlImageSampling sampling, @@ -1017,7 +1017,7 @@ void DlDispatcher::drawImageRect( } // |flutter::DlOpReceiver| -void DlDispatcher::drawImageNine(const sk_sp image, +void DlDispatcher::drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, flutter::DlFilterMode filter, @@ -1031,7 +1031,7 @@ void DlDispatcher::drawImageNine(const sk_sp image, } // |flutter::DlOpReceiver| -void DlDispatcher::drawAtlas(const sk_sp atlas, +void DlDispatcher::drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const flutter::DlColor colors[], @@ -1050,7 +1050,7 @@ void DlDispatcher::drawAtlas(const sk_sp atlas, // |flutter::DlOpReceiver| void DlDispatcher::drawDisplayList( - const sk_sp display_list, + const sk_sp& display_list, SkScalar opacity) { // Save all values that must remain untouched after the operation. Paint saved_paint = paint_; @@ -1108,7 +1108,7 @@ void DlDispatcher::drawDisplayList( } // |flutter::DlOpReceiver| -void DlDispatcher::drawTextBlob(const sk_sp blob, +void DlDispatcher::drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) { const auto maybe_text_frame = MakeTextFrameFromTextBlobSkia(blob); diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index d9a36e2239980..150ad8c36b524 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -172,13 +172,13 @@ class DlDispatcher final : public flutter::DlOpReceiver { flutter::DlBlendMode dl_mode) override; // |flutter::DlOpReceiver| - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, flutter::DlImageSampling sampling, bool render_with_attributes) override; // |flutter::DlOpReceiver| - void drawImageRect(const sk_sp image, + void drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, flutter::DlImageSampling sampling, @@ -186,14 +186,14 @@ class DlDispatcher final : public flutter::DlOpReceiver { SrcRectConstraint constraint) override; // |flutter::DlOpReceiver| - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, flutter::DlFilterMode filter, bool render_with_attributes) override; // |flutter::DlOpReceiver| - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const flutter::DlColor colors[], @@ -204,11 +204,11 @@ class DlDispatcher final : public flutter::DlOpReceiver { bool render_with_attributes) override; // |flutter::DlOpReceiver| - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity) override; // |flutter::DlOpReceiver| - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override; diff --git a/impeller/display_list/dl_playground.h b/impeller/display_list/dl_playground.h index f6ad193eb0241..b7c1730ced436 100644 --- a/impeller/display_list/dl_playground.h +++ b/impeller/display_list/dl_playground.h @@ -5,7 +5,7 @@ #pragma once #include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/fml/macros.h" #include "impeller/playground/playground_test.h" #include "third_party/skia/include/core/SkFont.h" diff --git a/impeller/display_list/dl_unittests.cc b/impeller/display_list/dl_unittests.cc index f79148465c4e7..be073917a483d 100644 --- a/impeller/display_list/dl_unittests.cc +++ b/impeller/display_list/dl_unittests.cc @@ -7,8 +7,8 @@ #include #include +#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_blend_mode.h" -#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_color.h" #include "flutter/display_list/dl_paint.h" #include "flutter/display_list/dl_tile_mode.h" diff --git a/impeller/display_list/skia_conversions.h b/impeller/display_list/skia_conversions.h index c56cf298a932f..286edaa569fb8 100644 --- a/impeller/display_list/skia_conversions.h +++ b/impeller/display_list/skia_conversions.h @@ -7,12 +7,15 @@ #include "display_list/dl_color.h" #include "impeller/core/formats.h" #include "impeller/geometry/color.h" +#include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkM44.h" +#include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint.h" #include "third_party/skia/include/core/SkRRect.h" @@ -46,5 +49,29 @@ Path PathDataFromTextBlob(const sk_sp& blob); std::optional ToPixelFormat(SkColorType type); +constexpr SkM44 ToSkM44(const Matrix& matrix) { + // SkM44 construction is in row major order (even though it stores the data in + // column major). + // clang-format off + return SkM44( + matrix.vec[0].x, matrix.vec[1].x, matrix.vec[2].x, matrix.vec[3].x, + matrix.vec[0].y, matrix.vec[1].y, matrix.vec[2].y, matrix.vec[3].y, + matrix.vec[0].z, matrix.vec[1].z, matrix.vec[2].z, matrix.vec[3].z, + matrix.vec[0].w, matrix.vec[1].w, matrix.vec[2].w, matrix.vec[3].w + ); + // clang-format on +} + +constexpr SkMatrix ToSkMatrix(const Matrix& matrix) { + // SkMatrix is in row major order. + // clang-format off + return SkMatrix::MakeAll( + matrix.vec[0].x, matrix.vec[1].x, matrix.vec[3].x, + matrix.vec[0].y, matrix.vec[1].y, matrix.vec[3].y, + matrix.vec[0].w, matrix.vec[1].w, matrix.vec[3].w + ); + /// clang-format on +} + } // namespace skia_conversions } // namespace impeller diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index f770165215f40..e926cd997236b 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -127,6 +127,9 @@ std::optional EntityPass::GetElementsCoverage( if (!coverage.has_value()) { continue; } + if (coverage->IsMaximum()) { + return coverage; + } result = result->Union(coverage.value()); } return result; diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 31db5d2324ce5..423e0e6498509 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -31,7 +31,6 @@ class EntityPass { /// When the element is a child `EntityPass`, it may be rendered to an /// offscreen texture and converted into an `Entity` that draws the texture /// into the current pass, or its children may be collapsed into the current - /// /// `EntityPass`. Elements are converted to Entities in /// `GetEntityForElement()`. using Element = std::variant>; diff --git a/impeller/entity/geometry/geometry.cc b/impeller/entity/geometry/geometry.cc index 36267c51c2331..85f2d80cf2b5a 100644 --- a/impeller/entity/geometry/geometry.cc +++ b/impeller/entity/geometry/geometry.cc @@ -116,10 +116,11 @@ std::unique_ptr Geometry::MakeFillPath( return std::make_unique(path, inner_rect); } -std::unique_ptr Geometry::MakePointField(std::vector points, - Scalar radius, - bool round) { - return std::make_unique(std::move(points), radius, round); +std::unique_ptr Geometry::MakePointField( + const std::vector& points, + Scalar radius, + bool round) { + return std::make_unique(points, radius, round); } std::unique_ptr Geometry::MakeStrokePath(const Path& path, diff --git a/impeller/entity/geometry/geometry.h b/impeller/entity/geometry/geometry.h index 6a8861d120e6e..43b91aa5054d7 100644 --- a/impeller/entity/geometry/geometry.h +++ b/impeller/entity/geometry/geometry.h @@ -72,9 +72,8 @@ class Geometry { static std::unique_ptr MakeRect(Rect rect); - static std::unique_ptr MakePointField(std::vector points, - Scalar radius, - bool round); + static std::unique_ptr + MakePointField(const std::vector& points, Scalar radius, bool round); virtual GeometryResult GetPositionBuffer(const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/geometry/point_field_geometry.cc b/impeller/entity/geometry/point_field_geometry.cc index 81fe3885d916b..50c43e5858bcf 100644 --- a/impeller/entity/geometry/point_field_geometry.cc +++ b/impeller/entity/geometry/point_field_geometry.cc @@ -9,10 +9,10 @@ namespace impeller { -PointFieldGeometry::PointFieldGeometry(std::vector points, +PointFieldGeometry::PointFieldGeometry(const std::vector& points, Scalar radius, bool round) - : points_(std::move(points)), radius_(radius), round_(round) {} + : points_(points), radius_(radius), round_(round) {} PointFieldGeometry::~PointFieldGeometry() = default; diff --git a/impeller/entity/geometry/point_field_geometry.h b/impeller/entity/geometry/point_field_geometry.h index deb0ce8dd342d..bbd69d8fe99f1 100644 --- a/impeller/entity/geometry/point_field_geometry.h +++ b/impeller/entity/geometry/point_field_geometry.h @@ -10,7 +10,9 @@ namespace impeller { class PointFieldGeometry : public Geometry { public: - PointFieldGeometry(std::vector points, Scalar radius, bool round); + PointFieldGeometry(const std::vector& points, + Scalar radius, + bool round); ~PointFieldGeometry(); diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 0bda6140d95b9..03c7874774b3c 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -72,6 +72,11 @@ struct TRect { return TRect::MakeLTRB(left, top, right, bottom); } + constexpr static TRect Giant() { + // See flutter::kGiantRect. + return TRect::MakeLTRB(-1E9, -1E9, 1E9, 1E9); + } + constexpr static TRect MakeMaximum() { return TRect::MakeLTRB(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), diff --git a/impeller/geometry/sigma.h b/impeller/geometry/sigma.h index c909128be4baa..c66b570e77eb9 100644 --- a/impeller/geometry/sigma.h +++ b/impeller/geometry/sigma.h @@ -20,7 +20,7 @@ namespace impeller { /// quality blurs (with exponentially diminishing returns for the same sigma /// input). Making this value any lower results in a noticable loss of /// quality in the blur. -constexpr static float kKernelRadiusPerSigma = 1.73205080757; +constexpr static float kKernelRadiusPerSigma = 1.73205080757f; struct Radius; @@ -29,7 +29,7 @@ struct Radius; /// the filter input. In other words, this determines how wide the /// distribution stretches. struct Sigma { - Scalar sigma = 0.0; + Scalar sigma = 0.0f; constexpr Sigma() = default; @@ -45,7 +45,7 @@ struct Sigma { /// relationship with `Sigma`. See `kKernelRadiusPerSigma` for /// details on how this relationship works. struct Radius { - Scalar radius = 0.0; + Scalar radius = 0.0f; constexpr Radius() = default; diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 5e44f3fcc27bf..d0647e3e6bfc6 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -88,13 +88,13 @@ static sk_sp CreateDeferredImage( bool impeller, std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner, + const fml::RefPtr& raster_task_runner, fml::RefPtr unref_queue) { #if IMPELLER_SUPPORTS_RENDERING if (impeller) { return DlDeferredImageGPUImpeller::Make(std::move(layer_tree), std::move(snapshot_delegate), - std::move(raster_task_runner)); + raster_task_runner); } #endif // IMPELLER_SUPPORTS_RENDERING @@ -121,8 +121,7 @@ void Scene::RasterizeToImage(uint32_t width, auto image = CanvasImage::Create(); auto dl_image = CreateDeferredImage( dart_state->IsImpellerEnabled(), BuildLayerTree(width, height), - std::move(snapshot_delegate), std::move(raster_task_runner), - std::move(unref_queue)); + std::move(snapshot_delegate), raster_task_runner, std::move(unref_queue)); image->set_image(dl_image); image->AssociateWithDartWrapper(raw_image_handle); } diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index ec0d59cb0d4e0..e47a42944c73d 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/compositing/scene_builder.h" +#include "flutter/flow/layers/aiks_layer.h" #include "flutter/flow/layers/backdrop_filter_layer.h" #include "flutter/flow/layers/clip_path_layer.h" #include "flutter/flow/layers/clip_rect_layer.h" @@ -226,6 +227,15 @@ void SceneBuilder::addPicture(double dx, SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)), picture->display_list(), !!(hints & 1), !!(hints & 2)); AddLayer(std::move(layer)); + return; + } + + auto impeller_picture = picture->impeller_picture(); + if (impeller_picture) { + auto layer = std::make_unique( + SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)), + std::move(impeller_picture)); + AddLayer(std::move(layer)); } } diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 0b090b68c9a7b..a0000967fef18 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -6,7 +6,7 @@ #include -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/dl_canvas.h" #include "flutter/lib/ui/floating_point.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/image_filter.h" @@ -38,18 +38,18 @@ void Canvas::Create(Dart_Handle wrapper, fml::MakeRefCounted(recorder->BeginRecording( SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right), SafeNarrow(bottom)))); + FML_DCHECK(canvas->dl_canvas_); recorder->set_canvas(canvas); canvas->AssociateWithDartWrapper(wrapper); } -Canvas::Canvas(sk_sp builder) - : display_list_builder_(std::move(builder)) {} +Canvas::Canvas(DlCanvas* canvas) : dl_canvas_(canvas) {} Canvas::~Canvas() {} void Canvas::save() { - if (display_list_builder_) { - builder()->Save(); + if (dl_canvas_) { + dl_canvas_->Save(); } } @@ -58,12 +58,12 @@ void Canvas::saveLayerWithoutBounds(Dart_Handle paint_objects, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; const DlPaint* save_paint = paint.paint(dl_paint, kSaveLayerWithPaintFlags); FML_DCHECK(save_paint); TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); - builder()->SaveLayer(nullptr, save_paint); + dl_canvas_->SaveLayer(nullptr, save_paint); } } @@ -78,65 +78,65 @@ void Canvas::saveLayer(double left, FML_DCHECK(paint.isNotNull()); SkRect bounds = SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right), SafeNarrow(bottom)); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; const DlPaint* save_paint = paint.paint(dl_paint, kSaveLayerWithPaintFlags); FML_DCHECK(save_paint); TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); - builder()->SaveLayer(&bounds, save_paint); + dl_canvas_->SaveLayer(&bounds, save_paint); } } void Canvas::restore() { - if (display_list_builder_) { - builder()->Restore(); + if (dl_canvas_) { + dl_canvas_->Restore(); } } int Canvas::getSaveCount() { - if (display_list_builder_) { - return builder()->GetSaveCount(); + if (dl_canvas_) { + return dl_canvas_->GetSaveCount(); } else { return 0; } } void Canvas::restoreToCount(int count) { - if (display_list_builder_ && count < getSaveCount()) { - builder()->RestoreToCount(count); + if (dl_canvas_ && count < getSaveCount()) { + dl_canvas_->RestoreToCount(count); } } void Canvas::translate(double dx, double dy) { - if (display_list_builder_) { - builder()->Translate(SafeNarrow(dx), SafeNarrow(dy)); + if (dl_canvas_) { + dl_canvas_->Translate(SafeNarrow(dx), SafeNarrow(dy)); } } void Canvas::scale(double sx, double sy) { - if (display_list_builder_) { - builder()->Scale(SafeNarrow(sx), SafeNarrow(sy)); + if (dl_canvas_) { + dl_canvas_->Scale(SafeNarrow(sx), SafeNarrow(sy)); } } void Canvas::rotate(double radians) { - if (display_list_builder_) { - builder()->Rotate(SafeNarrow(radians) * 180.0f / static_cast(M_PI)); + if (dl_canvas_) { + dl_canvas_->Rotate(SafeNarrow(radians) * 180.0f / static_cast(M_PI)); } } void Canvas::skew(double sx, double sy) { - if (display_list_builder_) { - builder()->Skew(SafeNarrow(sx), SafeNarrow(sy)); + if (dl_canvas_) { + dl_canvas_->Skew(SafeNarrow(sx), SafeNarrow(sy)); } } void Canvas::transform(const tonic::Float64List& matrix4) { // The Float array stored by Dart Matrix4 is in column-major order // Both DisplayList and SkM44 constructor take row-major matrix order - if (display_list_builder_) { + if (dl_canvas_) { // clang-format off - builder()->TransformFullPerspective( + dl_canvas_->TransformFullPerspective( SafeNarrow(matrix4[ 0]), SafeNarrow(matrix4[ 4]), SafeNarrow(matrix4[ 8]), SafeNarrow(matrix4[12]), SafeNarrow(matrix4[ 1]), SafeNarrow(matrix4[ 5]), SafeNarrow(matrix4[ 9]), SafeNarrow(matrix4[13]), SafeNarrow(matrix4[ 2]), SafeNarrow(matrix4[ 6]), SafeNarrow(matrix4[10]), SafeNarrow(matrix4[14]), @@ -146,8 +146,8 @@ void Canvas::transform(const tonic::Float64List& matrix4) { } void Canvas::getTransform(Dart_Handle matrix4_handle) { - if (display_list_builder_) { - SkM44 sk_m44 = builder()->GetTransformFullPerspective(); + if (dl_canvas_) { + SkM44 sk_m44 = dl_canvas_->GetTransformFullPerspective(); SkScalar m44_values[16]; // The Float array stored by Dart Matrix4 is in column-major order sk_m44.getColMajor(m44_values); @@ -164,17 +164,18 @@ void Canvas::clipRect(double left, double bottom, DlCanvas::ClipOp clipOp, bool doAntiAlias) { - if (display_list_builder_) { - builder()->ClipRect(SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), - SafeNarrow(right), SafeNarrow(bottom)), - clipOp, doAntiAlias); + if (dl_canvas_) { + dl_canvas_->ClipRect( + SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right), + SafeNarrow(bottom)), + clipOp, doAntiAlias); } } void Canvas::clipRRect(const RRect& rrect, bool doAntiAlias) { - if (display_list_builder_) { - builder()->ClipRRect(rrect.sk_rrect, DlCanvas::ClipOp::kIntersect, - doAntiAlias); + if (dl_canvas_) { + dl_canvas_->ClipRRect(rrect.sk_rrect, DlCanvas::ClipOp::kIntersect, + doAntiAlias); } } @@ -184,16 +185,16 @@ void Canvas::clipPath(const CanvasPath* path, bool doAntiAlias) { ToDart("Canvas.clipPath called with non-genuine Path.")); return; } - if (display_list_builder_) { - builder()->ClipPath(path->path(), DlCanvas::ClipOp::kIntersect, - doAntiAlias); + if (dl_canvas_) { + dl_canvas_->ClipPath(path->path(), DlCanvas::ClipOp::kIntersect, + doAntiAlias); } } void Canvas::getDestinationClipBounds(Dart_Handle rect_handle) { - if (display_list_builder_) { + if (dl_canvas_) { auto rect = tonic::Float64List(rect_handle); - SkRect bounds = builder()->GetDestinationClipBounds(); + SkRect bounds = dl_canvas_->GetDestinationClipBounds(); rect[0] = bounds.fLeft; rect[1] = bounds.fTop; rect[2] = bounds.fRight; @@ -202,9 +203,9 @@ void Canvas::getDestinationClipBounds(Dart_Handle rect_handle) { } void Canvas::getLocalClipBounds(Dart_Handle rect_handle) { - if (display_list_builder_) { + if (dl_canvas_) { auto rect = tonic::Float64List(rect_handle); - SkRect bounds = builder()->GetLocalClipBounds(); + SkRect bounds = dl_canvas_->GetLocalClipBounds(); rect[0] = bounds.fLeft; rect[1] = bounds.fTop; rect[2] = bounds.fRight; @@ -213,8 +214,8 @@ void Canvas::getLocalClipBounds(Dart_Handle rect_handle) { } void Canvas::drawColor(SkColor color, DlBlendMode blend_mode) { - if (display_list_builder_) { - builder()->DrawColor(color, blend_mode); + if (dl_canvas_) { + dl_canvas_->DrawColor(color, blend_mode); } } @@ -227,12 +228,12 @@ void Canvas::drawLine(double x1, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawLineFlags); - builder()->DrawLine(SkPoint::Make(SafeNarrow(x1), SafeNarrow(y1)), - SkPoint::Make(SafeNarrow(x2), SafeNarrow(y2)), - dl_paint); + dl_canvas_->DrawLine(SkPoint::Make(SafeNarrow(x1), SafeNarrow(y1)), + SkPoint::Make(SafeNarrow(x2), SafeNarrow(y2)), + dl_paint); } } @@ -240,7 +241,7 @@ void Canvas::drawPaint(Dart_Handle paint_objects, Dart_Handle paint_data) { Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawPaintFlags); std::shared_ptr filter = dl_paint.getImageFilter(); @@ -249,7 +250,7 @@ void Canvas::drawPaint(Dart_Handle paint_objects, Dart_Handle paint_data) { // present that cannot be replaced by an SkColorFilter. TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)"); } - builder()->DrawPaint(dl_paint); + dl_canvas_->DrawPaint(dl_paint); } } @@ -262,12 +263,13 @@ void Canvas::drawRect(double left, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawRectFlags); - builder()->DrawRect(SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), - SafeNarrow(right), SafeNarrow(bottom)), - dl_paint); + dl_canvas_->DrawRect( + SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right), + SafeNarrow(bottom)), + dl_paint); } } @@ -277,10 +279,10 @@ void Canvas::drawRRect(const RRect& rrect, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawRRectFlags); - builder()->DrawRRect(rrect.sk_rrect, dl_paint); + dl_canvas_->DrawRRect(rrect.sk_rrect, dl_paint); } } @@ -291,10 +293,10 @@ void Canvas::drawDRRect(const RRect& outer, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawDRRectFlags); - builder()->DrawDRRect(outer.sk_rrect, inner.sk_rrect, dl_paint); + dl_canvas_->DrawDRRect(outer.sk_rrect, inner.sk_rrect, dl_paint); } } @@ -307,12 +309,13 @@ void Canvas::drawOval(double left, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawOvalFlags); - builder()->DrawOval(SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), - SafeNarrow(right), SafeNarrow(bottom)), - dl_paint); + dl_canvas_->DrawOval( + SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right), + SafeNarrow(bottom)), + dl_paint); } } @@ -324,11 +327,11 @@ void Canvas::drawCircle(double x, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawCircleFlags); - builder()->DrawCircle(SkPoint::Make(SafeNarrow(x), SafeNarrow(y)), - SafeNarrow(radius), dl_paint); + dl_canvas_->DrawCircle(SkPoint::Make(SafeNarrow(x), SafeNarrow(y)), + SafeNarrow(radius), dl_paint); } } @@ -344,12 +347,12 @@ void Canvas::drawArc(double left, Paint paint(paint_objects, paint_data); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, useCenter // ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags); - builder()->DrawArc( + dl_canvas_->DrawArc( SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right), SafeNarrow(bottom)), SafeNarrow(startAngle) * 180.0f / static_cast(M_PI), @@ -369,10 +372,10 @@ void Canvas::drawPath(const CanvasPath* path, ToDart("Canvas.drawPath called with non-genuine Path.")); return; } - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawPathFlags); - builder()->DrawPath(path->path(), dl_paint); + dl_canvas_->DrawPath(path->path(), dl_paint); } } @@ -399,11 +402,11 @@ Dart_Handle Canvas::drawImage(const CanvasImage* image, } auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; const DlPaint* opt_paint = paint.paint(dl_paint, kDrawImageWithPaintFlags); - builder()->DrawImage(dl_image, SkPoint::Make(SafeNarrow(x), SafeNarrow(y)), - sampling, opt_paint); + dl_canvas_->DrawImage(dl_image, SkPoint::Make(SafeNarrow(x), SafeNarrow(y)), + sampling, opt_paint); } return Dart_Null(); } @@ -441,12 +444,12 @@ Dart_Handle Canvas::drawImageRect(const CanvasImage* image, SkRect dst = SkRect::MakeLTRB(SafeNarrow(dst_left), SafeNarrow(dst_top), SafeNarrow(dst_right), SafeNarrow(dst_bottom)); auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; const DlPaint* opt_paint = paint.paint(dl_paint, kDrawImageRectWithPaintFlags); - builder()->DrawImageRect(dl_image, src, dst, sampling, opt_paint, - DlCanvas::SrcRectConstraint::kFast); + dl_canvas_->DrawImageRect(dl_image, src, dst, sampling, opt_paint, + DlCanvas::SrcRectConstraint::kFast); } return Dart_Null(); } @@ -486,11 +489,11 @@ Dart_Handle Canvas::drawImageNine(const CanvasImage* image, SkRect dst = SkRect::MakeLTRB(SafeNarrow(dst_left), SafeNarrow(dst_top), SafeNarrow(dst_right), SafeNarrow(dst_bottom)); auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; const DlPaint* opt_paint = paint.paint(dl_paint, kDrawImageNineWithPaintFlags); - builder()->DrawImageNine(dl_image, icenter, dst, filter, opt_paint); + dl_canvas_->DrawImageNine(dl_image, icenter, dst, filter, opt_paint); } return Dart_Null(); } @@ -501,12 +504,20 @@ void Canvas::drawPicture(Picture* picture) { ToDart("Canvas.drawPicture called with non-genuine Picture.")); return; } + + if (!dl_canvas_) { + return; + } + if (picture->display_list()) { - if (display_list_builder_) { - builder()->DrawDisplayList(picture->display_list()); - } + dl_canvas_->DrawDisplayList(picture->display_list()); } else { - FML_DCHECK(false); + auto impeller_picture = picture->impeller_picture(); + if (impeller_picture) { + dl_canvas_->DrawImpellerPicture(impeller_picture); + } else { + FML_DCHECK(false); + } } } @@ -520,7 +531,7 @@ void Canvas::drawPoints(Dart_Handle paint_objects, "SkPoint doesn't use floats."); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; switch (point_mode) { case DlCanvas::PointMode::kPoints: @@ -533,10 +544,10 @@ void Canvas::drawPoints(Dart_Handle paint_objects, paint.paint(dl_paint, kDrawPointsAsPolygonFlags); break; } - builder()->DrawPoints(point_mode, - points.num_elements() / 2, // SkPoints have 2 floats - reinterpret_cast(points.data()), - dl_paint); + dl_canvas_->DrawPoints(point_mode, + points.num_elements() / 2, // SkPoints have 2 floats + reinterpret_cast(points.data()), + dl_paint); } } @@ -552,10 +563,10 @@ void Canvas::drawVertices(const Vertices* vertices, return; } FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { DlPaint dl_paint; paint.paint(dl_paint, kDrawVerticesFlags); - builder()->DrawVertices(vertices->vertices(), blend_mode, dl_paint); + dl_canvas_->DrawVertices(vertices->vertices(), blend_mode, dl_paint); } } @@ -590,7 +601,7 @@ Dart_Handle Canvas::drawAtlas(Dart_Handle paint_objects, auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); FML_DCHECK(paint.isNotNull()); - if (display_list_builder_) { + if (dl_canvas_) { tonic::Float32List transforms(transforms_handle); tonic::Float32List rects(rects_handle); tonic::Int32List colors(colors_handle); @@ -598,7 +609,7 @@ Dart_Handle Canvas::drawAtlas(Dart_Handle paint_objects, DlPaint dl_paint; const DlPaint* opt_paint = paint.paint(dl_paint, kDrawAtlasWithPaintFlags); - builder()->DrawAtlas( + dl_canvas_->DrawAtlas( dl_image, reinterpret_cast(transforms.data()), reinterpret_cast(rects.data()), reinterpret_cast(colors.data()), @@ -630,7 +641,7 @@ void Canvas::drawShadow(const CanvasPath* path, } else { dpr = static_cast(metrics->device_pixel_ratio); } - if (display_list_builder_) { + if (dl_canvas_) { // The DrawShadow mechanism results in non-public operations to be // performed on the canvas involving an SkDrawShadowRec. Since we // cannot include the header that defines that structure, we cannot @@ -638,13 +649,13 @@ void Canvas::drawShadow(const CanvasPath* path, // that situation we bypass the canvas interface and inject the // shadow parameters directly into the underlying DisplayList. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - builder()->DrawShadow(path->path(), color, SafeNarrow(elevation), - transparentOccluder, dpr); + dl_canvas_->DrawShadow(path->path(), color, SafeNarrow(elevation), + transparentOccluder, dpr); } } void Canvas::Invalidate() { - display_list_builder_ = nullptr; + dl_canvas_ = nullptr; if (dart_wrapper()) { ClearDartWrapper(); } diff --git a/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index 20ab1c9eafa9d..d735e875c840f 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -186,12 +186,12 @@ class Canvas : public RefCountedDartWrappable, DisplayListOpFlags { void Invalidate(); - DisplayListBuilder* builder() { return display_list_builder_.get(); } + DlCanvas* dl_canvas() { return dl_canvas_; } private: - explicit Canvas(sk_sp builder); + explicit Canvas(DlCanvas* canvas); - sk_sp display_list_builder_; + DlCanvas* dl_canvas_; }; } // namespace flutter diff --git a/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc b/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc index 0c2a7de57cca4..11692cd711860 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc +++ b/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc @@ -6,6 +6,7 @@ #include +#include "display_list_deferred_image_gpu_impeller.h" #include "flutter/fml/make_copyable.h" namespace flutter { @@ -13,22 +14,33 @@ namespace flutter { sk_sp DlDeferredImageGPUImpeller::Make( std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner) { + const fml::RefPtr& raster_task_runner) { return sk_sp(new DlDeferredImageGPUImpeller( DlDeferredImageGPUImpeller::ImageWrapper::Make( std::move(layer_tree), std::move(snapshot_delegate), - std::move(raster_task_runner)))); + raster_task_runner))); } sk_sp DlDeferredImageGPUImpeller::Make( sk_sp display_list, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner) { + const fml::RefPtr& raster_task_runner) { return sk_sp(new DlDeferredImageGPUImpeller( DlDeferredImageGPUImpeller::ImageWrapper::Make( std::move(display_list), size, std::move(snapshot_delegate), - std::move(raster_task_runner)))); + raster_task_runner))); +} + +sk_sp DlDeferredImageGPUImpeller::Make( + const std::shared_ptr& impeller_picture, + const SkISize& size, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner) { + return sk_sp(new DlDeferredImageGPUImpeller( + DlDeferredImageGPUImpeller::ImageWrapper::Make( + impeller_picture, size, std::move(snapshot_delegate), + raster_task_runner))); } DlDeferredImageGPUImpeller::DlDeferredImageGPUImpeller( @@ -96,22 +108,41 @@ DlDeferredImageGPUImpeller::ImageWrapper::Make( sk_sp display_list, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner) { - auto wrapper = std::shared_ptr(new ImageWrapper( - std::move(display_list), size, std::move(snapshot_delegate), - std::move(raster_task_runner))); + const fml::RefPtr& raster_task_runner) { + auto wrapper = std::shared_ptr( + new ImageWrapper(std::move(display_list), size, + std::move(snapshot_delegate), raster_task_runner)); wrapper->SnapshotDisplayList(); return wrapper; } +std::shared_ptr +DlDeferredImageGPUImpeller::ImageWrapper::Make( + const std::shared_ptr& impeller_picture, + const SkISize& size, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner) { + auto wrapper = std::shared_ptr(new ImageWrapper(size)); + wrapper->SnapshotPicture(impeller_picture, std::move(snapshot_delegate), + raster_task_runner); + return wrapper; +} + std::shared_ptr DlDeferredImageGPUImpeller::ImageWrapper::Make( std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner) { - auto wrapper = std::shared_ptr(new ImageWrapper( - nullptr, layer_tree->frame_size(), std::move(snapshot_delegate), - std::move(raster_task_runner))); + const fml::RefPtr& raster_task_runner) { + if (layer_tree) { + auto wrapper = std::shared_ptr( + new ImageWrapper(layer_tree->frame_size())); + wrapper->SnapshotLayer(std::move(layer_tree), std::move(snapshot_delegate), + raster_task_runner); + return wrapper; + } + auto wrapper = std::shared_ptr( + new ImageWrapper(sk_sp(), layer_tree->frame_size(), + std::move(snapshot_delegate), raster_task_runner)); wrapper->SnapshotDisplayList(std::move(layer_tree)); return wrapper; } @@ -126,14 +157,84 @@ DlDeferredImageGPUImpeller::ImageWrapper::ImageWrapper( snapshot_delegate_(std::move(snapshot_delegate)), raster_task_runner_(std::move(raster_task_runner)) {} -DlDeferredImageGPUImpeller::ImageWrapper::~ImageWrapper() { +DlDeferredImageGPUImpeller::ImageWrapper::ImageWrapper(const SkISize& size) + : size_(size) {} + +void DlDeferredImageGPUImpeller::ImageWrapper::SnapshotPicture( + const std::shared_ptr& impeller_picture, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner) { + fml::TaskRunner::RunNowOrPostTask( + raster_task_runner, + fml::MakeCopyable([weak_this = weak_from_this(), impeller_picture, + snapshot_delegate = std::move(snapshot_delegate)]() { + TRACE_EVENT0("flutter", "SnapshotPicture (impeller)"); + auto wrapper = weak_this.lock(); + if (!wrapper) { + return; + } + if (!snapshot_delegate) { + return; + } + + wrapper->texture_registry_ = snapshot_delegate->GetTextureRegistry(); + + auto snapshot = snapshot_delegate->MakeRasterSnapshot(impeller_picture, + wrapper->size_); + if (!snapshot) { + std::scoped_lock lock(wrapper->error_mutex_); + wrapper->error_ = "Failed to create snapshot."; + return; + } + wrapper->texture_ = snapshot->impeller_texture(); + })); +} + +void DlDeferredImageGPUImpeller::ImageWrapper::SnapshotLayer( + std::unique_ptr layer_tree, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner) { fml::TaskRunner::RunNowOrPostTask( - raster_task_runner_, [id = reinterpret_cast(this), - texture_registry = std::move(texture_registry_)]() { - if (texture_registry) { - texture_registry->UnregisterContextListener(id); + raster_task_runner, + fml::MakeCopyable([weak_this = weak_from_this(), + layer_tree = std::move(layer_tree), + snapshot_delegate = std::move(snapshot_delegate)]() { + TRACE_EVENT0("flutter", "SnapshotLayer (impeller)"); + auto wrapper = weak_this.lock(); + if (!wrapper) { + return; + } + if (!snapshot_delegate) { + return; } - }); + + wrapper->texture_registry_ = snapshot_delegate->GetTextureRegistry(); + + auto impeller_picture = layer_tree->FlattenToImpellerPicture( + SkRect::MakeWH(wrapper->size_.width(), wrapper->size_.height()), + wrapper->texture_registry_); + auto snapshot = snapshot_delegate->MakeRasterSnapshot(impeller_picture, + wrapper->size_); + if (!snapshot) { + std::scoped_lock lock(wrapper->error_mutex_); + wrapper->error_ = "Failed to create snapshot."; + return; + } + wrapper->texture_ = snapshot->impeller_texture(); + })); +} + +DlDeferredImageGPUImpeller::ImageWrapper::~ImageWrapper() { + if (display_list_) { + fml::TaskRunner::RunNowOrPostTask( + raster_task_runner_, + [id = reinterpret_cast(this), + texture_registry = std::move(texture_registry_)]() { + if (texture_registry) { + texture_registry->UnregisterContextListener(id); + } + }); + } } void DlDeferredImageGPUImpeller::ImageWrapper::OnGrContextCreated() { @@ -142,8 +243,9 @@ void DlDeferredImageGPUImpeller::ImageWrapper::OnGrContextCreated() { } void DlDeferredImageGPUImpeller::ImageWrapper::OnGrContextDestroyed() { - // Impeller textures do not have threading requirements for deletion, and - texture_.reset(); + if (display_list_) { + texture_.reset(); + } } bool DlDeferredImageGPUImpeller::ImageWrapper::isTextureBacked() const { @@ -152,6 +254,7 @@ bool DlDeferredImageGPUImpeller::ImageWrapper::isTextureBacked() const { void DlDeferredImageGPUImpeller::ImageWrapper::SnapshotDisplayList( std::unique_ptr layer_tree) { + FML_DCHECK(display_list_ || layer_tree); fml::TaskRunner::RunNowOrPostTask( raster_task_runner_, fml::MakeCopyable([weak_this = weak_from_this(), diff --git a/lib/ui/painting/display_list_deferred_image_gpu_impeller.h b/lib/ui/painting/display_list_deferred_image_gpu_impeller.h index caed73a7c86f8..2e57f1ba7ce12 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_impeller.h +++ b/lib/ui/painting/display_list_deferred_image_gpu_impeller.h @@ -20,13 +20,19 @@ class DlDeferredImageGPUImpeller final : public DlImage { static sk_sp Make( std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner); + const fml::RefPtr& raster_task_runner); static sk_sp Make( sk_sp display_list, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner); + const fml::RefPtr& raster_task_runner); + + static sk_sp Make( + const std::shared_ptr& impeller_picture, + const SkISize& size, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner); // |DlImage| ~DlDeferredImageGPUImpeller() override; @@ -67,12 +73,18 @@ class DlDeferredImageGPUImpeller final : public DlImage { sk_sp display_list, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner); + const fml::RefPtr& raster_task_runner); + + static std::shared_ptr Make( + const std::shared_ptr& impeller_picture, + const SkISize& size, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner); static std::shared_ptr Make( std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner); + const fml::RefPtr& raster_task_runner); bool isTextureBacked() const; @@ -101,6 +113,18 @@ class DlDeferredImageGPUImpeller final : public DlImage { fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner); + explicit ImageWrapper(const SkISize& size); + + void SnapshotPicture( + const std::shared_ptr& impeller_picture, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner); + + void SnapshotLayer( + std::unique_ptr layer_tree, + fml::TaskRunnerAffineWeakPtr snapshot_delegate, + const fml::RefPtr& raster_task_runner); + // If a layer tree is provided, it will be flattened during the raster // thread task spawned by this method. After being flattened into a display // list, the image wrapper will be updated to hold this display list and the diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 592643bee7d82..57ebd4d52f732 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -4,7 +4,6 @@ #include "flutter/lib/ui/painting/paint.h" -#include "flutter/display_list/dl_builder.h" #include "flutter/fml/logging.h" #include "flutter/lib/ui/floating_point.h" #include "flutter/lib/ui/painting/color_filter.h" @@ -146,10 +145,10 @@ const DlPaint* Paint::paint(DlPaint& paint, paint.setBlendMode(static_cast(blend_mode)); } - if (flags.applies_style()) { - uint32_t style = uint_data[kStyleIndex]; - paint.setDrawStyle(static_cast(style)); - } + // if (flags.applies_style()) { + uint32_t style = uint_data[kStyleIndex]; + paint.setDrawStyle(static_cast(style)); + // } if (flags.is_stroked(paint.getDrawStyle())) { float stroke_width = float_data[kStrokeWidthIndex]; diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 74c7b0dc78141..08c1ceb553497 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -16,9 +16,6 @@ #endif // IMPELLER_SUPPORTS_RENDERING #include "flutter/lib/ui/painting/display_list_image_gpu.h" #include "third_party/tonic/converter/dart_converter.h" -#include "third_party/tonic/dart_args.h" -#include "third_party/tonic/dart_binding_macros.h" -#include "third_party/tonic/dart_library_natives.h" #include "third_party/tonic/dart_persistent_value.h" #include "third_party/tonic/logging/dart_invoke.h" @@ -27,48 +24,53 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, Picture); fml::RefPtr Picture::Create(Dart_Handle dart_handle, - sk_sp display_list) { - FML_DCHECK(display_list->isUIThreadSafe()); - auto canvas_picture = fml::MakeRefCounted(std::move(display_list)); + DisplayListOrPicture picture) { + auto canvas_picture = fml::MakeRefCounted(std::move(picture)); canvas_picture->AssociateWithDartWrapper(dart_handle); return canvas_picture; } -Picture::Picture(sk_sp display_list) - : display_list_(std::move(display_list)) {} +Picture::Picture(DisplayListOrPicture picture) : picture_(std::move(picture)) {} Picture::~Picture() = default; Dart_Handle Picture::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - if (!display_list_) { + if (!impeller_picture() && !display_list()) { return tonic::ToDart("Picture is null"); } - return RasterizeToImage(display_list_, width, height, raw_image_callback); + return RasterizeToImage(width, height, raw_image_callback); } void Picture::toImageSync(uint32_t width, uint32_t height, Dart_Handle raw_image_handle) { - FML_DCHECK(display_list_); - RasterizeToImageSync(display_list_, width, height, raw_image_handle); + FML_DCHECK(impeller_picture() || display_list()); + RasterizeToImageSync(width, height, raw_image_handle); } static sk_sp CreateDeferredImage( bool impeller, sk_sp display_list, + const std::shared_ptr& impeller_picture, uint32_t width, uint32_t height, fml::TaskRunnerAffineWeakPtr snapshot_delegate, - fml::RefPtr raster_task_runner, + const fml::RefPtr& raster_task_runner, fml::RefPtr unref_queue) { #if IMPELLER_SUPPORTS_RENDERING if (impeller) { + if (display_list) { + return DlDeferredImageGPUImpeller::Make( + std::move(display_list), SkISize::Make(width, height), + std::move(snapshot_delegate), raster_task_runner); + } + FML_DCHECK(impeller_picture); return DlDeferredImageGPUImpeller::Make( - std::move(display_list), SkISize::Make(width, height), - std::move(snapshot_delegate), std::move(raster_task_runner)); + impeller_picture, SkISize::Make(width, height), + std::move(snapshot_delegate), raster_task_runner); } #endif // IMPELLER_SUPPORTS_RENDERING @@ -79,66 +81,126 @@ static sk_sp CreateDeferredImage( raster_task_runner, std::move(unref_queue)); } -// static -void Picture::RasterizeToImageSync(sk_sp display_list, - uint32_t width, +void Picture::RasterizeToImageSync(uint32_t width, uint32_t height, Dart_Handle raw_image_handle) { auto* dart_state = UIDartState::Current(); if (!dart_state) { return; } + auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner(); auto unref_queue = dart_state->GetSkiaUnrefQueue(); auto snapshot_delegate = dart_state->GetSnapshotDelegate(); - auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner(); auto image = CanvasImage::Create(); auto dl_image = CreateDeferredImage( - dart_state->IsImpellerEnabled(), std::move(display_list), width, height, - std::move(snapshot_delegate), std::move(raster_task_runner), + dart_state->IsImpellerEnabled(), display_list(), impeller_picture(), + width, height, std::move(snapshot_delegate), raster_task_runner, std::move(unref_queue)); + image->set_image(dl_image); image->AssociateWithDartWrapper(raw_image_handle); } void Picture::dispose() { - display_list_.reset(); + picture_ = sk_sp(nullptr); ClearDartWrapper(); } size_t Picture::GetAllocationSize() const { - if (display_list_) { - return display_list_->bytes() + sizeof(Picture); - } else { - return sizeof(Picture); + auto size = sizeof(Picture); + if (display_list()) { + size += display_list()->bytes(); } + // TODO(dnfield): Add support to EntityPass to get its allocation size. + return size; } -Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, - uint32_t width, +Dart_Handle Picture::RasterizeToImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - return DoRasterizeToImage(display_list, nullptr, width, height, - raw_image_callback); + return DoRasterizeToImage(width, height, raw_image_callback); } Dart_Handle Picture::RasterizeLayerTreeToImage( std::unique_ptr layer_tree, Dart_Handle raw_image_callback) { FML_DCHECK(layer_tree != nullptr); - auto frame_size = layer_tree->frame_size(); - return DoRasterizeToImage(nullptr, std::move(layer_tree), frame_size.width(), - frame_size.height(), raw_image_callback); + auto* dart_state = UIDartState::Current(); + auto image_callback = std::make_unique( + dart_state, raw_image_callback); + auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); + auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner(); + + auto unref_queue = dart_state->GetSkiaUnrefQueue(); + auto snapshot_delegate = dart_state->GetSnapshotDelegate(); + + // We can't create an image on this task runner because we don't have a + // graphics context. Even if we did, it would be slow anyway. Also, this + // thread owns the sole reference to the layer tree. So we do it in the + // raster thread. + + auto ui_task = + // The static leak checker gets confused by the use of + // fml::MakeCopyable. + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + fml::MakeCopyable([image_callback = std::move(image_callback), + unref_queue](sk_sp image) mutable { + auto dart_state = image_callback->dart_state().lock(); + if (!dart_state) { + // The root isolate could have died in the meantime. + return; + } + tonic::DartState::Scope scope(dart_state); + + if (!image) { + tonic::DartInvoke(image_callback->Get(), {Dart_Null()}); + return; + } + + if (!image->isUIThreadSafe()) { + // All images with impeller textures should already be safe. + FML_DCHECK(image->impeller_texture() == nullptr); + image = + DlImageGPU::Make({image->skia_image(), std::move(unref_queue)}); + } + + auto dart_image = CanvasImage::Create(); + dart_image->set_image(image); + auto* raw_dart_image = tonic::ToDart(dart_image); + + // All done! + tonic::DartInvoke(image_callback->Get(), {raw_dart_image}); + + // image_callback is associated with the Dart isolate and must be + // deleted on the UI thread. + image_callback.reset(); + }); + + // Kick things off on the raster rask runner. + fml::TaskRunner::RunNowOrPostTask( + raster_task_runner, + fml::MakeCopyable([ui_task_runner, snapshot_delegate, ui_task, + layer_tree = std::move(layer_tree)]() mutable { + sk_sp image; + auto display_list = layer_tree->Flatten( + SkRect::MakeXYWH(0, 0, layer_tree->frame_size().width(), + layer_tree->frame_size().height()), + snapshot_delegate->GetTextureRegistry(), + snapshot_delegate->GetGrContext()); + + image = snapshot_delegate->MakeRasterSnapshot(display_list, + layer_tree->frame_size()); + fml::TaskRunner::RunNowOrPostTask( + ui_task_runner, [ui_task, image]() { ui_task(image); }); + })); + + return Dart_Null(); } -Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, - std::unique_ptr layer_tree, - uint32_t width, +Dart_Handle Picture::DoRasterizeToImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - // Either display_list or layer_tree should be provided. - FML_DCHECK((display_list == nullptr) != (layer_tree == nullptr)); - if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { return tonic::ToDart("Image callback was invalid"); } @@ -150,9 +212,10 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, auto* dart_state = UIDartState::Current(); auto image_callback = std::make_unique( dart_state, raw_image_callback); - auto unref_queue = dart_state->GetSkiaUnrefQueue(); auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner(); + + auto unref_queue = dart_state->GetSkiaUnrefQueue(); auto snapshot_delegate = dart_state->GetSnapshotDelegate(); // We can't create an image on this task runner because we don't have a @@ -161,7 +224,8 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, // raster thread. auto ui_task = - // The static leak checker gets confused by the use of fml::MakeCopyable. + // The static leak checker gets confused by the use of + // fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) fml::MakeCopyable([image_callback = std::move(image_callback), unref_queue](sk_sp image) mutable { @@ -199,22 +263,18 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, // Kick things off on the raster rask runner. fml::TaskRunner::RunNowOrPostTask( raster_task_runner, - fml::MakeCopyable([ui_task_runner, snapshot_delegate, display_list, width, - height, ui_task, - layer_tree = std::move(layer_tree)]() mutable { + fml::MakeCopyable([ui_task_runner, snapshot_delegate, + display_list = display_list(), + impeller_picture = impeller_picture(), width, height, + ui_task]() mutable { auto picture_bounds = SkISize::Make(width, height); sk_sp image; - if (layer_tree) { - FML_DCHECK(picture_bounds == layer_tree->frame_size()); - auto display_list = - layer_tree->Flatten(SkRect::MakeWH(width, height), - snapshot_delegate->GetTextureRegistry(), - snapshot_delegate->GetGrContext()); - + if (display_list) { image = snapshot_delegate->MakeRasterSnapshot(display_list, picture_bounds); } else { - image = snapshot_delegate->MakeRasterSnapshot(display_list, + FML_DCHECK(impeller_picture); + image = snapshot_delegate->MakeRasterSnapshot(impeller_picture, picture_bounds); } diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index 6d41cbe8dad68..696de61b2230c 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -5,15 +5,29 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_H_ +#include + #include "flutter/display_list/display_list.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/ui_dart_state.h" +#include "third_party/skia/include/core/SkPicture.h" + +#if IMPELLER_SUPPORTS_RENDERING +#include "impeller/aiks/picture.h" // nogncheck +#else // IMPELLER_SUPPORTS_RENDERING +namespace impeller { +struct Picture; +} // namespace impeller +#endif // !IMPELLER_SUPPORTS_RENDERING namespace flutter { class Canvas; +using DisplayListOrPicture = + std::variant, std::shared_ptr>; + class Picture : public RefCountedDartWrappable { DEFINE_WRAPPERTYPEINFO(); FML_FRIEND_MAKE_REF_COUNTED(Picture); @@ -21,9 +35,22 @@ class Picture : public RefCountedDartWrappable { public: ~Picture() override; static fml::RefPtr Create(Dart_Handle dart_handle, - sk_sp display_list); - - sk_sp display_list() const { return display_list_; } + DisplayListOrPicture picture); + + const sk_sp display_list() const { + if (std::holds_alternative>(picture_)) { + return std::get>(picture_); + } + return nullptr; + } + + std::shared_ptr impeller_picture() const { + if (std::holds_alternative>( + picture_)) { + return std::get>(picture_); + } + return nullptr; + } Dart_Handle toImage(uint32_t width, uint32_t height, @@ -37,34 +64,26 @@ class Picture : public RefCountedDartWrappable { size_t GetAllocationSize() const; - static void RasterizeToImageSync(sk_sp display_list, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_handle); - - static Dart_Handle RasterizeToImage(const sk_sp& display_list, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_callback); - static Dart_Handle RasterizeLayerTreeToImage( std::unique_ptr layer_tree, Dart_Handle raw_image_callback); - // Callers may provide either a display list or a layer tree, but not both. - // - // If a layer tree is provided, it will be flattened on the raster thread, and - // picture_bounds should be the layer tree's frame_size(). - static Dart_Handle DoRasterizeToImage(const sk_sp& display_list, - std::unique_ptr layer_tree, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_callback); - private: - explicit Picture(sk_sp display_list); + explicit Picture(DisplayListOrPicture picture); + + DisplayListOrPicture picture_; + + void RasterizeToImageSync(uint32_t width, + uint32_t height, + Dart_Handle raw_image_handle); + + Dart_Handle RasterizeToImage(uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); - sk_sp display_list_; + Dart_Handle DoRasterizeToImage(uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); }; } // namespace flutter diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index a99b5e5855e40..88e1e6844ee5a 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -6,6 +6,7 @@ #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/painting/picture.h" +#include "impeller/display_list/skia_conversions.h" // nogncheck #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" @@ -25,21 +26,39 @@ PictureRecorder::PictureRecorder() {} PictureRecorder::~PictureRecorder() {} -sk_sp PictureRecorder::BeginRecording(SkRect bounds) { - display_list_builder_ = - sk_make_sp(bounds, /*prepare_rtree=*/true); - return display_list_builder_; +DlCanvas* PictureRecorder::BeginRecording(SkRect bounds) { +#if IMPELLER_SUPPORTS_RENDERING + if (UIDartState::Current()->IsImpellerEnabled()) { + dl_aiks_canvas_ = std::make_shared(bounds); + return dl_aiks_canvas_.get(); + } else { +#endif // IMPELLER_SUPPORTS_RENDERING + builder_ = sk_make_sp(bounds, /*prepare_rtree=*/true); + return builder_.get(); +#if IMPELLER_SUPPORTS_RENDERING + } +#endif // IMPELLER_SUPPORTS_RENDERING } fml::RefPtr PictureRecorder::endRecording(Dart_Handle dart_picture) { if (!canvas_) { return nullptr; } - fml::RefPtr picture; - picture = Picture::Create(dart_picture, display_list_builder_->Build()); - display_list_builder_ = nullptr; +#if IMPELLER_SUPPORTS_RENDERING + if (UIDartState::Current()->IsImpellerEnabled()) { + picture = Picture::Create(dart_picture, + std::make_shared( + dl_aiks_canvas_->EndRecordingAsPicture())); + dl_aiks_canvas_ = nullptr; + } else { +#endif + picture = Picture::Create(dart_picture, builder_->Build()); + builder_ = nullptr; +#if IMPELLER_SUPPORTS_RENDERING + } +#endif // IMPELLER_SUPPORTS_RENDERING canvas_->Invalidate(); canvas_ = nullptr; diff --git a/lib/ui/painting/picture_recorder.h b/lib/ui/painting/picture_recorder.h index c5046a424a6ac..079294b86314d 100644 --- a/lib/ui/painting/picture_recorder.h +++ b/lib/ui/painting/picture_recorder.h @@ -5,9 +5,19 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ -#include "flutter/display_list/dl_builder.h" +#include + +#include "flutter/display_list/display_list_builder.h" #include "flutter/lib/ui/dart_wrapper.h" +#if IMPELLER_SUPPORTS_RENDERING +#include "impeller/display_list/dl_aiks_canvas.h" // nogncheck +#else // IMPELLER_SUPPORTS_RENDERING +namespace impeller { +class DlAiksCanvas; +} // namespace impeller +#endif // !IMPELLER_SUPPORTS_RENDERING + namespace flutter { class Canvas; class Picture; @@ -21,7 +31,7 @@ class PictureRecorder : public RefCountedDartWrappable { ~PictureRecorder() override; - sk_sp BeginRecording(SkRect bounds); + DlCanvas* BeginRecording(SkRect bounds); fml::RefPtr endRecording(Dart_Handle dart_picture); void set_canvas(fml::RefPtr canvas) { canvas_ = std::move(canvas); } @@ -29,7 +39,8 @@ class PictureRecorder : public RefCountedDartWrappable { private: PictureRecorder(); - sk_sp display_list_builder_; + std::shared_ptr dl_aiks_canvas_; + sk_sp builder_; fml::RefPtr canvas_; }; diff --git a/lib/ui/snapshot_delegate.h b/lib/ui/snapshot_delegate.h index 2c3677d8551a8..d82f39e66cf56 100644 --- a/lib/ui/snapshot_delegate.h +++ b/lib/ui/snapshot_delegate.h @@ -70,6 +70,10 @@ class SnapshotDelegate { virtual sk_sp MakeRasterSnapshot(sk_sp display_list, SkISize picture_size) = 0; + virtual sk_sp MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize picture_size) = 0; + virtual sk_sp ConvertToRasterImage(sk_sp image) = 0; }; diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 624ed145dcf38..4c75bdef96d93 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -64,9 +64,9 @@ void Paragraph::paint(Canvas* canvas, double x, double y) { return; } - DisplayListBuilder* builder = canvas->builder(); - if (builder) { - m_paragraph->Paint(builder, x, y); + DlCanvas* dl_canvas = canvas->dl_canvas(); + if (dl_canvas) { + m_paragraph->Paint(dl_canvas, x, y); } } diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index c5a4db91e4333..978aaa7d1e690 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -85,8 +85,6 @@ source_set("common") { "context_options.h", "display_manager.cc", "display_manager.h", - "dl_op_spy.cc", - "dl_op_spy.h", "engine.cc", "engine.h", "pipeline.cc", @@ -294,7 +292,6 @@ if (enable_unittests) { sources = [ "animator_unittests.cc", "context_options_unittests.cc", - "dl_op_spy_unittests.cc", "engine_unittests.cc", "input_events_unittests.cc", "persistent_cache_unittests.cc", diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 03e7c4e7a59d8..7ccf4aee73c50 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -379,6 +379,12 @@ sk_sp Rasterizer::MakeRasterSnapshot(sk_sp display_list, return snapshot_controller_->MakeRasterSnapshot(display_list, picture_size); } +sk_sp Rasterizer::MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize picture_size) { + return snapshot_controller_->MakeRasterSnapshot(picture, picture_size); +} + sk_sp Rasterizer::ConvertToRasterImage(sk_sp image) { TRACE_EVENT0("flutter", __FUNCTION__); return snapshot_controller_->ConvertToRasterImage(image); diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index a54dd677a2fdb..8d0b0a2271411 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -534,6 +534,11 @@ class Rasterizer final : public SnapshotDelegate, sk_sp MakeRasterSnapshot(sk_sp display_list, SkISize picture_size) override; + // |SnapshotDelegate| + sk_sp MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize picture_size) override; + // |SnapshotDelegate| sk_sp ConvertToRasterImage(sk_sp image) override; diff --git a/shell/common/snapshot_controller.h b/shell/common/snapshot_controller.h index c402c3ec709fa..a5709f9d2d226 100644 --- a/shell/common/snapshot_controller.h +++ b/shell/common/snapshot_controller.h @@ -42,6 +42,10 @@ class SnapshotController { virtual sk_sp MakeRasterSnapshot(sk_sp display_list, SkISize size) = 0; + virtual sk_sp MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize size) = 0; + virtual sk_sp ConvertToRasterImage(sk_sp image) = 0; protected: diff --git a/shell/common/snapshot_controller_impeller.cc b/shell/common/snapshot_controller_impeller.cc index ea68bd91188a4..79c3bf5c65095 100644 --- a/shell/common/snapshot_controller_impeller.cc +++ b/shell/common/snapshot_controller_impeller.cc @@ -30,13 +30,36 @@ sk_sp SnapshotControllerImpeller::MakeRasterSnapshot( return result; } +sk_sp SnapshotControllerImpeller::MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize size) { + sk_sp result; + if (!picture) { + return result; + } + GetDelegate().GetIsGpuDisabledSyncSwitch()->Execute( + fml::SyncSwitch::Handlers() + .SetIfTrue([&] { + // Do nothing. + }) + .SetIfFalse([&] { result = DoMakeRasterSnapshot(*picture, size); })); + + return result; +} + sk_sp SnapshotControllerImpeller::DoMakeRasterSnapshot( const sk_sp& display_list, SkISize size) { TRACE_EVENT0("flutter", __FUNCTION__); impeller::DlDispatcher dispatcher; display_list->Dispatch(dispatcher); - impeller::Picture picture = dispatcher.EndRecordingAsPicture(); + return DoMakeRasterSnapshot(dispatcher.EndRecordingAsPicture(), size); +} + +sk_sp SnapshotControllerImpeller::DoMakeRasterSnapshot( + const impeller::Picture& picture, + SkISize size) { + TRACE_EVENT0("flutter", __FUNCTION__); auto context = GetDelegate().GetAiksContext(); if (context) { auto max_size = context->GetContext() diff --git a/shell/common/snapshot_controller_impeller.h b/shell/common/snapshot_controller_impeller.h index d9f1d632e3553..5bd40a969597f 100644 --- a/shell/common/snapshot_controller_impeller.h +++ b/shell/common/snapshot_controller_impeller.h @@ -18,12 +18,19 @@ class SnapshotControllerImpeller : public SnapshotController { sk_sp MakeRasterSnapshot(sk_sp display_list, SkISize size) override; + sk_sp MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize size) override; + sk_sp ConvertToRasterImage(sk_sp image) override; private: sk_sp DoMakeRasterSnapshot(const sk_sp& display_list, SkISize size); + sk_sp DoMakeRasterSnapshot(const impeller::Picture& picture, + SkISize size); + FML_DISALLOW_COPY_AND_ASSIGN(SnapshotControllerImpeller); }; diff --git a/shell/common/snapshot_controller_skia.cc b/shell/common/snapshot_controller_skia.cc index fe7897f38d178..096dfac614666 100644 --- a/shell/common/snapshot_controller_skia.cc +++ b/shell/common/snapshot_controller_skia.cc @@ -137,6 +137,13 @@ sk_sp SnapshotControllerSkia::MakeRasterSnapshot( }); } +sk_sp SnapshotControllerSkia::MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize size) { + FML_DCHECK(false); + return sk_sp(); +} + sk_sp SnapshotControllerSkia::ConvertToRasterImage( sk_sp image) { // If the rasterizer does not have a surface with a GrContext, then it will diff --git a/shell/common/snapshot_controller_skia.h b/shell/common/snapshot_controller_skia.h index 8be1d8e1ec274..09a48a4251a36 100644 --- a/shell/common/snapshot_controller_skia.h +++ b/shell/common/snapshot_controller_skia.h @@ -18,6 +18,10 @@ class SnapshotControllerSkia : public SnapshotController { sk_sp MakeRasterSnapshot(sk_sp display_list, SkISize size) override; + sk_sp MakeRasterSnapshot( + const std::shared_ptr& picture, + SkISize size) override; + virtual sk_sp ConvertToRasterImage(sk_sp image) override; private: diff --git a/shell/gpu/gpu_surface_gl_impeller.cc b/shell/gpu/gpu_surface_gl_impeller.cc index 19e2669c997a2..9bea5b0cd082c 100644 --- a/shell/gpu/gpu_surface_gl_impeller.cc +++ b/shell/gpu/gpu_surface_gl_impeller.cc @@ -5,10 +5,9 @@ #include "flutter/shell/gpu/gpu_surface_gl_impeller.h" #include "flutter/fml/make_copyable.h" -#include "impeller/display_list/dl_dispatcher.h" -#include "impeller/renderer/backend/gles/surface_gles.h" -#include "impeller/renderer/renderer.h" -#include "impeller/typographer/backends/skia/typographer_context_skia.h" +#include "flutter/impeller/renderer/backend/gles/surface_gles.h" +#include "flutter/impeller/renderer/renderer.h" +#include "flutter/impeller/typographer/backends/skia/typographer_context_skia.h" namespace flutter { @@ -102,27 +101,14 @@ std::unique_ptr GPUSurfaceGLImpeller::AcquireFrame( return false; } - auto display_list = surface_frame.BuildDisplayList(); - if (!display_list) { - FML_LOG(ERROR) << "Could not build display list for surface frame."; - return false; - } - - auto cull_rect = - surface->GetTargetRenderPassDescriptor().GetRenderTargetSize(); - impeller::Rect dl_cull_rect = impeller::Rect::MakeSize(cull_rect); - impeller::DlDispatcher impeller_dispatcher(dl_cull_rect); - display_list->Dispatch( - impeller_dispatcher, - SkIRect::MakeWH(cull_rect.width, cull_rect.height)); - auto picture = impeller_dispatcher.EndRecordingAsPicture(); + auto picture = surface_frame.GetImpellerPicture(); return renderer->Render( std::move(surface), fml::MakeCopyable( [aiks_context, picture = std::move(picture)]( impeller::RenderTarget& render_target) -> bool { - return aiks_context->Render(picture, render_target); + return aiks_context->Render(*picture, render_target); })); }); diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index d76b13824c86e..4c2b117a80592 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -11,9 +11,8 @@ #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/fml/trace_event.h" -#include "impeller/display_list/dl_dispatcher.h" -#include "impeller/renderer/backend/metal/surface_mtl.h" -#include "impeller/typographer/backends/skia/typographer_context_skia.h" +#include "flutter/impeller/renderer/backend/metal/surface_mtl.h" +#include "flutter/impeller/typographer/backends/skia/typographer_context_skia.h" static_assert(!__has_feature(objc_arc), "ARC must be disabled."); @@ -115,12 +114,6 @@ return false; } - auto display_list = surface_frame.BuildDisplayList(); - if (!display_list) { - FML_LOG(ERROR) << "Could not build display list for surface frame."; - return false; - } - if (!disable_partial_repaint_) { uintptr_t texture = reinterpret_cast(last_texture); @@ -150,17 +143,13 @@ return surface->Present(); } - impeller::IRect cull_rect = surface->coverage(); - SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.size.width, cull_rect.size.height); - impeller::DlDispatcher impeller_dispatcher(cull_rect); - display_list->Dispatch(impeller_dispatcher, sk_cull_rect); - auto picture = impeller_dispatcher.EndRecordingAsPicture(); + auto picture = surface_frame.GetImpellerPicture(); return renderer->Render( std::move(surface), fml::MakeCopyable([aiks_context, picture = std::move(picture)]( impeller::RenderTarget& render_target) -> bool { - return aiks_context->Render(picture, render_target); + return aiks_context->Render(*picture, render_target); })); }); @@ -213,12 +202,6 @@ return false; } - auto display_list = surface_frame.BuildDisplayList(); - if (!display_list) { - FML_LOG(ERROR) << "Could not build display list for surface frame."; - return false; - } - if (!disable_partial_repaint_) { uintptr_t texture_ptr = reinterpret_cast(mtl_texture); @@ -248,17 +231,13 @@ return surface->Present(); } - impeller::IRect cull_rect = surface->coverage(); - SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.size.width, cull_rect.size.height); - impeller::DlDispatcher impeller_dispatcher(cull_rect); - display_list->Dispatch(impeller_dispatcher, sk_cull_rect); - auto picture = impeller_dispatcher.EndRecordingAsPicture(); + auto picture = surface_frame.GetImpellerPicture(); bool render_result = renderer->Render(std::move(surface), fml::MakeCopyable([aiks_context, picture = std::move(picture)]( impeller::RenderTarget& render_target) -> bool { - return aiks_context->Render(picture, render_target); + return aiks_context->Render(*picture, render_target); })); if (!render_result) { FML_LOG(ERROR) << "Failed to render Impeller frame"; diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index 3b6ec9d876ec0..5c798bbca3732 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -5,7 +5,7 @@ #include "flutter/shell/gpu/gpu_surface_vulkan_impeller.h" #include "flutter/fml/make_copyable.h" -#include "impeller/display_list/dl_dispatcher.h" +#include "flutter/impeller/renderer/renderer.h" #include "impeller/renderer/backend/vulkan/surface_context_vk.h" #include "impeller/renderer/renderer.h" #include "impeller/renderer/surface.h" @@ -69,27 +69,14 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( return false; } - auto display_list = surface_frame.BuildDisplayList(); - if (!display_list) { - FML_LOG(ERROR) << "Could not build display list for surface frame."; - return false; - } - - auto cull_rect = - surface->GetTargetRenderPassDescriptor().GetRenderTargetSize(); - impeller::Rect dl_cull_rect = impeller::Rect::MakeSize(cull_rect); - impeller::DlDispatcher impeller_dispatcher(dl_cull_rect); - display_list->Dispatch( - impeller_dispatcher, - SkIRect::MakeWH(cull_rect.width, cull_rect.height)); - auto picture = impeller_dispatcher.EndRecordingAsPicture(); + auto picture = surface_frame.GetImpellerPicture(); return renderer->Render( std::move(surface), fml::MakeCopyable( [aiks_context, picture = std::move(picture)]( impeller::RenderTarget& render_target) -> bool { - return aiks_context->Render(picture, render_target); + return aiks_context->Render(*picture, render_target); })); }); diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index d29a80eb16a8c..10fc5fc4d27a8 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -12,13 +12,15 @@ AndroidExternalViewEmbedder::AndroidExternalViewEmbedder( const AndroidContext& android_context, std::shared_ptr jni_facade, std::shared_ptr surface_factory, - const TaskRunners& task_runners) + const TaskRunners& task_runners, + bool enable_impeller) : ExternalViewEmbedder(), android_context_(android_context), jni_facade_(std::move(jni_facade)), surface_factory_(std::move(surface_factory)), surface_pool_(std::make_unique()), - task_runners_(task_runners) {} + task_runners_(task_runners), + enable_impeller_(enable_impeller) {} // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView( @@ -29,7 +31,11 @@ void AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView( SkRect view_bounds = SkRect::Make(frame_size_); std::unique_ptr view; - view = std::make_unique(view_bounds); + if (enable_impeller_) { + view = std::make_unique(view_bounds); + } else { + view = std::make_unique(view_bounds); + } slices_.insert_or_assign(view_id, std::move(view)); composition_order_.push_back(view_id); diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index 400b43fb91a89..0f1d69e955d48 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -32,7 +32,8 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { const AndroidContext& android_context, std::shared_ptr jni_facade, std::shared_ptr surface_factory, - const TaskRunners& task_runners); + const TaskRunners& task_runners, + bool enable_impeller); // |ExternalViewEmbedder| void PrerollCompositeEmbeddedView( @@ -123,6 +124,8 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // The number of platform views in the previous frame. int64_t previous_frame_view_count_; + bool enable_impeller_ = false; + // Destroys the surfaces created from the surface factory. // This method schedules a task on the platform thread, and waits for // the task until it completes. diff --git a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc index e6417a08f0800..40d9e9fd526b5 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc @@ -107,7 +107,7 @@ TaskRunners GetTaskRunnersForFixture() { TEST(AndroidExternalViewEmbedder, CompositeEmbeddedView) { auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, nullptr, nullptr, GetTaskRunnersForFixture()); + android_context, nullptr, nullptr, GetTaskRunnersForFixture(), false); ASSERT_EQ(nullptr, embedder->CompositeEmbeddedView(0)); embedder->PrerollCompositeEmbeddedView( @@ -123,7 +123,7 @@ TEST(AndroidExternalViewEmbedder, CompositeEmbeddedView) { TEST(AndroidExternalViewEmbedder, CancelFrame) { auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, nullptr, nullptr, GetTaskRunnersForFixture()); + android_context, nullptr, nullptr, GetTaskRunnersForFixture(), false); embedder->PrerollCompositeEmbeddedView( 0, std::make_unique()); @@ -136,7 +136,7 @@ TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) { auto jni_mock = std::make_shared(); auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); fml::Thread rasterizer_thread("rasterizer"); auto raster_thread_merger = @@ -170,7 +170,7 @@ TEST(AndroidExternalViewEmbedder, RasterizerRunsOnRasterizerThread) { auto jni_mock = std::make_shared(); auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); fml::Thread rasterizer_thread("rasterizer"); auto raster_thread_merger = @@ -191,7 +191,7 @@ TEST(AndroidExternalViewEmbedder, PlatformViewRect) { auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); fml::Thread rasterizer_thread("rasterizer"); auto raster_thread_merger = GetThreadMergerFromPlatformThread(&rasterizer_thread); @@ -219,7 +219,7 @@ TEST(AndroidExternalViewEmbedder, PlatformViewRectChangedParams) { auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); fml::Thread rasterizer_thread("rasterizer"); auto raster_thread_merger = GetThreadMergerFromPlatformThread(&rasterizer_thread); @@ -295,7 +295,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { return android_surface_mock; }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); auto raster_thread_merger = GetThreadMergerFromPlatformThread(); @@ -494,7 +495,8 @@ TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { return android_surface_mock; }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); auto raster_thread_merger = GetThreadMergerFromPlatformThread(); @@ -592,7 +594,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { return android_surface_mock; }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); auto raster_thread_merger = GetThreadMergerFromPlatformThread(); @@ -695,7 +698,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFramePlatformViewWithoutAnyOverlay) { return android_surface_mock; }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); auto raster_thread_merger = GetThreadMergerFromPlatformThread(); @@ -735,7 +739,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotCallJNIPlatformThreadOnlyMethods) { auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); // While on the raster thread, don't make JNI calls as these methods can only // run on the platform thread. @@ -784,7 +788,8 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); fml::Thread rasterizer_thread("rasterizer"); auto raster_thread_merger = GetThreadMergerFromPlatformThread(&rasterizer_thread); @@ -872,7 +877,8 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); // ------------------ First frame ------------------ // { @@ -930,7 +936,7 @@ TEST(AndroidExternalViewEmbedder, SupportsDynamicThreadMerging) { auto jni_mock = std::make_shared(); auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); ASSERT_TRUE(embedder->SupportsDynamicThreadMerging()); } @@ -938,7 +944,7 @@ TEST(AndroidExternalViewEmbedder, DisableThreadMerger) { auto jni_mock = std::make_shared(); auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); fml::Thread platform_thread("platform"); auto raster_thread_merger = GetThreadMergerFromRasterThread(&platform_thread); @@ -994,7 +1000,8 @@ TEST(AndroidExternalViewEmbedder, Teardown) { }); auto embedder = std::make_unique( - *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture(), + false); fml::Thread rasterizer_thread("rasterizer"); auto raster_thread_merger = GetThreadMergerFromPlatformThread(&rasterizer_thread); @@ -1037,7 +1044,7 @@ TEST(AndroidExternalViewEmbedder, TeardownDoesNotCallJNIMethod) { auto android_context = std::make_shared(AndroidRenderingAPI::kSoftware); auto embedder = std::make_unique( - *android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); + *android_context, jni_mock, nullptr, GetTaskRunnersForFixture(), false); EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0); embedder->Teardown(); diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index ac6111c343ada..dabad16e3e4a5 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -363,7 +363,8 @@ std::unique_ptr PlatformViewAndroid::CreateRenderingSurface() { std::shared_ptr PlatformViewAndroid::CreateExternalViewEmbedder() { return std::make_shared( - *android_context_, jni_facade_, surface_factory_, task_runners_); + *android_context_, jni_facade_, surface_factory_, task_runners_, + delegate_.OnPlatformViewGetSettings().enable_impeller); } // |PlatformView| diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index b300714f166ad..4639c63038253 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -267,7 +267,7 @@ - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center { - (void)recreatePlatformViewController { _renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering); - _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); + _platformViewsController.reset(new flutter::FlutterPlatformViewsController(self.isUsingImpeller)); } - (flutter::IOSRenderingAPI)platformViewsRenderingAPI { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index bf7e607878d1b..13a8ca55fcf04 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -387,7 +387,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, SkRect view_bounds = SkRect::Make(frame_size_); std::unique_ptr view; - view = std::make_unique(view_bounds); + if (enable_impeller_) { + view = std::make_unique(view_bounds); + } else { + view = std::make_unique(view_bounds); + } slices_.insert_or_assign(view_id, std::move(view)); composition_order_.push_back(view_id); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index bba450217747a..95affd15e08d4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -145,7 +145,8 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -201,7 +202,8 @@ - (void)testCanCreatePlatformViewWithoutFlutterView { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -255,7 +257,8 @@ - (void)testApplyBackdropFilter { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -327,7 +330,8 @@ - (void)testApplyBackdropFilterWithCorrectFrame { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -399,7 +403,8 @@ - (void)testApplyMultipleBackdropFilters { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -472,7 +477,8 @@ - (void)testAddBackdropFilters { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -585,7 +591,8 @@ - (void)testRemoveBackdropFilters { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -722,7 +729,8 @@ - (void)testEditBackdropFilters { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -999,7 +1007,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1299,7 +1308,8 @@ - (void)testCompositePlatformView { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1355,7 +1365,8 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1449,7 +1460,8 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1520,7 +1532,8 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1587,7 +1600,8 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1653,7 +1667,8 @@ - (void)testClipRect { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1726,7 +1741,8 @@ - (void)testClipRRect { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1799,7 +1815,8 @@ - (void)testClipPath { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1873,7 +1890,8 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -1937,7 +1955,8 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2058,7 +2077,8 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2169,7 +2189,8 @@ - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2231,7 +2252,8 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2293,7 +2315,8 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2343,7 +2366,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2399,7 +2423,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2497,7 +2522,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2595,7 +2621,8 @@ - (void)testThreadMergeAtEndFrame { /*raster=*/thread_task_runner_other, /*ui=*/thread_task_runner_other, /*io=*/thread_task_runner_other); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2748,7 +2775,8 @@ - (void)testClipMaskViewIsReused { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2830,7 +2858,8 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2930,7 +2959,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -3038,7 +3068,8 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 43cb43cf4e33b..f1faf14373a3a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -198,7 +198,7 @@ class FlutterPlatformViewLayerPool { class FlutterPlatformViewsController { public: - FlutterPlatformViewsController(); + explicit FlutterPlatformViewsController(bool enable_impeller); ~FlutterPlatformViewsController(); @@ -330,6 +330,8 @@ class FlutterPlatformViewsController { // Resets the state of the frame. void ResetFrameState(); + bool enable_impeller_ = true; + // The pool of reusable view layers. The pool allows to recycle layer in each frame. std::unique_ptr layer_pool_; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index e84b5746d167f..fa9dc601af186 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -25,8 +25,9 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() - : layer_pool_(std::make_unique()), +FlutterPlatformViewsController::FlutterPlatformViewsController(bool enable_impeller) + : enable_impeller_(enable_impeller), + layer_pool_(std::make_unique()), weak_factory_(std::make_unique>(this)) { mask_view_pool_.reset( [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 283d4fe02decd..e93addec16035 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -274,7 +274,7 @@ - (void)testSemanticsDeallocated { /*io=*/thread_task_runner); auto flutterPlatformViewsController = - std::make_shared(); + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -333,7 +333,7 @@ - (void)testSemanticsDeallocatedWithoutLoadingView { /*io=*/thread_task_runner); auto flutterPlatformViewsController = - std::make_shared(); + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -378,7 +378,8 @@ - (void)testReplacedSemanticsDoesNotCleanupChildren { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -472,7 +473,8 @@ - (void)testScrollableSemanticsDeallocated { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -544,7 +546,8 @@ - (void)testBridgeReplacesSemanticsNode { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(false); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, @@ -2064,7 +2067,7 @@ - (void)testPlatformViewDestructorDoesNotCallSemanticsAPIs { id mockFlutterViewController = OCMClassMock([FlutterViewController class]); auto flutterPlatformViewsController = - std::make_shared(); + std::make_shared(false); OCMStub([mockFlutterViewController platformViewsController]) .andReturn(flutterPlatformViewsController.get()); auto weakFactory = diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index af32d9ffa3f1c..d6913b400c654 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1312,7 +1312,7 @@ InferExternalViewEmbedderFromArgs(const FlutterCompositor* compositor, return {std::make_unique( avoid_backing_store_cache, create_render_target_callback, - present_callback), + present_callback, enable_impeller), false}; } diff --git a/shell/platform/embedder/embedder_external_view.cc b/shell/platform/embedder/embedder_external_view.cc index 843ef85dfed91..c28d2fce96f94 100644 --- a/shell/platform/embedder/embedder_external_view.cc +++ b/shell/platform/embedder/embedder_external_view.cc @@ -4,13 +4,8 @@ #include "flutter/shell/platform/embedder/embedder_external_view.h" -#include "flutter/display_list/dl_builder.h" +#include "flow/embedded_views.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/common/dl_op_spy.h" - -#ifdef IMPELLER_SUPPORTS_RENDERING -#include "impeller/display_list/dl_dispatcher.h" // nogncheck -#endif // IMPELLER_SUPPORTS_RENDERING namespace flutter { @@ -23,21 +18,37 @@ static SkISize TransformedSurfaceSize(const SkISize& size, EmbedderExternalView::EmbedderExternalView( const SkISize& frame_size, - const SkMatrix& surface_transformation) - : EmbedderExternalView(frame_size, surface_transformation, {}, nullptr) {} + const SkMatrix& surface_transformation, + bool enable_impeller) + : EmbedderExternalView(frame_size, + surface_transformation, + {}, + nullptr, + enable_impeller) {} EmbedderExternalView::EmbedderExternalView( const SkISize& frame_size, const SkMatrix& surface_transformation, ViewIdentifier view_identifier, - std::unique_ptr params) + std::unique_ptr params, + bool enable_impeller) : render_surface_size_( TransformedSurfaceSize(frame_size, surface_transformation)), surface_transformation_(surface_transformation), view_identifier_(view_identifier), - embedded_view_params_(std::move(params)), - slice_(std::make_unique( - SkRect::Make(frame_size))) {} + embedded_view_params_(std::move(params)) { +#if IMPELLER_SUPPORTS_RENDERING + if (enable_impeller) { + slice_ = + std::make_unique(SkRect::Make(frame_size)); + } else { +#endif // IMPELLER_SUPPORTS_RENDERING + slice_ = std::make_unique( + SkRect::Make(frame_size)); +#if IMPELLER_SUPPORTS_RENDERING + } +#endif // IMPELLER_SUPPORTS_RENDERING +} EmbedderExternalView::~EmbedderExternalView() = default; @@ -72,9 +83,7 @@ bool EmbedderExternalView::HasEngineRenderedContents() { return has_engine_rendered_contents_.value(); } TryEndRecording(); - DlOpSpy dl_op_spy; - slice_->dispatch(dl_op_spy); - has_engine_rendered_contents_ = dl_op_spy.did_draw() && !slice_->is_empty(); + has_engine_rendered_contents_ = slice_->renders_anything(); return has_engine_rendered_contents_.value(); } @@ -99,13 +108,12 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target) { if (impeller_target) { auto aiks_context = render_target.GetAiksContext(); - auto dl_builder = DisplayListBuilder(); - dl_builder.SetTransform(&surface_transformation_); - slice_->render_into(&dl_builder); + impeller::DlAiksCanvas dl_canvas( + SkRect::Make(render_target.GetRenderTargetSize())); + dl_canvas.SetTransform(&surface_transformation_); + slice_->render_into(&dl_canvas); - auto dispatcher = impeller::DlDispatcher(); - dispatcher.drawDisplayList(dl_builder.Build(), 1); - return aiks_context->Render(dispatcher.EndRecordingAsPicture(), + return aiks_context->Render(dl_canvas.EndRecordingAsPicture(), *impeller_target); } #endif // IMPELLER_SUPPORTS_RENDERING diff --git a/shell/platform/embedder/embedder_external_view.h b/shell/platform/embedder/embedder_external_view.h index 6ba27860a449b..95f42a94b6c7a 100644 --- a/shell/platform/embedder/embedder_external_view.h +++ b/shell/platform/embedder/embedder_external_view.h @@ -82,12 +82,14 @@ class EmbedderExternalView { ViewIdentifier::Equal>; EmbedderExternalView(const SkISize& frame_size, - const SkMatrix& surface_transformation); + const SkMatrix& surface_transformation, + bool enable_impeller); EmbedderExternalView(const SkISize& frame_size, const SkMatrix& surface_transformation, ViewIdentifier view_identifier, - std::unique_ptr params); + std::unique_ptr params, + bool enable_impeller); ~EmbedderExternalView(); @@ -120,7 +122,7 @@ class EmbedderExternalView { const SkMatrix surface_transformation_; ViewIdentifier view_identifier_; std::unique_ptr embedded_view_params_; - std::unique_ptr slice_; + std::unique_ptr slice_; std::optional has_engine_rendered_contents_; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalView); diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index ca38e86af4826..2f1828773100a 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -16,8 +16,10 @@ namespace flutter { EmbedderExternalViewEmbedder::EmbedderExternalViewEmbedder( bool avoid_backing_store_cache, const CreateRenderTargetCallback& create_render_target_callback, - const PresentCallback& present_callback) - : avoid_backing_store_cache_(avoid_backing_store_cache), + const PresentCallback& present_callback, + bool enable_impeller) + : enable_impeller_(enable_impeller), + avoid_backing_store_cache_(avoid_backing_store_cache), create_render_target_callback_(create_render_target_callback), present_callback_(present_callback) { FML_DCHECK(create_render_target_callback_); @@ -65,7 +67,7 @@ void EmbedderExternalViewEmbedder::BeginFrame( EmbedderExternalView::ViewIdentifier{}; pending_views_[kRootViewIdentifier] = std::make_unique( - pending_frame_size_, pending_surface_transformation_); + pending_frame_size_, pending_surface_transformation_, enable_impeller_); composition_order_.push_back(kRootViewIdentifier); } @@ -80,7 +82,8 @@ void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView( pending_frame_size_, // frame size pending_surface_transformation_, // surface xformation vid, // view identifier - std::move(params) // embedded view params + std::move(params), // embedded view params + enable_impeller_ // enable_impeller ); composition_order_.push_back(vid); } diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 014f7393c416e..6446fa085cefc 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -58,7 +58,8 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { EmbedderExternalViewEmbedder( bool avoid_backing_store_cache, const CreateRenderTargetCallback& create_render_target_callback, - const PresentCallback& present_callback); + const PresentCallback& present_callback, + bool enable_impeller); //---------------------------------------------------------------------------- /// @brief Collects the external view embedder. @@ -104,6 +105,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { DlCanvas* GetRootCanvas() override; private: + bool enable_impeller_ = false; const bool avoid_backing_store_cache_; const CreateRenderTargetCallback create_render_target_callback_; const PresentCallback present_callback_; diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index f54a90c192d19..7b6102306355d 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -791,7 +791,7 @@ void DisplayListStreamDispatcher::drawVertices(const DlVertices* vertices, out_array("indices", vertices->index_count(), vertices->indices()) << "), " << mode << ");" << std::endl; } -void DisplayListStreamDispatcher::drawImage(const sk_sp image, +void DisplayListStreamDispatcher::drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { @@ -801,7 +801,7 @@ void DisplayListStreamDispatcher::drawImage(const sk_sp image, << "with attributes: " << render_with_attributes << ");" << std::endl; } -void DisplayListStreamDispatcher::drawImageRect(const sk_sp image, +void DisplayListStreamDispatcher::drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -815,7 +815,7 @@ void DisplayListStreamDispatcher::drawImageRect(const sk_sp image, << constraint << ");" << std::endl; } -void DisplayListStreamDispatcher::drawImageNine(const sk_sp image, +void DisplayListStreamDispatcher::drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -827,7 +827,7 @@ void DisplayListStreamDispatcher::drawImageNine(const sk_sp image, << "with attributes: " << render_with_attributes << ");" << std::endl; } -void DisplayListStreamDispatcher::drawAtlas(const sk_sp atlas, +void DisplayListStreamDispatcher::drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -845,14 +845,14 @@ void DisplayListStreamDispatcher::drawAtlas(const sk_sp atlas, << ");" << std::endl; } void DisplayListStreamDispatcher::drawDisplayList( - const sk_sp display_list, SkScalar opacity) { + const sk_sp& display_list, SkScalar opacity) { startl() << "drawDisplayList(" << "ID: " << display_list->unique_id() << ", " << "bounds: " << display_list->bounds() << ", " << "opacity: " << opacity << ");" << std::endl; } -void DisplayListStreamDispatcher::drawTextBlob(const sk_sp blob, +void DisplayListStreamDispatcher::drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) { startl() << "drawTextBlob(" diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index 4f3830a204b44..3fde199fa271e 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -115,22 +115,22 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp image, + void drawImage(const sk_sp& image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageRect(const sk_sp image, + void drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint) override; - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -139,9 +139,9 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list, + void drawDisplayList(const sk_sp& display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp blob, + void drawTextBlob(const sk_sp& blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc index 75b03107e2a75..bd9d321fc2500 100644 --- a/testing/mock_canvas.cc +++ b/testing/mock_canvas.cc @@ -193,6 +193,14 @@ void MockCanvas::DrawImage(const sk_sp& image, } } +void MockCanvas::DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity) { + draw_calls_.emplace_back( + DrawCall{.layer = current_layer_, + .data = DrawImpellerPictureData{picture, opacity}}); +} + void MockCanvas::DrawDisplayList(const sk_sp display_list, SkScalar opacity) { draw_calls_.emplace_back( @@ -462,6 +470,16 @@ std::ostream& operator<<(std::ostream& os, << data.options; } +bool operator==(const MockCanvas::DrawImpellerPictureData& a, + const MockCanvas::DrawImpellerPictureData& b) { + return a.picture == b.picture && a.opacity == b.opacity; +} + +std::ostream& operator<<(std::ostream& os, + const MockCanvas::DrawImpellerPictureData& data) { + return os << "[Impeller picture] " << data.opacity; +} + bool operator==(const MockCanvas::DrawDisplayListData& a, const MockCanvas::DrawDisplayListData& b) { return a.display_list->Equals(b.display_list) && a.opacity == b.opacity; diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index 71260d5b8755f..b24918d10fa55 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -27,7 +27,7 @@ namespace flutter { namespace testing { -static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); +[[maybe_unused]] static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); // Mock |SkCanvas|, useful for writing tests that use Skia but do not interact // with the GPU. @@ -95,6 +95,11 @@ class MockCanvas final : public DlCanvas { DlPaint paint; }; + struct DrawImpellerPictureData { + std::shared_ptr picture; + SkScalar opacity; + }; + struct DrawDisplayListData { sk_sp display_list; SkScalar opacity; @@ -142,6 +147,7 @@ class MockCanvas final : public DlCanvas { DrawTextData, DrawImageDataNoPaint, DrawImageData, + DrawImpellerPictureData, DrawDisplayListData, DrawShadowData, ClipRectData, @@ -266,7 +272,9 @@ class MockCanvas final : public DlCanvas { DlImageSampling sampling, const SkRect* cullRect, const DlPaint* paint = nullptr) override; - + void DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity = SK_Scalar1) override; void DrawDisplayList(const sk_sp display_list, SkScalar opacity) override; void DrawTextBlob(const sk_sp& blob, @@ -327,6 +335,11 @@ extern bool operator==(const MockCanvas::DrawImageDataNoPaint& a, const MockCanvas::DrawImageDataNoPaint& b); extern std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawImageDataNoPaint& data); +extern std::ostream& operator<<( + std::ostream& os, + const MockCanvas::DrawImpellerPictureData& data); +extern bool operator==(const MockCanvas::DrawImpellerPictureData& a, + const MockCanvas::DrawImpellerPictureData& b); extern bool operator==(const MockCanvas::DrawDisplayListData& a, const MockCanvas::DrawDisplayListData& b); extern std::ostream& operator<<(std::ostream& os, diff --git a/third_party/txt/src/skia/paragraph_skia.cc b/third_party/txt/src/skia/paragraph_skia.cc index 76d737224aa79..23a0c85bf6e72 100644 --- a/third_party/txt/src/skia/paragraph_skia.cc +++ b/third_party/txt/src/skia/paragraph_skia.cc @@ -63,10 +63,10 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { /// See https://github.com/flutter/flutter/issues/126673. It /// probably makes sense to eventually make this a compile-time /// decision (i.e. with `#ifdef`) instead of a runtime option. - DisplayListParagraphPainter(DisplayListBuilder* builder, + DisplayListParagraphPainter(DlCanvas* canvas, const std::vector& dl_paints, bool draw_path_effect) - : builder_(builder), + : canvas_(canvas), dl_paints_(dl_paints), draw_path_effect_(draw_path_effect) {} @@ -79,7 +79,7 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { } size_t paint_id = std::get(paint); FML_DCHECK(paint_id < dl_paints_.size()); - builder_->DrawTextBlob(blob, x, y, dl_paints_[paint_id]); + canvas_->DrawTextBlob(blob, x, y, dl_paints_[paint_id]); } void drawTextShadow(const sk_sp& blob, @@ -96,24 +96,24 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { DlBlurMaskFilter filter(DlBlurStyle::kNormal, blur_sigma, false); paint.setMaskFilter(&filter); } - builder_->DrawTextBlob(blob, x, y, paint); + canvas_->DrawTextBlob(blob, x, y, paint); } void drawRect(const SkRect& rect, const SkPaintOrID& paint) override { size_t paint_id = std::get(paint); FML_DCHECK(paint_id < dl_paints_.size()); - builder_->DrawRect(rect, dl_paints_[paint_id]); + canvas_->DrawRect(rect, dl_paints_[paint_id]); } void drawFilledRect(const SkRect& rect, const DecorationStyle& decor_style) override { DlPaint paint = toDlPaint(decor_style, DlDrawStyle::kFill); - builder_->DrawRect(rect, paint); + canvas_->DrawRect(rect, paint); } void drawPath(const SkPath& path, const DecorationStyle& decor_style) override { - builder_->DrawPath(path, toDlPaint(decor_style)); + canvas_->DrawPath(path, toDlPaint(decor_style)); } void drawLine(SkScalar x0, @@ -124,14 +124,14 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { // We only support horizontal lines. FML_DCHECK(y0 == y1); - // This function is called for both solid and dashed lines. If we're drawing - // a dashed line, and we're using the Impeller backend, then we need to draw - // the line directly using the `drawLine` API instead of using a path effect - // (because Impeller does not support path effects). + // This function is called for both solid and dashed lines. If we're + // drawing a dashed line, and we're using the Impeller backend, then we + // need to draw the line directly using the `drawLine` API instead of + // using a path effect (because Impeller does not support path effects). auto dash_path_effect = decor_style.getDashPathEffect(); if (draw_path_effect_ && dash_path_effect) { auto path = dashedLine(x0, x1, y0, *dash_path_effect); - builder_->DrawPath(path, toDlPaint(decor_style)); + canvas_->DrawPath(path, toDlPaint(decor_style)); return; } @@ -139,20 +139,20 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { if (dash_path_effect) { setPathEffect(paint, *dash_path_effect); } - builder_->DrawLine(SkPoint::Make(x0, y0), SkPoint::Make(x1, y1), paint); + canvas_->DrawLine(SkPoint::Make(x0, y0), SkPoint::Make(x1, y1), paint); } void clipRect(const SkRect& rect) override { - builder_->ClipRect(rect, DlCanvas::ClipOp::kIntersect, false); + canvas_->ClipRect(rect, DlCanvas::ClipOp::kIntersect, false); } void translate(SkScalar dx, SkScalar dy) override { - builder_->Translate(dx, dy); + canvas_->Translate(dx, dy); } - void save() override { builder_->Save(); } + void save() override { canvas_->Save(); } - void restore() override { builder_->Restore(); } + void restore() override { canvas_->Restore(); } private: SkPath dashedLine(SkScalar x0, @@ -200,7 +200,7 @@ class DisplayListParagraphPainter : public skt::ParagraphPainter { paint.setPathEffect(effect); } - DisplayListBuilder* builder_; + DlCanvas* canvas_; const std::vector& dl_paints_; bool draw_path_effect_; }; @@ -291,7 +291,7 @@ void ParagraphSkia::Layout(double width) { paragraph_->layout(width); } -bool ParagraphSkia::Paint(DisplayListBuilder* builder, double x, double y) { +bool ParagraphSkia::Paint(DlCanvas* builder, double x, double y) { DisplayListParagraphPainter painter(builder, dl_paints_, impeller_enabled_); paragraph_->paint(&painter, x, y); return true; diff --git a/third_party/txt/src/skia/paragraph_skia.h b/third_party/txt/src/skia/paragraph_skia.h index 5779445e09a71..6126ac605be6d 100644 --- a/third_party/txt/src/skia/paragraph_skia.h +++ b/third_party/txt/src/skia/paragraph_skia.h @@ -54,7 +54,7 @@ class ParagraphSkia : public Paragraph { void Layout(double width) override; - bool Paint(flutter::DisplayListBuilder* builder, double x, double y) override; + bool Paint(flutter::DlCanvas* canvas, double x, double y) override; std::vector GetRectsForRange( size_t start, diff --git a/third_party/txt/src/txt/paragraph.h b/third_party/txt/src/txt/paragraph.h index 2d736fb3935c5..5c4e374ddc585 100644 --- a/third_party/txt/src/txt/paragraph.h +++ b/third_party/txt/src/txt/paragraph.h @@ -18,7 +18,7 @@ #ifndef LIB_TXT_SRC_PARAGRAPH_H_ #define LIB_TXT_SRC_PARAGRAPH_H_ -#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/dl_canvas.h" #include "line_metrics.h" #include "paragraph_style.h" #include "third_party/skia/include/core/SkRect.h" @@ -143,11 +143,9 @@ class Paragraph { // before Painting and getting any statistics from this class. virtual void Layout(double width) = 0; - // Paints the laid out text onto the supplied DisplayListBuilder at + // Paints the laid out text onto the supplied DlCanvas at // (x, y) offset from the origin. Only valid after Layout() is called. - virtual bool Paint(flutter::DisplayListBuilder* builder, - double x, - double y) = 0; + virtual bool Paint(flutter::DlCanvas* canvas, double x, double y) = 0; // Returns a vector of bounding boxes that enclose all text between start and // end glyph indexes, including start and excluding end. diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index ec06ce702512e..9e13492bcc1fc 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include "display_list/display_list_builder.h" #include "display_list/utils/dl_receiver_utils.h" #include "gtest/gtest.h" #include "runtime/test_font_data.h" From 0154a5a939695d2c078a2f76e9ab31dca93e7ce3 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 1 Sep 2023 10:23:53 -0700 Subject: [PATCH 2/3] [Impeller] Reland DlAiksCanvas again --- ci/licenses_golden/licenses_flutter | 16 +- display_list/BUILD.gn | 8 +- display_list/benchmarking/dl_benchmarks.cc | 2 +- display_list/benchmarking/dl_complexity_gl.cc | 8 +- display_list/benchmarking/dl_complexity_gl.h | 8 +- .../benchmarking/dl_complexity_helper.h | 4 +- .../benchmarking/dl_complexity_metal.cc | 8 +- .../benchmarking/dl_complexity_metal.h | 8 +- .../benchmarking/dl_complexity_unittests.cc | 4 +- display_list/display_list.cc | 87 +- display_list/display_list.h | 68 +- display_list/display_list_builder.cc | 34 - display_list/display_list_builder.h | 59 - display_list/display_list_unittests.cc | 1517 ++++++++------- display_list/dl_builder.cc | 1625 +++++++++++++++++ display_list/dl_builder.h | 788 ++++++++ display_list/dl_canvas.h | 2 +- display_list/dl_canvas_to_receiver.cc | 1158 ------------ display_list/dl_canvas_to_receiver.h | 573 ------ display_list/dl_op_flags.h | 35 +- display_list/dl_op_receiver.h | 20 +- display_list/dl_op_recorder.cc | 691 ------- display_list/dl_op_recorder.h | 210 --- display_list/dl_op_records.h | 92 +- display_list/dl_op_spy.cc | 12 +- display_list/dl_op_spy.h | 12 +- display_list/dl_op_spy_unittests.cc | 2 +- display_list/dl_paint.h | 4 +- display_list/dl_vertices.h | 2 +- display_list/dl_vertices_unittests.cc | 2 +- display_list/effects/dl_color_source.h | 8 +- display_list/effects/dl_path_effect.h | 2 +- display_list/skia/dl_sk_dispatcher.cc | 12 +- display_list/skia/dl_sk_dispatcher.h | 12 +- .../testing/dl_rendering_unittests.cc | 2 +- display_list/testing/dl_test_snippets.cc | 52 +- display_list/testing/dl_test_snippets.h | 2 +- display_list/utils/dl_bounds_accumulator.h | 23 +- display_list/utils/dl_matrix_clip_tracker.cc | 2 +- display_list/utils/dl_receiver_utils.h | 12 +- flow/embedded_views.h | 2 +- .../checkerboard_layertree_unittests.cc | 15 +- flow/layers/display_list_layer.cc | 1 + flow/layers/display_list_layer_unittests.cc | 2 +- flow/layers/image_filter_layer_unittests.cc | 2 - flow/raster_cache_unittests.cc | 2 +- flow/surface_frame.h | 2 +- flow/testing/diff_context_test.cc | 3 +- flow/testing/layer_test.h | 52 +- flow/testing/mock_texture_unittests.cc | 2 +- impeller/display_list/dl_dispatcher.cc | 12 +- impeller/display_list/dl_dispatcher.h | 12 +- impeller/display_list/dl_playground.h | 2 +- impeller/display_list/dl_unittests.cc | 2 +- lib/ui/painting/paint.cc | 1 + lib/ui/painting/picture_recorder.h | 2 +- testing/display_list_testing.cc | 12 +- testing/display_list_testing.h | 12 +- testing/mock_canvas.h | 2 +- third_party/txt/tests/paragraph_unittests.cc | 2 +- 60 files changed, 3569 insertions(+), 3759 deletions(-) delete mode 100644 display_list/display_list_builder.cc delete mode 100644 display_list/display_list_builder.h create mode 100644 display_list/dl_builder.cc create mode 100644 display_list/dl_builder.h delete mode 100644 display_list/dl_canvas_to_receiver.cc delete mode 100644 display_list/dl_canvas_to_receiver.h delete mode 100644 display_list/dl_op_recorder.cc delete mode 100644 display_list/dl_op_recorder.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 89f591e6cc19e..8a942913c45b7 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -717,22 +717,18 @@ ORIGIN: ../../../flutter/display_list/benchmarking/dl_complexity_metal.h + ../.. ORIGIN: ../../../flutter/display_list/benchmarking/dl_region_benchmarks.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/display_list.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/display_list.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/display_list_builder.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/display_list_builder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_attributes.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_blend_mode.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_blend_mode.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_builder.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/display_list/dl_builder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_canvas.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_canvas.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/dl_canvas_to_receiver.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/dl_canvas_to_receiver.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_color.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_flags.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_flags.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_receiver.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_receiver.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/dl_op_recorder.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/dl_op_recorder.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_records.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_records.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/display_list/dl_op_spy.cc + ../../../flutter/LICENSE @@ -3471,22 +3467,18 @@ FILE: ../../../flutter/display_list/benchmarking/dl_complexity_metal.h FILE: ../../../flutter/display_list/benchmarking/dl_region_benchmarks.cc FILE: ../../../flutter/display_list/display_list.cc FILE: ../../../flutter/display_list/display_list.h -FILE: ../../../flutter/display_list/display_list_builder.cc -FILE: ../../../flutter/display_list/display_list_builder.h FILE: ../../../flutter/display_list/dl_attributes.h FILE: ../../../flutter/display_list/dl_blend_mode.cc FILE: ../../../flutter/display_list/dl_blend_mode.h +FILE: ../../../flutter/display_list/dl_builder.cc +FILE: ../../../flutter/display_list/dl_builder.h FILE: ../../../flutter/display_list/dl_canvas.cc FILE: ../../../flutter/display_list/dl_canvas.h -FILE: ../../../flutter/display_list/dl_canvas_to_receiver.cc -FILE: ../../../flutter/display_list/dl_canvas_to_receiver.h FILE: ../../../flutter/display_list/dl_color.h FILE: ../../../flutter/display_list/dl_op_flags.cc FILE: ../../../flutter/display_list/dl_op_flags.h FILE: ../../../flutter/display_list/dl_op_receiver.cc FILE: ../../../flutter/display_list/dl_op_receiver.h -FILE: ../../../flutter/display_list/dl_op_recorder.cc -FILE: ../../../flutter/display_list/dl_op_recorder.h FILE: ../../../flutter/display_list/dl_op_records.cc FILE: ../../../flutter/display_list/dl_op_records.h FILE: ../../../flutter/display_list/dl_op_spy.cc diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index d4b21754a8fb0..07067e3150647 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -25,22 +25,18 @@ source_set("display_list") { "benchmarking/dl_complexity_metal.h", "display_list.cc", "display_list.h", - "display_list_builder.cc", - "display_list_builder.h", "dl_attributes.h", "dl_blend_mode.cc", "dl_blend_mode.h", + "dl_builder.cc", + "dl_builder.h", "dl_canvas.cc", "dl_canvas.h", - "dl_canvas_to_receiver.cc", - "dl_canvas_to_receiver.h", "dl_color.h", "dl_op_flags.cc", "dl_op_flags.h", "dl_op_receiver.cc", "dl_op_receiver.h", - "dl_op_recorder.cc", - "dl_op_recorder.h", "dl_op_records.cc", "dl_op_records.h", "dl_op_spy.cc", diff --git a/display_list/benchmarking/dl_benchmarks.cc b/display_list/benchmarking/dl_benchmarks.cc index 07458b5a96474..b2dcf78a2c3bc 100644 --- a/display_list/benchmarking/dl_benchmarks.cc +++ b/display_list/benchmarking/dl_benchmarks.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/display_list/benchmarking/dl_benchmarks.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_op_flags.h" #include "flutter/display_list/skia/dl_sk_canvas.h" diff --git a/display_list/benchmarking/dl_complexity_gl.cc b/display_list/benchmarking/dl_complexity_gl.cc index c3738d0dc5cd5..f8dd1c52a1260 100644 --- a/display_list/benchmarking/dl_complexity_gl.cc +++ b/display_list/benchmarking/dl_complexity_gl.cc @@ -503,7 +503,7 @@ void DisplayListGLComplexityCalculator::GLHelper::drawVertices( } void DisplayListGLComplexityCalculator::GLHelper::drawImage( - const sk_sp& image, + const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { @@ -583,7 +583,7 @@ void DisplayListGLComplexityCalculator::GLHelper::ImageRect( } void DisplayListGLComplexityCalculator::GLHelper::drawImageNine( - const sk_sp& image, + const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -608,7 +608,7 @@ void DisplayListGLComplexityCalculator::GLHelper::drawImageNine( } void DisplayListGLComplexityCalculator::GLHelper::drawDisplayList( - const sk_sp& display_list, + const sk_sp display_list, SkScalar opacity) { if (IsComplex()) { return; @@ -622,7 +622,7 @@ void DisplayListGLComplexityCalculator::GLHelper::drawDisplayList( } void DisplayListGLComplexityCalculator::GLHelper::drawTextBlob( - const sk_sp& blob, + const sk_sp blob, SkScalar x, SkScalar y) { if (IsComplex()) { diff --git a/display_list/benchmarking/dl_complexity_gl.h b/display_list/benchmarking/dl_complexity_gl.h index f08357c0d79a7..9fc7596687051 100644 --- a/display_list/benchmarking/dl_complexity_gl.h +++ b/display_list/benchmarking/dl_complexity_gl.h @@ -56,18 +56,18 @@ class DisplayListGLComplexityCalculator uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/benchmarking/dl_complexity_helper.h b/display_list/benchmarking/dl_complexity_helper.h index 464b4675caeeb..759d6e64460ed 100644 --- a/display_list/benchmarking/dl_complexity_helper.h +++ b/display_list/benchmarking/dl_complexity_helper.h @@ -146,7 +146,7 @@ class ComplexityCalculatorHelper } void drawImageRect( - const sk_sp& image, + const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -159,7 +159,7 @@ class ComplexityCalculatorHelper render_with_attributes, constraint == SrcRectConstraint::kStrict); } - void drawAtlas(const sk_sp& atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], diff --git a/display_list/benchmarking/dl_complexity_metal.cc b/display_list/benchmarking/dl_complexity_metal.cc index dcf6e9fcdf870..56d4f3b406ad9 100644 --- a/display_list/benchmarking/dl_complexity_metal.cc +++ b/display_list/benchmarking/dl_complexity_metal.cc @@ -455,7 +455,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawVertices( } void DisplayListMetalComplexityCalculator::MetalHelper::drawImage( - const sk_sp& image, + const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { @@ -532,7 +532,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::ImageRect( } void DisplayListMetalComplexityCalculator::MetalHelper::drawImageNine( - const sk_sp& image, + const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -552,7 +552,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawImageNine( } void DisplayListMetalComplexityCalculator::MetalHelper::drawDisplayList( - const sk_sp& display_list, + const sk_sp display_list, SkScalar opacity) { if (IsComplex()) { return; @@ -566,7 +566,7 @@ void DisplayListMetalComplexityCalculator::MetalHelper::drawDisplayList( } void DisplayListMetalComplexityCalculator::MetalHelper::drawTextBlob( - const sk_sp& blob, + const sk_sp blob, SkScalar x, SkScalar y) { if (IsComplex()) { diff --git a/display_list/benchmarking/dl_complexity_metal.h b/display_list/benchmarking/dl_complexity_metal.h index 16d58aa2b4bff..aa63863fa4d05 100644 --- a/display_list/benchmarking/dl_complexity_metal.h +++ b/display_list/benchmarking/dl_complexity_metal.h @@ -56,18 +56,18 @@ class DisplayListMetalComplexityCalculator uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/benchmarking/dl_complexity_unittests.cc b/display_list/benchmarking/dl_complexity_unittests.cc index d349d1ce5afe6..eee77ac9bdf55 100644 --- a/display_list/benchmarking/dl_complexity_unittests.cc +++ b/display_list/benchmarking/dl_complexity_unittests.cc @@ -6,7 +6,7 @@ #include "flutter/display_list/benchmarking/dl_complexity_gl.h" #include "flutter/display_list/benchmarking/dl_complexity_metal.h" #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/testing/dl_test_snippets.h" #include "flutter/testing/testing.h" @@ -102,7 +102,7 @@ TEST(DisplayListComplexity, StrokeWidth) { auto display_list_stroke_0 = builder_stroke_0.Build(); DisplayListBuilder builder_stroke_1; - builder_stroke_1.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), + builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), DlPaint().setStrokeWidth(1.0f)); auto display_list_stroke_1 = builder_stroke_1.Build(); diff --git a/display_list/display_list.cc b/display_list/display_list.cc index cee978dd56536..73f593d135b18 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -15,7 +15,7 @@ const SaveLayerOptions SaveLayerOptions::kWithAttributes = kNoAttributes.with_renders_with_attributes(); DisplayList::DisplayList() - : storage_(), + : byte_count_(0), op_count_(0), nested_byte_count_(0), nested_op_count_(0), @@ -25,7 +25,8 @@ DisplayList::DisplayList() is_ui_thread_safe_(true), modifies_transparent_black_(false) {} -DisplayList::DisplayList(DlStorage&& storage, +DisplayList::DisplayList(DisplayListStorage&& storage, + size_t byte_count, unsigned int op_count, size_t nested_byte_count, unsigned int nested_op_count, @@ -35,6 +36,7 @@ DisplayList::DisplayList(DlStorage&& storage, bool modifies_transparent_black, std::shared_ptr rtree) : storage_(std::move(storage)), + byte_count_(byte_count), op_count_(op_count), nested_byte_count_(nested_byte_count), nested_op_count_(nested_op_count), @@ -45,65 +47,9 @@ DisplayList::DisplayList(DlStorage&& storage, modifies_transparent_black_(modifies_transparent_black), rtree_(std::move(rtree)) {} -DisplayList::DlStorage::DlStorage(DisplayList::DlStorage&& source) - : ptr_(std::move(source.ptr_)), - used_(source.used_), - allocated_(source.allocated_) { - FML_DCHECK(source.ptr_.get() == nullptr); - FML_DCHECK(!source.disabled_); - source.ptr_ = nullptr; - source.used_ = 0u; - source.allocated_ = 0u; - source.disabled_ = true; -} - -DisplayList::DlStorage::~DlStorage() { - uint8_t* ptr = get(); - if (ptr != nullptr) { - FML_DCHECK(used_ <= allocated_); - DisposeOps(ptr, ptr + used_); - } else { - FML_DCHECK(used_ == 0u); - FML_DCHECK(allocated_ == 0u); - } -} - -static constexpr inline bool is_power_of_two(int value) { - return (value & (value - 1)) == 0; -} - -uint8_t* DisplayList::DlStorage::alloc(size_t bytes) { - if (disabled_) { - FML_DCHECK(is_valid()); - return nullptr; - } - FML_DCHECK(bytes < (1 << 24)); - if (used_ + bytes > allocated_) { - static_assert(is_power_of_two(kPageSize), - "This math needs updating for non-pow2."); - // Next greater multiple of DL_BUILDER_PAGE. - allocated_ = (used_ + bytes + kPageSize) & ~(kPageSize - 1); - realloc(allocated_); - FML_DCHECK(get()); - memset(get() + used_, 0, allocated_ - used_); - } - FML_DCHECK(used_ + bytes <= allocated_); - uint8_t* ret = get() + used_; - used_ += bytes; - return ret; -} - -void DisplayList::DlStorage::realloc(size_t count) { - FML_DCHECK(is_valid()); - FML_DCHECK(count >= used_); - ptr_.reset(static_cast(std::realloc(ptr_.release(), count))); - allocated_ = count; - FML_CHECK(ptr_); -} - -DisplayList::DlStorage DisplayList::DlStorage::take() { - realloc(used_); - return std::move(*this); +DisplayList::~DisplayList() { + uint8_t* ptr = storage_.get(); + DisposeOps(ptr, ptr + byte_count_); } uint32_t DisplayList::next_unique_id() { @@ -166,10 +112,10 @@ class VectorCuller final : public Culler { } } void update(DispatchContext& context) override { - if (context.cur_render_index > context.next_render_index) { + if (++context.cur_index > context.next_render_index) { while (cur_ < end_) { context.next_render_index = rtree_->id(*cur_++); - if (context.next_render_index >= context.cur_render_index) { + if (context.next_render_index >= context.cur_index) { // It should be rare that we have duplicate indices // but if we do, then having a while loop is a cheap // insurance for those cases. @@ -193,7 +139,8 @@ class VectorCuller final : public Culler { }; void DisplayList::Dispatch(DlOpReceiver& receiver) const { - Dispatch(receiver, storage_.get(), storage_.end(), NopCuller::instance); + uint8_t* ptr = storage_.get(); + Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance); } void DisplayList::Dispatch(DlOpReceiver& receiver, @@ -217,10 +164,11 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, Dispatch(receiver); return; } + uint8_t* ptr = storage_.get(); std::vector rect_indices; rtree->search(cull_rect, &rect_indices); VectorCuller culler(rtree, rect_indices); - Dispatch(receiver, storage_.get(), storage_.end(), culler); + Dispatch(receiver, ptr, ptr + byte_count_, culler); } void DisplayList::Dispatch(DlOpReceiver& receiver, @@ -229,7 +177,7 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, Culler& culler) const { DispatchContext context = { .receiver = receiver, - .cur_render_index = 0, + .cur_index = 0, // next_render_index will be initialized by culler.init() .next_restore_index = std::numeric_limits::max(), }; @@ -261,7 +209,7 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, } } -void DisplayList::DlStorage::DisposeOps(uint8_t* ptr, uint8_t* end) { +void DisplayList::DisposeOps(uint8_t* ptr, uint8_t* end) { while (ptr < end) { auto op = reinterpret_cast(ptr); ptr += op->size; @@ -361,8 +309,7 @@ bool DisplayList::Equals(const DisplayList* other) const { if (this == other) { return true; } - if (storage_.used() != other->storage_.used() || - op_count_ != other->op_count_) { + if (byte_count_ != other->byte_count_ || op_count_ != other->op_count_) { return false; } uint8_t* ptr = storage_.get(); @@ -370,7 +317,7 @@ bool DisplayList::Equals(const DisplayList* other) const { if (ptr == o_ptr) { return true; } - return CompareOps(ptr, storage_.end(), o_ptr, other->storage_.end()); + return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other->byte_count_); } } // namespace flutter diff --git a/display_list/display_list.h b/display_list/display_list.h index b5fdc8c5c8a26..5b6c2e321cd0a 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -150,6 +150,7 @@ enum class DisplayListOpType { #undef DL_OP_TO_ENUM_VALUE class DlOpReceiver; +class DisplayListBuilder; class SaveLayerOptions { public: @@ -201,6 +202,26 @@ class SaveLayerOptions { }; }; +// Manages a buffer allocated with malloc. +class DisplayListStorage { + public: + DisplayListStorage() = default; + DisplayListStorage(DisplayListStorage&&) = default; + + uint8_t* get() const { return ptr_.get(); } + + void realloc(size_t count) { + ptr_.reset(static_cast(std::realloc(ptr_.release(), count))); + FML_CHECK(ptr_); + } + + private: + struct FreeDeleter { + void operator()(uint8_t* p) { std::free(p); } + }; + std::unique_ptr ptr_; +}; + class Culler; // The base class that contains a sequence of rendering operations @@ -210,7 +231,7 @@ class DisplayList : public SkRefCnt { public: DisplayList(); - ~DisplayList() = default; + ~DisplayList(); void Dispatch(DlOpReceiver& ctx) const; void Dispatch(DlOpReceiver& ctx, const SkRect& cull_rect) const; @@ -220,8 +241,7 @@ class DisplayList : public SkRefCnt { // but nested ops are only included if requested. The defaults used // here for these accessors follow that pattern. size_t bytes(bool nested = true) const { - FML_DCHECK(storage_.used() == storage_.allocated()); - return sizeof(DisplayList) + storage_.allocated() + + return sizeof(DisplayList) + byte_count_ + (nested ? nested_byte_count_ : 0); } @@ -259,40 +279,8 @@ class DisplayList : public SkRefCnt { } private: - // Manages a buffer allocated with malloc. - class DlStorage { - public: - DlStorage() = default; - DlStorage(DlStorage&& source); - - ~DlStorage(); - - uint8_t* get() const { return ptr_.get(); } - uint8_t* end() const { return ptr_.get() + used_; } - size_t used() const { return used_; } - size_t allocated() const { return allocated_; } - bool is_valid() const { return !disabled_; } - - uint8_t* alloc(size_t bytes); - void realloc(size_t count); - DlStorage take(); - - private: - static constexpr size_t kPageSize = 4096u; - - static void DisposeOps(uint8_t* ptr, uint8_t* end); - - struct FreeDeleter { - void operator()(uint8_t* p) { std::free(p); } - }; - std::unique_ptr ptr_; - - bool disabled_ = false; - size_t used_ = 0u; - size_t allocated_ = 0u; - }; - - DisplayList(DlStorage&& ptr, + DisplayList(DisplayListStorage&& ptr, + size_t byte_count, unsigned int op_count, size_t nested_byte_count, unsigned int nested_op_count, @@ -304,7 +292,10 @@ class DisplayList : public SkRefCnt { static uint32_t next_unique_id(); - const DlStorage storage_; + static void DisposeOps(uint8_t* ptr, uint8_t* end); + + const DisplayListStorage storage_; + const size_t byte_count_; const unsigned int op_count_; const size_t nested_byte_count_; @@ -324,7 +315,6 @@ class DisplayList : public SkRefCnt { uint8_t* end, Culler& culler) const; - friend class DlOpRecorder; friend class DisplayListBuilder; }; diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc deleted file mode 100644 index 871f5ee016863..0000000000000 --- a/display_list/display_list_builder.cc +++ /dev/null @@ -1,34 +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. - -#include "flutter/display_list/display_list_builder.h" - -namespace flutter { - -DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, - bool prepare_rtree) - : DisplayListBuilder( - std::make_shared(cull_rect, prepare_rtree)) {} - -DisplayListBuilder::DisplayListBuilder( - const std::shared_ptr& recorder) - : DlCanvasToReceiver(recorder), recorder_(recorder) {} - -sk_sp DisplayListBuilder::Build() { - FML_CHECK(recorder_ != nullptr); - FML_CHECK(receiver_ != nullptr); - - RestoreToCount(1); - - sk_sp dl = - recorder_->Build(current_group_opacity_compatibility(), - current_affects_transparent_layer()); - - recorder_ = nullptr; - receiver_ = nullptr; - - return dl; -} - -} // namespace flutter diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h deleted file mode 100644 index 3fb50efb9b279..0000000000000 --- a/display_list/display_list_builder.h +++ /dev/null @@ -1,59 +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. - -#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ -#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ - -#include "flutter/display_list/dl_canvas.h" -#include "flutter/display_list/dl_canvas_to_receiver.h" -#include "flutter/display_list/dl_op_flags.h" -#include "flutter/display_list/dl_op_receiver.h" -#include "flutter/display_list/dl_op_recorder.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" -#include "flutter/display_list/utils/dl_matrix_clip_tracker.h" -#include "flutter/fml/macros.h" - -namespace flutter { - -// The primary class used to build a display list. The list of methods -// here matches the list of methods invoked on a |DlOpReceiver| combined -// with the list of methods invoked on a |DlCanvas|. -class DisplayListBuilder final : public DlCanvasToReceiver, // - public SkRefCnt { - public: - static constexpr SkRect kMaxCullRect = - SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); - - explicit DisplayListBuilder(bool prepare_rtree) - : DisplayListBuilder(kMaxCullRect, prepare_rtree) {} - - explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect, - bool prepare_rtree = false); - - ~DisplayListBuilder() = default; - - sk_sp Build(); - - private: - explicit DisplayListBuilder(const std::shared_ptr& recorder); - - std::shared_ptr recorder_; - - // This method exposes the internal stateful DlOpReceiver implementation - // of the DisplayListBuilder, primarily for testing purposes. Its use - // is obsolete and forbidden in every other case and is only shared to a - // pair of "friend" accessors in the benchmark/unittest files. - DlOpReceiver& asReceiver() { return *receiver_; } - - friend DlOpReceiver& DisplayListBuilderBenchmarkAccessor( - DisplayListBuilder& builder); - friend DlOpReceiver& DisplayListBuilderTestingAccessor( - DisplayListBuilder& builder); - friend DlPaint DisplayListBuilderTestingAttributes( - DisplayListBuilder& builder); -}; - -} // namespace flutter - -#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index e9df415abb2dd..db84b0b36bf06 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -9,8 +9,8 @@ #include #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_blend_mode.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_paint.h" #include "flutter/display_list/geometry/dl_rtree.h" #include "flutter/display_list/skia/dl_sk_dispatcher.h" @@ -141,9 +141,6 @@ class DisplayListTestBase : public BaseT { setup(builder); renderer(builder, paint, render_rect); auto dl = builder.Build(); - FML_LOG(ERROR) << "dl: " << *dl; - FML_LOG(ERROR) << "ops: " << dl->op_count(); - FML_LOG(ERROR) << "bytes: " << dl->bytes(); EXPECT_EQ(dl->op_count(), 1u) << desc; EXPECT_EQ(dl->bounds(), expected_bounds) << desc; } @@ -236,21 +233,23 @@ TEST_F(DisplayListTest, EmptyBuild) { EXPECT_EQ(dl->bytes(), sizeof(DisplayList)); } -#ifndef NDEBUG -TEST_F(DisplayListTest, SecondBuildDies) { +TEST_F(DisplayListTest, EmptyRebuild) { DisplayListBuilder builder; auto dl1 = builder.Build(); - EXPECT_DEATH_IF_SUPPORTED(builder.Build(), "recorder_ != nullptr"); + auto dl2 = builder.Build(); + auto dl3 = builder.Build(); + ASSERT_TRUE(dl1->Equals(dl2)); + ASSERT_TRUE(dl2->Equals(dl3)); } -TEST_F(DisplayListTest, BuilderCannotBeReused) { +TEST_F(DisplayListTest, BuilderCanBeReused) { DisplayListBuilder builder(kTestBounds); builder.DrawRect(kTestBounds, DlPaint()); auto dl = builder.Build(); - EXPECT_DEATH_IF_SUPPORTED(builder.DrawRect(kTestBounds, DlPaint()), - "receiver_ != nullptr"); + builder.DrawRect(kTestBounds, DlPaint()); + auto dl2 = builder.Build(); + ASSERT_TRUE(dl->Equals(dl2)); } -#endif TEST_F(DisplayListTest, SaveRestoreRestoresTransform) { SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); @@ -301,6 +300,47 @@ TEST_F(DisplayListTest, SaveRestoreRestoresTransform) { check_defaults(builder, cull_rect); } +TEST_F(DisplayListTest, BuildRestoresTransform) { + SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); + DisplayListBuilder builder(cull_rect); + + builder.Translate(10.0f, 10.0f); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.Scale(10.0f, 10.0f); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.Skew(0.1f, 0.1f); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.Rotate(45.0f); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.Transform(SkMatrix::Scale(10.0f, 10.0f)); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.Transform2DAffine(1.0f, 0.0f, 12.0f, // + 0.0f, 1.0f, 35.0f); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.Transform(SkM44(SkMatrix::Scale(10.0f, 10.0f))); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.TransformFullPerspective(1.0f, 0.0f, 0.0f, 12.0f, // + 0.0f, 1.0f, 0.0f, 35.0f, // + 0.0f, 0.0f, 1.0f, 5.0f, // + 0.0f, 0.0f, 0.0f, 1.0f); + builder.Build(); + check_defaults(builder, cull_rect); +} + TEST_F(DisplayListTest, SaveRestoreRestoresClip) { SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); DisplayListBuilder builder(cull_rect); @@ -321,6 +361,89 @@ TEST_F(DisplayListTest, SaveRestoreRestoresClip) { check_defaults(builder, cull_rect); } +TEST_F(DisplayListTest, BuildRestoresClip) { + SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); + DisplayListBuilder builder(cull_rect); + + builder.ClipRect({0.0f, 0.0f, 10.0f, 10.0f}); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.ClipRRect(SkRRect::MakeRectXY({0.0f, 0.0f, 5.0f, 5.0f}, 2.0f, 2.0f)); + builder.Build(); + check_defaults(builder, cull_rect); + + builder.ClipPath(SkPath().addOval({0.0f, 0.0f, 10.0f, 10.0f})); + builder.Build(); + check_defaults(builder, cull_rect); +} + +TEST_F(DisplayListTest, BuildRestoresAttributes) { + SkRect cull_rect = SkRect::MakeLTRB(-10.0f, -10.0f, 500.0f, 500.0f); + DisplayListBuilder builder(cull_rect); + DlOpReceiver& receiver = ToReceiver(builder); + + receiver.setAntiAlias(true); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setDither(true); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setInvertColors(true); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setColor(DlColor::kRed()); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setBlendMode(DlBlendMode::kColorBurn); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setDrawStyle(DlDrawStyle::kStrokeAndFill); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setStrokeWidth(300.0f); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setStrokeMiter(300.0f); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setStrokeCap(DlStrokeCap::kRound); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setStrokeJoin(DlStrokeJoin::kRound); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setColorSource(&kTestSource1); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setColorFilter(&kTestMatrixColorFilter1); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setImageFilter(&kTestBlurImageFilter1); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setMaskFilter(&kTestMaskFilter1); + builder.Build(); + check_defaults(builder, cull_rect); + + receiver.setPathEffect(kTestPathEffect1.get()); + builder.Build(); + check_defaults(builder, cull_rect); +} + TEST_F(DisplayListTest, BuilderBoundsTransformComparedToSkia) { const SkRect frame_rect = SkRect::MakeLTRB(10, 10, 100, 100); DisplayListBuilder builder(frame_rect); @@ -452,8 +575,8 @@ TEST_F(DisplayListTest, SingleOpSizes) { auto& invocation = group.variants[i]; sk_sp dl = Build(invocation); auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")"; - EXPECT_EQ(dl->op_count(false), invocation.op_count()) << desc; - EXPECT_EQ(dl->bytes(false), invocation.byte_count()) << desc; + ASSERT_EQ(dl->op_count(false), invocation.op_count()) << desc; + ASSERT_EQ(dl->bytes(false), invocation.byte_count()) << desc; } } } @@ -561,11 +684,23 @@ TEST_F(DisplayListTest, SingleOpDisplayListsAreEqualWithOrWithoutRtree) { TEST_F(DisplayListTest, FullRotationsAreNop) { DisplayListBuilder builder; - builder.Rotate(0); - builder.Rotate(360); - builder.Rotate(720); - builder.Rotate(1080); - builder.Rotate(1440); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.rotate(0); + receiver.rotate(360); + receiver.rotate(720); + receiver.rotate(1080); + receiver.rotate(1440); + sk_sp dl = builder.Build(); + ASSERT_EQ(dl->bytes(false), sizeof(DisplayList)); + ASSERT_EQ(dl->bytes(true), sizeof(DisplayList)); + ASSERT_EQ(dl->op_count(false), 0u); + ASSERT_EQ(dl->op_count(true), 0u); +} + +TEST_F(DisplayListTest, AllBlendModeNops) { + DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setBlendMode(DlBlendMode::kSrcOver); sk_sp dl = builder.Build(); ASSERT_EQ(dl->bytes(false), sizeof(DisplayList)); ASSERT_EQ(dl->bytes(true), sizeof(DisplayList)); @@ -634,10 +769,10 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // No tricky stuff, just verifying drawing a rect produces rect bounds DisplayListBuilder builder(build_bounds); - DlPaint save_paint; - builder.SaveLayer(&save_bounds, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), rect); } @@ -645,11 +780,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Now checking that a normal color filter still produces rect bounds DisplayListBuilder builder(build_bounds); - DlPaint save_paint; - save_paint.setColorFilter(&base_color_filter); - builder.SaveLayer(&save_bounds, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColorFilter(&base_color_filter); + receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); + receiver.setColorFilter(nullptr); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), rect); } @@ -677,11 +813,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // cull rect of the DisplayListBuilder when it encounters a // save layer that modifies an unbounded region DisplayListBuilder builder(build_bounds); - DlPaint save_paint; - save_paint.setColorFilter(&alpha_color_filter); - builder.SaveLayer(&save_bounds, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColorFilter(&alpha_color_filter); + receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); + receiver.setColorFilter(nullptr); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -690,11 +827,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // Verifying that the save layer bounds are not relevant // to the behavior in the previous example DisplayListBuilder builder(build_bounds); - DlPaint save_paint; - save_paint.setColorFilter(&alpha_color_filter); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColorFilter(&alpha_color_filter); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setColorFilter(nullptr); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -703,12 +841,13 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // Making sure hiding a ColorFilter as an ImageFilter will // generate the same behavior as setting it as a ColorFilter DisplayListBuilder builder(build_bounds); - DlPaint save_paint; + DlOpReceiver& receiver = ToReceiver(builder); DlColorFilterImageFilter color_filter_image_filter(base_color_filter); - save_paint.setImageFilter(&color_filter_image_filter); - builder.SaveLayer(&save_bounds, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + receiver.setImageFilter(&color_filter_image_filter); + receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); + receiver.setImageFilter(nullptr); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), rect); } @@ -717,12 +856,13 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { // Making sure hiding a problematic ColorFilter as an ImageFilter // will generate the same behavior as setting it as a ColorFilter DisplayListBuilder builder(build_bounds); - DlPaint save_paint; + DlOpReceiver& receiver = ToReceiver(builder); DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter); - save_paint.setImageFilter(&color_filter_image_filter); - builder.SaveLayer(&save_bounds, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + receiver.setImageFilter(&color_filter_image_filter); + receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); + receiver.setImageFilter(nullptr); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -730,12 +870,13 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Same as above (ImageFilter hiding ColorFilter) with no save bounds DisplayListBuilder builder(build_bounds); - DlPaint save_paint; + DlOpReceiver& receiver = ToReceiver(builder); DlColorFilterImageFilter color_filter_image_filter(alpha_color_filter); - save_paint.setImageFilter(&color_filter_image_filter); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + receiver.setImageFilter(&color_filter_image_filter); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setImageFilter(nullptr); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -743,11 +884,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Testing behavior with an unboundable blend mode DisplayListBuilder builder(build_bounds); - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kClear); - builder.SaveLayer(&save_bounds, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setBlendMode(DlBlendMode::kClear); + receiver.saveLayer(&save_bounds, SaveLayerOptions::kWithAttributes); + receiver.setBlendMode(DlBlendMode::kSrcOver); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -755,11 +897,12 @@ TEST_F(DisplayListTest, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Same as previous with no save bounds DisplayListBuilder builder(build_bounds); - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kClear); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect(rect, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setBlendMode(DlBlendMode::kClear); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setBlendMode(DlBlendMode::kSrcOver); + receiver.drawRect(rect); + receiver.restore(); sk_sp display_list = builder.Build(); ASSERT_EQ(display_list->bounds(), build_bounds); } @@ -786,14 +929,16 @@ TEST_F(DisplayListTest, NestedOpCountMetricsSameAsSkPicture) { ASSERT_EQ(picture->approximateOpCount(true), 36); DisplayListBuilder builder(SkRect::MakeWH(150, 100)); + DlOpReceiver& receiver = ToReceiver(builder); for (int y = 10; y <= 60; y += 10) { for (int x = 10; x <= 60; x += 10) { - DlColor color = ((x + y) % 20) == 10 ? DlColor::kRed() : DlColor::kBlue(); - builder.DrawRect(SkRect::MakeXYWH(x, y, 80, 80), DlPaint(color)); + receiver.setColor(((x + y) % 20) == 10 ? SK_ColorRED : SK_ColorBLUE); + receiver.drawRect(SkRect::MakeXYWH(x, y, 80, 80)); } } DisplayListBuilder outer_builder(SkRect::MakeWH(150, 100)); - outer_builder.DrawDisplayList(builder.Build()); + DlOpReceiver& outer_receiver = ToReceiver(outer_builder); + outer_receiver.drawDisplayList(builder.Build()); auto display_list = outer_builder.Build(); ASSERT_EQ(display_list->op_count(), 1u); @@ -818,8 +963,9 @@ TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) { { // First test == DisplayListBuilder builder; - // builder.transformFullPerspective takes row-major order - builder.TransformFullPerspective( + DlOpReceiver& receiver = ToReceiver(builder); + // receiver.transformFullPerspective takes row-major order + receiver.transformFullPerspective( // clang-format off 1, 2, 3, 4, 5, 6, 7, 8, @@ -840,8 +986,9 @@ TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) { } { // Next test != DisplayListBuilder builder; - // builder.transformFullPerspective takes row-major order - builder.TransformFullPerspective( + DlOpReceiver& receiver = ToReceiver(builder); + // receiver.transformFullPerspective takes row-major order + receiver.transformFullPerspective( // clang-format off 1, 5, 9, 13, 2, 6, 7, 11, @@ -864,8 +1011,9 @@ TEST_F(DisplayListTest, DisplayListFullPerspectiveTransformHandling) { TEST_F(DisplayListTest, DisplayListTransformResetHandling) { DisplayListBuilder builder; - builder.Scale(20.0, 20.0); - builder.TransformReset(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.scale(20.0, 20.0); + receiver.transformReset(); auto display_list = builder.Build(); ASSERT_NE(display_list, nullptr); sk_sp surface = @@ -880,14 +1028,14 @@ TEST_F(DisplayListTest, DisplayListTransformResetHandling) { TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { auto run_tests = [](const std::string& name, - void build(DlCanvas & cv, const DlPaint& paint), - bool expect_for_op, bool expect_with_kSrc) { + void build(DlOpReceiver & receiver), bool expect_for_op, + bool expect_with_kSrc) { { // First test is the draw op, by itself // (usually supports group opacity) DisplayListBuilder builder; - DlPaint paint; - build(builder, paint); + DlOpReceiver& receiver = ToReceiver(builder); + build(receiver); auto display_list = builder.Build(); EXPECT_EQ(display_list->can_apply_group_opacity(), expect_for_op) << "{" << std::endl @@ -895,12 +1043,12 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { << "}"; } { - // Second test is the draw op with kSrc, + // Second test i the draw op with kSrc, // (usually fails group opacity) DisplayListBuilder builder; - DlPaint paint; - paint.setBlendMode(DlBlendMode::kSrc); - build(builder, paint); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setBlendMode(DlBlendMode::kSrc); + build(receiver); auto display_list = builder.Build(); EXPECT_EQ(display_list->can_apply_group_opacity(), expect_with_kSrc) << "{" << std::endl @@ -912,64 +1060,67 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { #define RUN_TESTS(body) \ run_tests( \ - #body, [](DlCanvas& cv, const DlPaint& paint) { body }, true, false) + #body, [](DlOpReceiver& receiver) { body }, true, false) #define RUN_TESTS2(body, expect) \ run_tests( \ - #body, [](DlCanvas& cv, const DlPaint& paint) { body }, expect, expect) - - RUN_TESTS(cv.DrawPaint(paint);); - RUN_TESTS(cv.DrawColor(SK_ColorRED, paint.getBlendMode());); - RUN_TESTS2(cv.DrawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true); - RUN_TESTS2(cv.DrawColor(SK_ColorRED, DlBlendMode::kSrc);, false); - RUN_TESTS(cv.DrawLine({0, 0}, {10, 10}, paint);); - RUN_TESTS(cv.DrawRect({0, 0, 10, 10}, paint);); - RUN_TESTS(cv.DrawOval({0, 0, 10, 10}, paint);); - RUN_TESTS(cv.DrawCircle({10, 10}, 5, paint);); - RUN_TESTS(cv.DrawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), paint);); - RUN_TESTS(cv.DrawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), - SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2), paint);); - RUN_TESTS(cv.DrawPath( - SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15}), paint);); - RUN_TESTS(cv.DrawArc({0, 0, 10, 10}, 0, math::kPi, true, paint);); + #body, [](DlOpReceiver& receiver) { body }, expect, expect) + + RUN_TESTS(receiver.drawPaint();); + RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrcOver);, true); + RUN_TESTS2(receiver.drawColor(SK_ColorRED, DlBlendMode::kSrc);, false); + RUN_TESTS(receiver.drawLine({0, 0}, {10, 10});); + RUN_TESTS(receiver.drawRect({0, 0, 10, 10});); + RUN_TESTS(receiver.drawOval({0, 0, 10, 10});); + RUN_TESTS(receiver.drawCircle({10, 10}, 5);); + RUN_TESTS(receiver.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2));); + RUN_TESTS(receiver.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), + SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2));); + RUN_TESTS(receiver.drawPath( + SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15}));); + RUN_TESTS(receiver.drawArc({0, 0, 10, 10}, 0, math::kPi, true);); RUN_TESTS2( - cv.DrawPoints(PointMode::kPoints, TestPointCount, TestPoints, paint); + receiver.drawPoints(PointMode::kPoints, TestPointCount, TestPoints); , false); - RUN_TESTS2(cv.DrawVertices(TestVertices1.get(), DlBlendMode::kSrc, paint); + RUN_TESTS2(receiver.drawVertices(TestVertices1.get(), DlBlendMode::kSrc); , false); - RUN_TESTS(cv.DrawImage(TestImage1, {0, 0}, kLinearSampling, &paint);); - RUN_TESTS(cv.DrawImageRect(TestImage1, SkRect{10.0f, 10.0f, 20.0f, 20.0f}, - {0, 0, 10, 10}, kNearestSampling, &paint, - DlCanvas::SrcRectConstraint::kFast);); - RUN_TESTS2(cv.DrawImageRect(TestImage1, SkRect{10, 10, 20, 20}, - {0, 0, 10, 10}, kNearestSampling, nullptr, - DlCanvas::SrcRectConstraint::kFast); + RUN_TESTS(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, true);); + RUN_TESTS2(receiver.drawImage(TestImage1, {0, 0}, kLinearSampling, false); , true); - RUN_TESTS(cv.DrawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, - DlFilterMode::kLinear, &paint);); - RUN_TESTS2(cv.DrawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, - DlFilterMode::kLinear, nullptr); + RUN_TESTS(receiver.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10}, + kNearestSampling, true, + DlCanvas::SrcRectConstraint::kFast);); + RUN_TESTS2(receiver.drawImageRect(TestImage1, {10, 10, 20, 20}, + {0, 0, 10, 10}, kNearestSampling, false, + DlCanvas::SrcRectConstraint::kFast); , true); + RUN_TESTS(receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, + DlFilterMode::kLinear, true);); + RUN_TESTS2( + receiver.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, + DlFilterMode::kLinear, false); + , true); static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}}; static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}}; RUN_TESTS2( - cv.DrawAtlas(TestImage1, xforms, texs, nullptr, 2, DlBlendMode::kSrcIn, - kNearestSampling, nullptr, &paint); + receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2, + DlBlendMode::kSrcIn, kNearestSampling, nullptr, true); , false); RUN_TESTS2( - cv.DrawAtlas(TestImage1, xforms, texs, nullptr, 2, DlBlendMode::kSrcIn, - kNearestSampling, nullptr, nullptr); + receiver.drawAtlas(TestImage1, xforms, texs, nullptr, 2, + DlBlendMode::kSrcIn, kNearestSampling, nullptr, false); , false); EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity()); - RUN_TESTS2(cv.DrawDisplayList(TestDisplayList1);, true); + RUN_TESTS2(receiver.drawDisplayList(TestDisplayList1);, true); { static DisplayListBuilder builder; builder.DrawRect({0, 0, 10, 10}, DlPaint()); builder.DrawRect({5, 5, 15, 15}, DlPaint()); static auto display_list = builder.Build(); - RUN_TESTS2(cv.DrawDisplayList(display_list);, false); + RUN_TESTS2(receiver.drawDisplayList(display_list);, false); } - RUN_TESTS2(cv.DrawTextBlob(TestBlob1, 0, 0, paint);, false); - RUN_TESTS2(cv.DrawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0);, false); + RUN_TESTS2(receiver.drawTextBlob(TestBlob1, 0, 0);, false); + RUN_TESTS2(receiver.drawShadow(kTestPath1, SK_ColorBLACK, 1.0, false, 1.0); + , false); #undef RUN_TESTS2 #undef RUN_TESTS @@ -977,8 +1128,9 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); for (int i = 0; i < 10; i++) { - builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); + receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); } auto display_list = builder.Build(); EXPECT_FALSE(display_list->can_apply_group_opacity()); @@ -986,65 +1138,80 @@ TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) { TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) { DisplayListBuilder builder; - builder.SaveLayer(nullptr, nullptr); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); for (int i = 0; i < 10; i++) { - builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); + receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); } - builder.Restore(); + receiver.restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityOverlappingChidren) { DisplayListBuilder builder; - DlPaint save_paint; - builder.SaveLayer(nullptr, &save_paint); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); for (int i = 0; i < 10; i++) { - builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); + receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); } - builder.Restore(); + receiver.restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } -TEST_F(DisplayListTest, SaveLayerPaintWithSrcBlendDoesNotSupportGroupOpacity) { +TEST_F(DisplayListTest, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) { DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setBlendMode(DlBlendMode::kSrc); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({0, 0, 10, 10}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setBlendMode(DlBlendMode::kSrc); + receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + receiver.drawRect({0, 0, 10, 10}); + receiver.restore(); + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + +TEST_F(DisplayListTest, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) { + DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setBlendMode(DlBlendMode::kSrc); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.drawRect({0, 0, 10, 10}); + receiver.restore(); auto display_list = builder.Build(); EXPECT_FALSE(display_list->can_apply_group_opacity()); } TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) { DisplayListBuilder builder; - builder.SaveLayer(nullptr, nullptr); - builder.DrawRect({0, 0, 10, 10}, DlPaint().setBlendMode(DlBlendMode::kSrc)); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + receiver.setBlendMode(DlBlendMode::kSrc); + receiver.drawRect({0, 0, 10, 10}); + receiver.restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } -TEST_F(DisplayListTest, SaveLayerPaintSupportsGroupOpacityWithChildSrcBlend) { +TEST_F(DisplayListTest, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) { DisplayListBuilder builder; - DlPaint save_paint; - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({0, 0, 10, 10}, DlPaint().setBlendMode(DlBlendMode::kSrc)); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setBlendMode(DlBlendMode::kSrc); + receiver.drawRect({0, 0, 10, 10}); + receiver.restore(); auto display_list = builder.Build(); EXPECT_TRUE(display_list->can_apply_group_opacity()); } TEST_F(DisplayListTest, SaveLayerBoundsSnapshotsImageFilter) { DisplayListBuilder builder; - DlPaint save_paint; - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({50, 50, 100, 100}, DlPaint()); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.drawRect({50, 50, 100, 100}); // This image filter should be ignored since it was not set before saveLayer - ToReceiver(builder).setImageFilter(&kTestBlurImageFilter1); - builder.Restore(); + receiver.setImageFilter(&kTestBlurImageFilter1); + receiver.restore(); SkRect bounds = builder.Build()->bounds(); EXPECT_EQ(bounds, SkRect::MakeLTRB(50, 50, 100, 100)); } @@ -1082,11 +1249,11 @@ TEST_F(DisplayListTest, SaveLayerOneSimpleOpInheritsOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1098,9 +1265,10 @@ TEST_F(DisplayListTest, SaveLayerNoAttributesInheritsOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - builder.SaveLayer(nullptr, nullptr); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1111,12 +1279,12 @@ TEST_F(DisplayListTest, SaveLayerTwoOverlappingOpsDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.DrawRect({15, 15, 25, 25}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.drawRect({10, 10, 20, 20}); + receiver.drawRect({15, 15, 25, 25}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1131,16 +1299,16 @@ TEST_F(DisplayListTest, NestedSaveLayersMightInheritOpacity) { SaveLayerOptionsExpector expector({expected1, expected2, expected3}); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({15, 15, 25, 25}, DlPaint()); - builder.Restore(); - builder.Restore(); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.drawRect({10, 10, 20, 20}); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.drawRect({15, 15, 25, 25}); + receiver.restore(); + receiver.restore(); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 3); @@ -1154,13 +1322,13 @@ TEST_F(DisplayListTest, NestedSaveLayersCanBothSupportOpacityOptimization) { SaveLayerOptionsExpector expector({expected1, expected2}); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.SaveLayer(nullptr, nullptr); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Restore(); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 2); @@ -1171,12 +1339,13 @@ TEST_F(DisplayListTest, SaveLayerImageFilterDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - save_paint.setImageFilter(&kTestBlurImageFilter1); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setImageFilter(&kTestBlurImageFilter1); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setImageFilter(nullptr); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1187,12 +1356,13 @@ TEST_F(DisplayListTest, SaveLayerColorFilterDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - save_paint.setColorFilter(&kTestMatrixColorFilter1); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setColorFilter(&kTestMatrixColorFilter1); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setColorFilter(nullptr); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1203,12 +1373,13 @@ TEST_F(DisplayListTest, SaveLayerSrcBlendDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - save_paint.setBlendMode(DlBlendMode::kSrc); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.setBlendMode(DlBlendMode::kSrc); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setBlendMode(DlBlendMode::kSrcOver); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1220,12 +1391,12 @@ TEST_F(DisplayListTest, SaveLayerImageFilterOnChildInheritsOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, - DlPaint().setImageFilter(&kTestBlurImageFilter1)); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setImageFilter(&kTestBlurImageFilter1); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1236,12 +1407,12 @@ TEST_F(DisplayListTest, SaveLayerColorFilterOnChildDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, - DlPaint().setColorFilter(&kTestMatrixColorFilter1)); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setColorFilter(&kTestMatrixColorFilter1); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1252,11 +1423,12 @@ TEST_F(DisplayListTest, SaveLayerSrcBlendOnChildDoesNotInheritOpacity) { SaveLayerOptionsExpector expector(expected); DisplayListBuilder builder; - DlPaint save_paint; - save_paint.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.SaveLayer(nullptr, &save_paint); - builder.DrawRect({10, 10, 20, 20}, DlPaint().setBlendMode(DlBlendMode::kSrc)); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setColor(SkColorSetARGB(127, 255, 255, 255)); + receiver.saveLayer(nullptr, SaveLayerOptions::kWithAttributes); + receiver.setBlendMode(DlBlendMode::kSrc); + receiver.drawRect({10, 10, 20, 20}); + receiver.restore(); builder.Build()->Dispatch(expector); EXPECT_EQ(expector.save_layer_count(), 1); @@ -1390,14 +1562,15 @@ TEST_F(DisplayListTest, FlutterSvgIssue661BoundsWereEmpty) { TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) { DisplayListBuilder builder; - builder.Translate(12.3, 14.5); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.translate(12.3, 14.5); SkMatrix matrix = SkMatrix::Translate(12.3, 14.5); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - builder.Translate(10, 10); + receiver.translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1408,14 +1581,15 @@ TEST_F(DisplayListTest, TranslateAffectsCurrentTransform) { TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) { DisplayListBuilder builder; - builder.Scale(12.3, 14.5); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.scale(12.3, 14.5); SkMatrix matrix = SkMatrix::Scale(12.3, 14.5); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - builder.Translate(10, 10); + receiver.translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1426,14 +1600,15 @@ TEST_F(DisplayListTest, ScaleAffectsCurrentTransform) { TEST_F(DisplayListTest, RotateAffectsCurrentTransform) { DisplayListBuilder builder; - builder.Rotate(12.3); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.rotate(12.3); SkMatrix matrix = SkMatrix::RotateDeg(12.3); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - builder.Translate(10, 10); + receiver.translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1444,14 +1619,15 @@ TEST_F(DisplayListTest, RotateAffectsCurrentTransform) { TEST_F(DisplayListTest, SkewAffectsCurrentTransform) { DisplayListBuilder builder; - builder.Skew(12.3, 14.5); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.skew(12.3, 14.5); SkMatrix matrix = SkMatrix::Skew(12.3, 14.5); SkM44 m44 = SkM44(matrix); SkM44 cur_m44 = builder.GetTransformFullPerspective(); SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - builder.Translate(10, 10); + receiver.translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1462,8 +1638,9 @@ TEST_F(DisplayListTest, SkewAffectsCurrentTransform) { TEST_F(DisplayListTest, TransformAffectsCurrentTransform) { DisplayListBuilder builder; - builder.Transform2DAffine(3, 0, 12.3, // - 1, 5, 14.5); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.transform2DAffine(3, 0, 12.3, // + 1, 5, 14.5); SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3, // 1, 5, 14.5, // 0, 0, 1); @@ -1472,7 +1649,7 @@ TEST_F(DisplayListTest, TransformAffectsCurrentTransform) { SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - builder.Translate(10, 10); + receiver.translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1483,10 +1660,11 @@ TEST_F(DisplayListTest, TransformAffectsCurrentTransform) { TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) { DisplayListBuilder builder; - builder.TransformFullPerspective(3, 0, 4, 12.3, // - 1, 5, 3, 14.5, // - 0, 0, 7, 16.2, // - 0, 0, 0, 1); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.transformFullPerspective(3, 0, 4, 12.3, // + 1, 5, 3, 14.5, // + 0, 0, 7, 16.2, // + 0, 0, 0, 1); SkMatrix matrix = SkMatrix::MakeAll(3, 0, 12.3, // 1, 5, 14.5, // 0, 0, 1); @@ -1498,7 +1676,7 @@ TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) { SkMatrix cur_matrix = builder.GetTransform(); ASSERT_EQ(cur_m44, m44); ASSERT_EQ(cur_matrix, matrix); - builder.Translate(10, 10); + receiver.translate(10, 10); // CurrentTransform has changed ASSERT_NE(builder.GetTransformFullPerspective(), m44); ASSERT_NE(builder.GetTransform(), cur_matrix); @@ -1509,8 +1687,9 @@ TEST_F(DisplayListTest, FullTransformAffectsCurrentTransform) { TEST_F(DisplayListTest, ClipRectAffectsClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); - builder.ClipRect(clip_bounds, ClipOp::kIntersect, false); + receiver.clipRect(clip_bounds, ClipOp::kIntersect, false); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1518,27 +1697,27 @@ TEST_F(DisplayListTest, ClipRectAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.Save(); - builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - builder.Save(); - builder.Scale(2, 2); + receiver.save(); + receiver.scale(2, 2); SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1547,9 +1726,10 @@ TEST_F(DisplayListTest, ClipRectAffectsClipBounds) { TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26); - builder.ClipRect(clip_bounds, ClipOp::kIntersect, true); + receiver.clipRect(clip_bounds, ClipOp::kIntersect, true); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1557,27 +1737,27 @@ TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - builder.Save(); - builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); + receiver.save(); + receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - builder.Save(); - builder.Scale(2, 2); + receiver.save(); + receiver.scale(2, 2); SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1586,28 +1766,30 @@ TEST_F(DisplayListTest, ClipRectDoAAAffectsClipBounds) { TEST_F(DisplayListTest, ClipRectAffectsClipBoundsWithMatrix) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10); SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20); - builder.Save(); - builder.ClipRect(clip_bounds_1, ClipOp::kIntersect, false); - builder.Translate(10, 0); - builder.ClipRect(clip_bounds_1, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false); + receiver.translate(10, 0); + receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false); ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty()); - builder.Restore(); + receiver.restore(); - builder.Save(); - builder.ClipRect(clip_bounds_1, ClipOp::kIntersect, false); - builder.Translate(-10, -10); - builder.ClipRect(clip_bounds_2, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRect(clip_bounds_1, ClipOp::kIntersect, false); + receiver.translate(-10, -10); + receiver.clipRect(clip_bounds_2, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1); - builder.Restore(); + receiver.restore(); } TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2); - builder.ClipRRect(clip, ClipOp::kIntersect, false); + receiver.clipRRect(clip, ClipOp::kIntersect, false); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1615,27 +1797,27 @@ TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.Save(); - builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - builder.Save(); - builder.Scale(2, 2); + receiver.save(); + receiver.scale(2, 2); SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1644,10 +1826,11 @@ TEST_F(DisplayListTest, ClipRRectAffectsClipBounds) { TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26); SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2); - builder.ClipRRect(clip, ClipOp::kIntersect, true); + receiver.clipRRect(clip, ClipOp::kIntersect, true); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1655,27 +1838,27 @@ TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - builder.Save(); - builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); + receiver.save(); + receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - builder.Save(); - builder.Scale(2, 2); + receiver.save(); + receiver.scale(2, 2); SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1684,31 +1867,33 @@ TEST_F(DisplayListTest, ClipRRectDoAAAffectsClipBounds) { TEST_F(DisplayListTest, ClipRRectAffectsClipBoundsWithMatrix) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds_1 = SkRect::MakeLTRB(0, 0, 10, 10); SkRect clip_bounds_2 = SkRect::MakeLTRB(10, 10, 20, 20); SkRRect clip1 = SkRRect::MakeRectXY(clip_bounds_1, 3, 2); SkRRect clip2 = SkRRect::MakeRectXY(clip_bounds_2, 3, 2); - builder.Save(); - builder.ClipRRect(clip1, ClipOp::kIntersect, false); - builder.Translate(10, 0); - builder.ClipRRect(clip1, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRRect(clip1, ClipOp::kIntersect, false); + receiver.translate(10, 0); + receiver.clipRRect(clip1, ClipOp::kIntersect, false); ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty()); - builder.Restore(); + receiver.restore(); - builder.Save(); - builder.ClipRRect(clip1, ClipOp::kIntersect, false); - builder.Translate(-10, -10); - builder.ClipRRect(clip2, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRRect(clip1, ClipOp::kIntersect, false); + receiver.translate(-10, -10); + receiver.clipRRect(clip2, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds_1); - builder.Restore(); + receiver.restore(); } TEST_F(DisplayListTest, ClipPathAffectsClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); - builder.ClipPath(clip, ClipOp::kIntersect, false); + receiver.clipPath(clip, ClipOp::kIntersect, false); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1716,27 +1901,27 @@ TEST_F(DisplayListTest, ClipPathAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.Save(); - builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, false); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - builder.Save(); - builder.Scale(2, 2); + receiver.save(); + receiver.scale(2, 2); SkRect scaled_clip_bounds = SkRect::MakeLTRB(4.1, 4.65, 11.2, 13.85); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_clip_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1745,9 +1930,10 @@ TEST_F(DisplayListTest, ClipPathAffectsClipBounds) { TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28); - builder.ClipPath(clip, ClipOp::kIntersect, true); + receiver.clipPath(clip, ClipOp::kIntersect, true); // Save initial return values for testing restored values SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1755,27 +1941,27 @@ TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) { ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - builder.Save(); - builder.ClipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); + receiver.save(); + receiver.clipRect({0, 0, 15, 15}, ClipOp::kIntersect, true); // Both clip bounds have changed ASSERT_NE(builder.GetLocalClipBounds(), clip_expanded_bounds); ASSERT_NE(builder.GetDestinationClipBounds(), clip_expanded_bounds); // Previous return values have not changed ASSERT_EQ(initial_local_bounds, clip_expanded_bounds); ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); - builder.Save(); - builder.Scale(2, 2); + receiver.save(); + receiver.scale(2, 2); SkRect scaled_expanded_bounds = SkRect::MakeLTRB(4, 4.5, 11.5, 14); ASSERT_EQ(builder.GetLocalClipBounds(), scaled_expanded_bounds); // Destination bounds are unaffected by transform ASSERT_EQ(builder.GetDestinationClipBounds(), clip_expanded_bounds); - builder.Restore(); + receiver.restore(); // save/restore returned the values to their original values ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); @@ -1784,30 +1970,32 @@ TEST_F(DisplayListTest, ClipPathDoAAAffectsClipBounds) { TEST_F(DisplayListTest, ClipPathAffectsClipBoundsWithMatrix) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect clip_bounds = SkRect::MakeLTRB(0, 0, 10, 10); SkPath clip1 = SkPath().addCircle(2.5, 2.5, 2.5).addCircle(7.5, 7.5, 2.5); SkPath clip2 = SkPath().addCircle(12.5, 12.5, 2.5).addCircle(17.5, 17.5, 2.5); - builder.Save(); - builder.ClipPath(clip1, ClipOp::kIntersect, false); - builder.Translate(10, 0); - builder.ClipPath(clip1, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipPath(clip1, ClipOp::kIntersect, false); + receiver.translate(10, 0); + receiver.clipPath(clip1, ClipOp::kIntersect, false); ASSERT_TRUE(builder.GetDestinationClipBounds().isEmpty()); - builder.Restore(); + receiver.restore(); - builder.Save(); - builder.ClipPath(clip1, ClipOp::kIntersect, false); - builder.Translate(-10, -10); - builder.ClipPath(clip2, ClipOp::kIntersect, false); + receiver.save(); + receiver.clipPath(clip1, ClipOp::kIntersect, false); + receiver.translate(-10, -10); + receiver.clipPath(clip2, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); - builder.Restore(); + receiver.restore(); } TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); - builder.ClipRect(clip_bounds, ClipOp::kIntersect, false); + receiver.clipRect(clip_bounds, ClipOp::kIntersect, false); // Save initial return values for testing after kDifference clip SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1815,17 +2003,18 @@ TEST_F(DisplayListTest, DiffClipRectDoesNotAffectClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.ClipRect(diff_clip, ClipOp::kDifference, false); + receiver.clipRect(diff_clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); } TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkRRect diff_clip = SkRRect::MakeRectXY({0, 0, 15, 15}, 1, 1); SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7); SkRRect clip = SkRRect::MakeRectXY({10.2, 11.3, 20.4, 25.7}, 3, 2); - builder.ClipRRect(clip, ClipOp::kIntersect, false); + receiver.clipRRect(clip, ClipOp::kIntersect, false); // Save initial return values for testing after kDifference clip SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1833,17 +2022,18 @@ TEST_F(DisplayListTest, DiffClipRRectDoesNotAffectClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.ClipRRect(diff_clip, ClipOp::kDifference, false); + receiver.clipRRect(diff_clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); } TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); SkPath diff_clip = SkPath().addRect({0, 0, 15, 15}); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); - builder.ClipPath(clip, ClipOp::kIntersect, false); + receiver.clipPath(clip, ClipOp::kIntersect, false); // Save initial return values for testing after kDifference clip SkRect initial_local_bounds = builder.GetLocalClipBounds(); @@ -1851,7 +2041,7 @@ TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) { ASSERT_EQ(initial_local_bounds, clip_bounds); ASSERT_EQ(initial_destination_bounds, clip_bounds); - builder.ClipPath(diff_clip, ClipOp::kDifference, false); + receiver.clipPath(diff_clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), initial_local_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), initial_destination_bounds); } @@ -1859,9 +2049,10 @@ TEST_F(DisplayListTest, DiffClipPathDoesNotAffectClipBounds) { TEST_F(DisplayListTest, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) { SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0); DisplayListBuilder builder(cull_rect); + DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); clip.setFillType(SkPathFillType::kInverseWinding); - builder.ClipPath(clip, ClipOp::kIntersect, false); + receiver.clipPath(clip, ClipOp::kIntersect, false); ASSERT_EQ(builder.GetLocalClipBounds(), cull_rect); ASSERT_EQ(builder.GetDestinationClipBounds(), cull_rect); @@ -1870,10 +2061,11 @@ TEST_F(DisplayListTest, ClipPathWithInvertFillTypeDoesNotAffectClipBounds) { TEST_F(DisplayListTest, DiffClipPathWithInvertFillTypeAffectsClipBounds) { SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0); DisplayListBuilder builder(cull_rect); + DlOpReceiver& receiver = ToReceiver(builder); SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2); clip.setFillType(SkPathFillType::kInverseWinding); SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7); - builder.ClipPath(clip, ClipOp::kDifference, false); + receiver.clipPath(clip, ClipOp::kDifference, false); ASSERT_EQ(builder.GetLocalClipBounds(), clip_bounds); ASSERT_EQ(builder.GetDestinationClipBounds(), clip_bounds); @@ -1884,7 +2076,8 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { SkPoint vertical_points[2] = {{10, 10}, {10, 20}}; { DisplayListBuilder builder; - builder.DrawPoints(PointMode::kPolygon, 2, horizontal_points, DlPaint()); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(20, 10)); @@ -1892,7 +2085,8 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - builder.DrawPoints(PointMode::kPolygon, 2, vertical_points, DlPaint()); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawPoints(PointMode::kPolygon, 2, vertical_points); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(10, 20)); @@ -1900,14 +2094,16 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - builder.DrawPoints(PointMode::kPoints, 1, horizontal_points, DlPaint()); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawPoints(PointMode::kPoints, 1, horizontal_points); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); } { DisplayListBuilder builder; - builder.DrawPoints(PointMode::kPolygon, 2, horizontal_points, - DlPaint().setStrokeWidth(2)); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setStrokeWidth(2); + receiver.drawPoints(PointMode::kPolygon, 2, horizontal_points); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(20, 10)); @@ -1915,8 +2111,9 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - builder.DrawPoints(PointMode::kPolygon, 2, vertical_points, - DlPaint().setStrokeWidth(2)); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setStrokeWidth(2); + receiver.drawPoints(PointMode::kPolygon, 2, vertical_points); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_TRUE(bounds.contains(10, 20)); @@ -1924,8 +2121,9 @@ TEST_F(DisplayListTest, FlatDrawPointsProducesBounds) { } { DisplayListBuilder builder; - builder.DrawPoints(PointMode::kPoints, 1, horizontal_points, - DlPaint().setStrokeWidth(2)); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.setStrokeWidth(2); + receiver.drawPoints(PointMode::kPoints, 1, horizontal_points); SkRect bounds = builder.Build()->bounds(); EXPECT_TRUE(bounds.contains(10, 10)); EXPECT_EQ(bounds, SkRect::MakeLTRB(9, 9, 11, 11)); @@ -1951,8 +2149,9 @@ static void test_rtree(const std::shared_ptr& rtree, TEST_F(DisplayListTest, RTreeOfSimpleScene) { DisplayListBuilder builder(/*prepare_rtree=*/true); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.DrawRect({50, 50, 60, 60}, DlPaint()); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawRect({10, 10, 20, 20}); + receiver.drawRect({50, 50, 60, 60}); auto display_list = builder.Build(); auto rtree = display_list->rtree(); std::vector rects = { @@ -1978,10 +2177,11 @@ TEST_F(DisplayListTest, RTreeOfSimpleScene) { TEST_F(DisplayListTest, RTreeOfSaveRestoreScene) { DisplayListBuilder builder(/*prepare_rtree=*/true); - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Save(); - builder.DrawRect({50, 50, 60, 60}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawRect({10, 10, 20, 20}); + receiver.save(); + receiver.drawRect({50, 50, 60, 60}); + receiver.restore(); auto display_list = builder.Build(); auto rtree = display_list->rtree(); std::vector rects = { @@ -2042,12 +2242,14 @@ TEST_F(DisplayListTest, RTreeOfSaveLayerFilterScene) { TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) { DisplayListBuilder nested_dl_builder(/**prepare_rtree=*/true); - nested_dl_builder.DrawRect({10, 10, 20, 20}, DlPaint()); - nested_dl_builder.DrawRect({50, 50, 60, 60}, DlPaint()); + DlOpReceiver& nested_dl_receiver = ToReceiver(nested_dl_builder); + nested_dl_receiver.drawRect({10, 10, 20, 20}); + nested_dl_receiver.drawRect({50, 50, 60, 60}); auto nested_display_list = nested_dl_builder.Build(); DisplayListBuilder builder(/**prepare_rtree=*/true); - builder.DrawDisplayList(nested_display_list); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawDisplayList(nested_display_list); auto display_list = builder.Build(); auto rtree = display_list->rtree(); @@ -2063,61 +2265,67 @@ TEST_F(DisplayListTest, NestedDisplayListRTreesAreSparse) { TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairs) { { DisplayListBuilder builder; - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Save(); // This save op is unnecessary - builder.DrawRect({50, 50, 60, 60}, DlPaint()); - builder.Restore(); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawRect({10, 10, 20, 20}); + receiver.save(); // This save op is unnecessary + receiver.drawRect({50, 50, 60, 60}); + receiver.restore(); DisplayListBuilder builder2; - builder2.DrawRect({10, 10, 20, 20}, DlPaint()); - builder2.DrawRect({50, 50, 60, 60}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({10, 10, 20, 20}); + receiver2.drawRect({50, 50, 60, 60}); ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build())); } { DisplayListBuilder builder; - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - builder.Save(); - builder.Translate(1.0, 1.0); + DlOpReceiver& receiver = ToReceiver(builder); + receiver.drawRect({10, 10, 20, 20}); + receiver.save(); + receiver.translate(1.0, 1.0); { - builder.Save(); // unnecessary - builder.DrawRect({50, 50, 60, 60}, DlPaint()); - builder.Restore(); + receiver.save(); // unnecessary + receiver.drawRect({50, 50, 60, 60}); + receiver.restore(); } - builder.Restore(); + receiver.restore(); DisplayListBuilder builder2; - builder2.DrawRect({10, 10, 20, 20}, DlPaint()); - builder2.Save(); - builder2.Translate(1.0, 1.0); - { builder2.DrawRect({50, 50, 60, 60}, DlPaint()); } - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({10, 10, 20, 20}); + receiver2.save(); + receiver2.translate(1.0, 1.0); + { receiver2.drawRect({50, 50, 60, 60}); } + receiver2.restore(); ASSERT_TRUE(DisplayListsEQ_Verbose(builder.Build(), builder2.Build())); } } TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Save(); - builder1.Translate(10, 10); - builder1.Scale(2, 2); - builder1.ClipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.save(); + receiver1.translate(10, 10); + receiver1.scale(2, 2); + receiver1.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.Translate(10, 10); - builder2.Scale(2, 2); - builder2.ClipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.translate(10, 10); + receiver2.scale(2, 2); + receiver2.clipRect({10, 10, 20, 20}, ClipOp::kIntersect, false); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2125,19 +2333,21 @@ TEST_F(DisplayListTest, CollapseMultipleNestedSaveRestore) { TEST_F(DisplayListTest, CollapseNestedSaveAndSaveLayerRestore) { DisplayListBuilder builder1; - builder1.Save(); - builder1.SaveLayer(nullptr, nullptr); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Scale(2, 2); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.scale(2, 2); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.SaveLayer(nullptr, nullptr); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Scale(2, 2); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.saveLayer(nullptr, SaveLayerOptions::kNoAttributes); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.scale(2, 2); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2200,37 +2410,39 @@ TEST_F(DisplayListTest, RemoveUnnecessarySaveRestorePairsInSetPaint) { TEST_F(DisplayListTest, TransformTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.TransformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.TransformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.transformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.TransformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); - builder2.Save(); - builder2.TransformFullPerspective(1, 0, 0, 10, // - 0, 1, 0, 100, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.transformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); + receiver2.save(); + receiver2.transformFullPerspective(1, 0, 0, 10, // + 0, 1, 0, 100, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2238,19 +2450,21 @@ TEST_F(DisplayListTest, TransformTriggersDeferredSave) { TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Transform2DAffine(0, 1, 12, 1, 0, 33); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transform2DAffine(0, 1, 12, 1, 0, 33); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.Transform2DAffine(0, 1, 12, 1, 0, 33); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.transform2DAffine(0, 1, 12, 1, 0, 33); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2258,25 +2472,27 @@ TEST_F(DisplayListTest, Transform2DTriggersDeferredSave) { TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.TransformFullPerspective(0, 1, 0, 12, // - 1, 0, 0, 33, // - 3, 2, 5, 29, // - 0, 0, 0, 12); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transformFullPerspective(0, 1, 0, 12, // + 1, 0, 0, 33, // + 3, 2, 5, 29, // + 0, 0, 0, 12); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.TransformFullPerspective(0, 1, 0, 12, // - 1, 0, 0, 33, // - 3, 2, 5, 29, // - 0, 0, 0, 12); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.transformFullPerspective(0, 1, 0, 12, // + 1, 0, 0, 33, // + 3, 2, 5, 29, // + 0, 0, 0, 12); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2284,19 +2500,21 @@ TEST_F(DisplayListTest, TransformPerspectiveTriggersDeferredSave) { TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.TransformReset(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transformReset(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.TransformReset(); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.transformReset(); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2304,19 +2522,21 @@ TEST_F(DisplayListTest, ResetTransformTriggersDeferredSave) { TEST_F(DisplayListTest, SkewTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Skew(10, 10); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.skew(10, 10); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.Skew(10, 10); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.skew(10, 10); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2324,19 +2544,21 @@ TEST_F(DisplayListTest, SkewTriggersDeferredSave) { TEST_F(DisplayListTest, TranslateTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Translate(10, 10); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.translate(10, 10); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.Translate(10, 10); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.translate(10, 10); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2344,19 +2566,21 @@ TEST_F(DisplayListTest, TranslateTriggersDeferredSave) { TEST_F(DisplayListTest, ScaleTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Scale(0.5, 0.5); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.scale(0.5, 0.5); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.Scale(0.5, 0.5); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.scale(0.5, 0.5); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2364,29 +2588,33 @@ TEST_F(DisplayListTest, ScaleTriggersDeferredSave) { TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.ClipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, true); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, + true); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.ClipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, true); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); - builder2.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.clipRect(SkRect::MakeLTRB(0, 0, 100, 100), ClipOp::kIntersect, + true); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); + receiver2.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2394,31 +2622,33 @@ TEST_F(DisplayListTest, ClipRectTriggersDeferredSave) { TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.ClipRRect(kTestRRect, ClipOp::kIntersect, true); - - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.clipRRect(kTestRRect, ClipOp::kIntersect, true); + + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.ClipRRect(kTestRRect, ClipOp::kIntersect, true); - - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); - builder2.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.clipRRect(kTestRRect, ClipOp::kIntersect, true); + + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); + receiver2.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2426,29 +2656,31 @@ TEST_F(DisplayListTest, ClipRRectTriggersDeferredSave) { TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.ClipPath(kTestPath1, ClipOp::kIntersect, true); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.clipPath(kTestPath1, ClipOp::kIntersect, true); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.ClipPath(kTestPath1, ClipOp::kIntersect, true); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); - builder2.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.clipPath(kTestPath1, ClipOp::kIntersect, true); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); + receiver2.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2456,18 +2688,20 @@ TEST_F(DisplayListTest, ClipPathTriggersDeferredSave) { TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Translate(0, 0); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.translate(0, 0); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2475,18 +2709,20 @@ TEST_F(DisplayListTest, NOPTranslateDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Scale(1.0, 1.0); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.scale(1.0, 1.0); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2494,18 +2730,20 @@ TEST_F(DisplayListTest, NOPScaleDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Rotate(360); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.rotate(360); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2513,18 +2751,20 @@ TEST_F(DisplayListTest, NOPRotationDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Skew(0, 0); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.skew(0, 0); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2532,25 +2772,27 @@ TEST_F(DisplayListTest, NOPSkewDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2558,18 +2800,20 @@ TEST_F(DisplayListTest, NOPTransformDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.Transform2DAffine(1, 0, 0, 0, 1, 0); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transform2DAffine(1, 0, 0, 0, 1, 0); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2578,21 +2822,23 @@ TEST_F(DisplayListTest, NOPTransform2DDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2600,25 +2846,27 @@ TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.TransformFullPerspective(1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1); - builder1.TransformReset(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.transformFullPerspective(1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1); + receiver1.transformReset(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.Save(); - builder2.TransformReset(); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.Restore(); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.save(); + receiver2.transformReset(); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.restore(); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); @@ -2628,19 +2876,21 @@ TEST_F(DisplayListTest, NOPTransformFullPerspectiveDoesNotTriggerDeferredSave) { TEST_F(DisplayListTest, NOPClipDoesNotTriggerDeferredSave) { DisplayListBuilder builder1; - builder1.Save(); - builder1.Save(); - builder1.ClipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0), - ClipOp::kIntersect, true); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); - builder1.DrawRect({0, 0, 100, 100}, DlPaint()); - builder1.Restore(); + DlOpReceiver& receiver1 = ToReceiver(builder1); + receiver1.save(); + receiver1.save(); + receiver1.clipRect(SkRect::MakeLTRB(0, SK_ScalarNaN, SK_ScalarNaN, 0), + ClipOp::kIntersect, true); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); + receiver1.drawRect({0, 0, 100, 100}); + receiver1.restore(); auto display_list1 = builder1.Build(); DisplayListBuilder builder2; - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); - builder2.DrawRect({0, 0, 100, 100}, DlPaint()); + DlOpReceiver& receiver2 = ToReceiver(builder2); + receiver2.drawRect({0, 0, 100, 100}); + receiver2.drawRect({0, 0, 100, 100}); auto display_list2 = builder2.Build(); ASSERT_TRUE(DisplayListsEQ_Verbose(display_list1, display_list2)); @@ -2685,10 +2935,11 @@ TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) { TEST_F(DisplayListTest, RTreeRenderCulling) { DisplayListBuilder main_builder(true); - main_builder.DrawRect({0, 0, 10, 10}, DlPaint()); - main_builder.DrawRect({20, 0, 30, 10}, DlPaint()); - main_builder.DrawRect({0, 20, 10, 30}, DlPaint()); - main_builder.DrawRect({20, 20, 30, 30}, DlPaint()); + DlOpReceiver& main_receiver = ToReceiver(main_builder); + main_receiver.drawRect({0, 0, 10, 10}); + main_receiver.drawRect({20, 0, 30, 10}); + main_receiver.drawRect({0, 20, 10, 30}); + main_receiver.drawRect({20, 20, 30, 30}); auto main = main_builder.Build(); auto test = [main](SkIRect cull_rect, const sk_sp& expected) { @@ -2720,7 +2971,8 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {9, 9, 19, 19}; DisplayListBuilder expected_builder; - expected_builder.DrawRect({0, 0, 10, 10}, DlPaint()); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({0, 0, 10, 10}); auto expected = expected_builder.Build(); test(cull_rect, expected); @@ -2730,7 +2982,8 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {11, 9, 21, 19}; DisplayListBuilder expected_builder; - expected_builder.DrawRect({20, 0, 30, 10}, DlPaint()); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({20, 0, 30, 10}); auto expected = expected_builder.Build(); test(cull_rect, expected); @@ -2740,7 +2993,8 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {9, 11, 19, 21}; DisplayListBuilder expected_builder; - expected_builder.DrawRect({0, 20, 10, 30}, DlPaint()); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({0, 20, 10, 30}); auto expected = expected_builder.Build(); test(cull_rect, expected); @@ -2750,7 +3004,8 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { SkIRect cull_rect = {11, 11, 21, 21}; DisplayListBuilder expected_builder; - expected_builder.DrawRect({20, 20, 30, 30}, DlPaint()); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({20, 20, 30, 30}); auto expected = expected_builder.Build(); test(cull_rect, expected); diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc new file mode 100644 index 0000000000000..97268684966fb --- /dev/null +++ b/display_list/dl_builder.cc @@ -0,0 +1,1625 @@ +// 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 "flutter/display_list/dl_builder.h" + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/dl_blend_mode.h" +#include "flutter/display_list/dl_op_flags.h" +#include "flutter/display_list/dl_op_records.h" +#include "flutter/display_list/effects/dl_color_source.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "fml/logging.h" +#include "third_party/skia/include/core/SkScalar.h" + +namespace flutter { + +#define DL_BUILDER_PAGE 4096 + +// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. +static void CopyV(void* dst) {} + +template +static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { + FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) + << "Expected " << dst << " to be aligned for at least " << alignof(S) + << " bytes."; + // If n is 0, there is nothing to copy into dst from src. + if (n > 0) { + memcpy(dst, src, n * sizeof(S)); + dst = reinterpret_cast(reinterpret_cast(dst) + + n * sizeof(S)); + } + // Repeat for the next items, if any + CopyV(dst, std::forward(rest)...); +} + +static constexpr inline bool is_power_of_two(int value) { + return (value & (value - 1)) == 0; +} + +template +void* DisplayListBuilder::Push(size_t pod, int render_op_inc, Args&&... args) { + size_t size = SkAlignPtr(sizeof(T) + pod); + FML_DCHECK(size < (1 << 24)); + if (used_ + size > allocated_) { + static_assert(is_power_of_two(DL_BUILDER_PAGE), + "This math needs updating for non-pow2."); + // Next greater multiple of DL_BUILDER_PAGE. + allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); + storage_.realloc(allocated_); + FML_DCHECK(storage_.get()); + memset(storage_.get() + used_, 0, allocated_ - used_); + } + FML_DCHECK(used_ + size <= allocated_); + auto op = reinterpret_cast(storage_.get() + used_); + used_ += size; + new (op) T{std::forward(args)...}; + op->type = T::kType; + op->size = size; + render_op_count_ += render_op_inc; + op_index_++; + return op + 1; +} + +sk_sp DisplayListBuilder::Build() { + while (layer_stack_.size() > 1) { + restore(); + } + + size_t bytes = used_; + int count = render_op_count_; + size_t nested_bytes = nested_bytes_; + int nested_count = nested_op_count_; + bool compatible = current_layer_->is_group_opacity_compatible(); + bool is_safe = is_ui_thread_safe_; + bool affects_transparency = current_layer_->affects_transparent_layer(); + + used_ = allocated_ = render_op_count_ = op_index_ = 0; + nested_bytes_ = nested_op_count_ = 0; + is_ui_thread_safe_ = true; + storage_.realloc(bytes); + layer_stack_.pop_back(); + layer_stack_.emplace_back(); + tracker_.reset(); + current_ = DlPaint(); + + return sk_sp(new DisplayList( + std::move(storage_), bytes, count, nested_bytes, nested_count, bounds(), + compatible, is_safe, affects_transparency, rtree())); +} + +DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, + bool prepare_rtree) + : tracker_(cull_rect, SkMatrix::I()) { + if (prepare_rtree) { + accumulator_ = std::make_unique(); + } else { + accumulator_ = std::make_unique(); + } + + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); +} + +DisplayListBuilder::~DisplayListBuilder() { + uint8_t* ptr = storage_.get(); + if (ptr) { + DisplayList::DisposeOps(ptr, ptr + used_); + } +} + +SkISize DisplayListBuilder::GetBaseLayerSize() const { + return tracker_.base_device_cull_rect().roundOut().size(); +} + +SkImageInfo DisplayListBuilder::GetImageInfo() const { + SkISize size = GetBaseLayerSize(); + return SkImageInfo::MakeUnknown(size.width(), size.height()); +} + +void DisplayListBuilder::onSetAntiAlias(bool aa) { + current_.setAntiAlias(aa); + Push(0, 0, aa); +} +void DisplayListBuilder::onSetDither(bool dither) { + current_.setDither(dither); + Push(0, 0, dither); +} +void DisplayListBuilder::onSetInvertColors(bool invert) { + current_.setInvertColors(invert); + Push(0, 0, invert); + UpdateCurrentOpacityCompatibility(); +} +void DisplayListBuilder::onSetStrokeCap(DlStrokeCap cap) { + current_.setStrokeCap(cap); + Push(0, 0, cap); +} +void DisplayListBuilder::onSetStrokeJoin(DlStrokeJoin join) { + current_.setStrokeJoin(join); + Push(0, 0, join); +} +void DisplayListBuilder::onSetDrawStyle(DlDrawStyle style) { + current_.setDrawStyle(style); + Push(0, 0, style); +} +void DisplayListBuilder::onSetStrokeWidth(float width) { + current_.setStrokeWidth(width); + Push(0, 0, width); +} +void DisplayListBuilder::onSetStrokeMiter(float limit) { + current_.setStrokeMiter(limit); + Push(0, 0, limit); +} +void DisplayListBuilder::onSetColor(DlColor color) { + current_.setColor(color); + Push(0, 0, color); +} +void DisplayListBuilder::onSetBlendMode(DlBlendMode mode) { + current_.setBlendMode(mode); + Push(0, 0, mode); + UpdateCurrentOpacityCompatibility(); +} + +void DisplayListBuilder::onSetColorSource(const DlColorSource* source) { + if (source == nullptr) { + current_.setColorSource(nullptr); + Push(0, 0); + } else { + current_.setColorSource(source->shared()); + is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe(); + switch (source->type()) { + case DlColorSourceType::kColor: { + const DlColorColorSource* color_source = source->asColor(); + current_.setColorSource(nullptr); + setColor(color_source->color()); + break; + } + case DlColorSourceType::kImage: { + const DlImageColorSource* image_source = source->asImage(); + FML_DCHECK(image_source); + Push(0, 0, image_source); + break; + } + case DlColorSourceType::kLinearGradient: { + const DlLinearGradientColorSource* linear = source->asLinearGradient(); + FML_DCHECK(linear); + void* pod = Push(linear->size(), 0); + new (pod) DlLinearGradientColorSource(linear); + break; + } + case DlColorSourceType::kRadialGradient: { + const DlRadialGradientColorSource* radial = source->asRadialGradient(); + FML_DCHECK(radial); + void* pod = Push(radial->size(), 0); + new (pod) DlRadialGradientColorSource(radial); + break; + } + case DlColorSourceType::kConicalGradient: { + const DlConicalGradientColorSource* conical = + source->asConicalGradient(); + FML_DCHECK(conical); + void* pod = Push(conical->size(), 0); + new (pod) DlConicalGradientColorSource(conical); + break; + } + case DlColorSourceType::kSweepGradient: { + const DlSweepGradientColorSource* sweep = source->asSweepGradient(); + FML_DCHECK(sweep); + void* pod = Push(sweep->size(), 0); + new (pod) DlSweepGradientColorSource(sweep); + break; + } + case DlColorSourceType::kRuntimeEffect: { + const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect(); + FML_DCHECK(effect); + Push(0, 0, effect); + break; + } +#ifdef IMPELLER_ENABLE_3D + case DlColorSourceType::kScene: { + const DlSceneColorSource* scene = source->asScene(); + FML_DCHECK(scene); + Push(0, 0, scene); + break; + } +#endif // IMPELLER_ENABLE_3D + } + } +} +void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) { + if (filter == nullptr) { + current_.setImageFilter(nullptr); + Push(0, 0); + } else { + current_.setImageFilter(filter->shared()); + switch (filter->type()) { + case DlImageFilterType::kBlur: { + const DlBlurImageFilter* blur_filter = filter->asBlur(); + FML_DCHECK(blur_filter); + void* pod = Push(blur_filter->size(), 0); + new (pod) DlBlurImageFilter(blur_filter); + break; + } + case DlImageFilterType::kDilate: { + const DlDilateImageFilter* dilate_filter = filter->asDilate(); + FML_DCHECK(dilate_filter); + void* pod = Push(dilate_filter->size(), 0); + new (pod) DlDilateImageFilter(dilate_filter); + break; + } + case DlImageFilterType::kErode: { + const DlErodeImageFilter* erode_filter = filter->asErode(); + FML_DCHECK(erode_filter); + void* pod = Push(erode_filter->size(), 0); + new (pod) DlErodeImageFilter(erode_filter); + break; + } + case DlImageFilterType::kMatrix: { + const DlMatrixImageFilter* matrix_filter = filter->asMatrix(); + FML_DCHECK(matrix_filter); + void* pod = Push(matrix_filter->size(), 0); + new (pod) DlMatrixImageFilter(matrix_filter); + break; + } + case DlImageFilterType::kCompose: + case DlImageFilterType::kLocalMatrix: + case DlImageFilterType::kColorFilter: { + Push(0, 0, filter); + break; + } + } + } +} +void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) { + if (filter == nullptr) { + current_.setColorFilter(nullptr); + Push(0, 0); + } else { + current_.setColorFilter(filter->shared()); + switch (filter->type()) { + case DlColorFilterType::kBlend: { + const DlBlendColorFilter* blend_filter = filter->asBlend(); + FML_DCHECK(blend_filter); + void* pod = Push(blend_filter->size(), 0); + new (pod) DlBlendColorFilter(blend_filter); + break; + } + case DlColorFilterType::kMatrix: { + const DlMatrixColorFilter* matrix_filter = filter->asMatrix(); + FML_DCHECK(matrix_filter); + void* pod = Push(matrix_filter->size(), 0); + new (pod) DlMatrixColorFilter(matrix_filter); + break; + } + case DlColorFilterType::kSrgbToLinearGamma: { + void* pod = Push(filter->size(), 0); + new (pod) DlSrgbToLinearGammaColorFilter(); + break; + } + case DlColorFilterType::kLinearToSrgbGamma: { + void* pod = Push(filter->size(), 0); + new (pod) DlLinearToSrgbGammaColorFilter(); + break; + } + } + } + UpdateCurrentOpacityCompatibility(); +} +void DisplayListBuilder::onSetPathEffect(const DlPathEffect* effect) { + if (effect == nullptr) { + current_.setPathEffect(nullptr); + Push(0, 0); + } else { + current_.setPathEffect(effect->shared()); + switch (effect->type()) { + case DlPathEffectType::kDash: { + const DlDashPathEffect* dash_effect = effect->asDash(); + void* pod = Push(dash_effect->size(), 0); + new (pod) DlDashPathEffect(dash_effect); + break; + } + } + } +} +void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) { + if (filter == nullptr) { + current_.setMaskFilter(nullptr); + Push(0, 0); + } else { + current_.setMaskFilter(filter->shared()); + switch (filter->type()) { + case DlMaskFilterType::kBlur: { + const DlBlurMaskFilter* blur_filter = filter->asBlur(); + FML_DCHECK(blur_filter); + void* pod = Push(blur_filter->size(), 0); + new (pod) DlBlurMaskFilter(blur_filter); + break; + } + } + } +} + +void DisplayListBuilder::SetAttributesFromPaint( + const DlPaint& paint, + const DisplayListAttributeFlags flags) { + if (flags.applies_anti_alias()) { + setAntiAlias(paint.isAntiAlias()); + } + if (flags.applies_dither()) { + setDither(paint.isDither()); + } + if (flags.applies_alpha_or_color()) { + setColor(paint.getColor().argb); + } + if (flags.applies_blend()) { + setBlendMode(paint.getBlendMode()); + } + if (flags.applies_style()) { + setDrawStyle(paint.getDrawStyle()); + } + if (flags.is_stroked(paint.getDrawStyle())) { + setStrokeWidth(paint.getStrokeWidth()); + setStrokeMiter(paint.getStrokeMiter()); + setStrokeCap(paint.getStrokeCap()); + setStrokeJoin(paint.getStrokeJoin()); + } + if (flags.applies_shader()) { + setColorSource(paint.getColorSource().get()); + } + if (flags.applies_color_filter()) { + setInvertColors(paint.isInvertColors()); + setColorFilter(paint.getColorFilter().get()); + } + if (flags.applies_image_filter()) { + setImageFilter(paint.getImageFilter().get()); + } + if (flags.applies_path_effect()) { + setPathEffect(paint.getPathEffect().get()); + } + if (flags.applies_mask_filter()) { + setMaskFilter(paint.getMaskFilter().get()); + } +} + +void DisplayListBuilder::checkForDeferredSave() { + if (current_layer_->has_deferred_save_op_) { + size_t save_offset_ = used_; + Push(0, 1); + current_layer_->save_offset_ = save_offset_; + current_layer_->has_deferred_save_op_ = false; + } +} + +void DisplayListBuilder::Save() { + bool is_nop = current_layer_->is_nop_; + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); + current_layer_->has_deferred_save_op_ = true; + current_layer_->is_nop_ = is_nop; + tracker_.save(); + accumulator()->save(); +} + +void DisplayListBuilder::Restore() { + if (layer_stack_.size() > 1) { + SaveOpBase* op = reinterpret_cast( + storage_.get() + current_layer_->save_offset()); + if (!current_layer_->has_deferred_save_op_) { + op->restore_index = op_index_; + Push(0, 1); + } + // Grab the current layer info before we push the restore + // on the stack. + LayerInfo layer_info = layer_stack_.back(); + + tracker_.restore(); + layer_stack_.pop_back(); + current_layer_ = &layer_stack_.back(); + bool is_unbounded = layer_info.is_unbounded(); + + // Before we pop_back we will get the current layer bounds from the + // current accumulator and adjust it as required based on the filter. + std::shared_ptr filter = layer_info.filter(); + if (filter) { + const SkRect clip = tracker_.device_cull_rect(); + if (!accumulator()->restore( + [filter = filter, matrix = GetTransform()](const SkRect& input, + SkRect& output) { + SkIRect output_bounds; + bool ret = filter->map_device_bounds(input.roundOut(), matrix, + output_bounds); + output.set(output_bounds); + return ret; + }, + &clip)) { + is_unbounded = true; + } + } else { + accumulator()->restore(); + } + + if (is_unbounded) { + AccumulateUnbounded(); + } + + if (layer_info.has_layer()) { + // Layers are never deferred for now, we need to update the + // following code if we ever do saveLayer culling... + FML_DCHECK(!layer_info.has_deferred_save_op_); + if (layer_info.is_group_opacity_compatible()) { + // We are now going to go back and modify the matching saveLayer + // call to add the option indicating it can distribute an opacity + // value to its children. + // + // Note that this operation cannot and does not change the size + // or structure of the SaveLayerOp record. It only sets an option + // flag on an existing field. + // + // Note that these kinds of modification operations on data already + // in the DisplayList are only allowed *during* the build phase. + // Once built, the DisplayList records must remain read only to + // ensure consistency of rendering and |Equals()| behavior. + op->options = op->options.with_can_distribute_opacity(); + } + } else { + // For regular save() ops there was no protecting layer so we have to + // accumulate the values into the enclosing layer. + if (layer_info.cannot_inherit_opacity()) { + current_layer_->mark_incompatible(); + } else if (layer_info.has_compatible_op()) { + current_layer_->add_compatible_op(); + } + } + } +} +void DisplayListBuilder::RestoreToCount(int restore_count) { + FML_DCHECK(restore_count <= GetSaveCount()); + while (restore_count < GetSaveCount() && GetSaveCount() > 1) { + restore(); + } +} +void DisplayListBuilder::saveLayer(const SkRect* bounds, + const SaveLayerOptions in_options, + const DlImageFilter* backdrop) { + SaveLayerOptions options = in_options.without_optimizations(); + DisplayListAttributeFlags flags = options.renders_with_attributes() + ? kSaveLayerWithPaintFlags + : kSaveLayerFlags; + OpResult result = PaintResult(current_, flags); + if (result == OpResult::kNoEffect) { + save(); + current_layer_->is_nop_ = true; + return; + } + size_t save_layer_offset = used_; + if (options.renders_with_attributes()) { + // The actual flood of the outer layer clip will occur after the + // (eventual) corresponding restore is called, but rather than + // remember this information in the LayerInfo until the restore + // method is processed, we just mark the unbounded state up front. + // Another reason to accumulate the clip here rather than in + // restore is so that this savelayer will be tagged in the rtree + // with its full bounds and the right op_index so that it doesn't + // get culled during rendering. + if (!paint_nops_on_transparency()) { + // We will fill the clip of the outer layer when we restore. + // Accumulate should always return true here because if the + // clip was empty then that would have been caught up above + // when we tested the PaintResult. + [[maybe_unused]] bool unclipped = AccumulateUnbounded(); + FML_DCHECK(unclipped); + } + CheckLayerOpacityCompatibility(true); + layer_stack_.emplace_back(save_layer_offset, true, + current_.getImageFilter()); + } else { + CheckLayerOpacityCompatibility(false); + layer_stack_.emplace_back(save_layer_offset, true, nullptr); + } + current_layer_ = &layer_stack_.back(); + + tracker_.save(); + accumulator()->save(); + + if (backdrop) { + // A backdrop will affect up to the entire surface, bounded by the clip + // Accumulate should always return true here because if the + // clip was empty then that would have been caught up above + // when we tested the PaintResult. + [[maybe_unused]] bool unclipped = AccumulateUnbounded(); + FML_DCHECK(unclipped); + bounds // + ? Push(0, 1, options, *bounds, backdrop) + : Push(0, 1, options, backdrop); + } else { + bounds // + ? Push(0, 1, options, *bounds) + : Push(0, 1, options); + } + + if (options.renders_with_attributes()) { + // |current_opacity_compatibility_| does not take an ImageFilter into + // account because an individual primitive with an ImageFilter can apply + // opacity on top of it. But, if the layer is applying the ImageFilter + // then it cannot pass the opacity on. + if (!current_opacity_compatibility_ || + current_.getImageFilter() != nullptr) { + UpdateLayerOpacityCompatibility(false); + } + } + UpdateLayerResult(result); + + if (options.renders_with_attributes() && current_.getImageFilter()) { + // We use |resetCullRect| here because we will be accumulating bounds of + // primitives before applying the filter to those bounds. We might + // encounter a primitive whose bounds are clipped, but whose filtered + // bounds will not be clipped. If the individual rendering ops bounds + // are clipped, it will not contribute to the overall bounds which + // could lead to inaccurate (subset) bounds of the DisplayList. + // We need to reset the cull rect here to avoid this premature clipping. + // The filtered bounds will be clipped to the existing clip rect when + // this layer is restored. + // If bounds is null then the original cull_rect will be used. + tracker_.resetCullRect(bounds); + } else if (bounds) { + // Even though Skia claims that the bounds are only a hint, they actually + // use them as the temporary layer bounds during rendering the layer, so + // we set them as if a clip operation were performed. + tracker_.clipRect(*bounds, ClipOp::kIntersect, false); + } +} +void DisplayListBuilder::SaveLayer(const SkRect* bounds, + const DlPaint* paint, + const DlImageFilter* backdrop) { + if (paint != nullptr) { + SetAttributesFromPaint(*paint, + DisplayListOpFlags::kSaveLayerWithPaintFlags); + saveLayer(bounds, SaveLayerOptions::kWithAttributes, backdrop); + } else { + saveLayer(bounds, SaveLayerOptions::kNoAttributes, backdrop); + } +} + +void DisplayListBuilder::Translate(SkScalar tx, SkScalar ty) { + if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && + (tx != 0.0 || ty != 0.0)) { + checkForDeferredSave(); + Push(0, 1, tx, ty); + tracker_.translate(tx, ty); + } +} +void DisplayListBuilder::Scale(SkScalar sx, SkScalar sy) { + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 1.0 || sy != 1.0)) { + checkForDeferredSave(); + Push(0, 1, sx, sy); + tracker_.scale(sx, sy); + } +} +void DisplayListBuilder::Rotate(SkScalar degrees) { + if (SkScalarMod(degrees, 360.0) != 0.0) { + checkForDeferredSave(); + Push(0, 1, degrees); + tracker_.rotate(degrees); + } +} +void DisplayListBuilder::Skew(SkScalar sx, SkScalar sy) { + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 0.0 || sy != 0.0)) { + checkForDeferredSave(); + Push(0, 1, sx, sy); + tracker_.skew(sx, sy); + } +} + +// clang-format off + +// 2x3 2D affine subset of a 4x4 transform in row major order +void DisplayListBuilder::Transform2DAffine( + SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) { + if (SkScalarsAreFinite(mxx, myx) && + SkScalarsAreFinite(mxy, myy) && + SkScalarsAreFinite(mxt, myt)) { + if (mxx == 1 && mxy == 0 && + myx == 0 && myy == 1) { + Translate(mxt, myt); + } else { + checkForDeferredSave(); + Push(0, 1, + mxx, mxy, mxt, + myx, myy, myt); + tracker_.transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + } + } +} +// full 4x4 transform in row major order +void DisplayListBuilder::TransformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { + if ( mxz == 0 && + myz == 0 && + mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 && + mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { + Transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && + SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && + SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && + SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { + checkForDeferredSave(); + Push(0, 1, + mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + tracker_.transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + } +} +// clang-format on +void DisplayListBuilder::TransformReset() { + checkForDeferredSave(); + Push(0, 0); + tracker_.setIdentity(); +} +void DisplayListBuilder::Transform(const SkMatrix* matrix) { + if (matrix != nullptr) { + Transform(SkM44(*matrix)); + } +} +void DisplayListBuilder::Transform(const SkM44* m44) { + if (m44 != nullptr) { + transformFullPerspective( + m44->rc(0, 0), m44->rc(0, 1), m44->rc(0, 2), m44->rc(0, 3), + m44->rc(1, 0), m44->rc(1, 1), m44->rc(1, 2), m44->rc(1, 3), + m44->rc(2, 0), m44->rc(2, 1), m44->rc(2, 2), m44->rc(2, 3), + m44->rc(3, 0), m44->rc(3, 1), m44->rc(3, 2), m44->rc(3, 3)); + } +} + +void DisplayListBuilder::ClipRect(const SkRect& rect, + ClipOp clip_op, + bool is_aa) { + if (!rect.isFinite()) { + return; + } + tracker_.clipRect(rect, clip_op, is_aa); + if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { + current_layer_->is_nop_ = true; + return; + } + checkForDeferredSave(); + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, 1, rect, is_aa); + break; + case ClipOp::kDifference: + Push(0, 1, rect, is_aa); + break; + } +} +void DisplayListBuilder::ClipRRect(const SkRRect& rrect, + ClipOp clip_op, + bool is_aa) { + if (rrect.isRect()) { + clipRect(rrect.rect(), clip_op, is_aa); + } else { + tracker_.clipRRect(rrect, clip_op, is_aa); + if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { + current_layer_->is_nop_ = true; + return; + } + checkForDeferredSave(); + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, 1, rrect, is_aa); + break; + case ClipOp::kDifference: + Push(0, 1, rrect, is_aa); + break; + } + } +} +void DisplayListBuilder::ClipPath(const SkPath& path, + ClipOp clip_op, + bool is_aa) { + if (!path.isInverseFillType()) { + SkRect rect; + if (path.isRect(&rect)) { + this->clipRect(rect, clip_op, is_aa); + return; + } + SkRRect rrect; + if (path.isOval(&rect)) { + rrect.setOval(rect); + this->clipRRect(rrect, clip_op, is_aa); + return; + } + if (path.isRRect(&rrect)) { + this->clipRRect(rrect, clip_op, is_aa); + return; + } + } + tracker_.clipPath(path, clip_op, is_aa); + if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { + current_layer_->is_nop_ = true; + return; + } + checkForDeferredSave(); + switch (clip_op) { + case ClipOp::kIntersect: + Push(0, 1, path, is_aa); + break; + case ClipOp::kDifference: + Push(0, 1, path, is_aa); + break; + } +} + +bool DisplayListBuilder::QuickReject(const SkRect& bounds) const { + return tracker_.content_culled(bounds); +} + +void DisplayListBuilder::drawPaint() { + OpResult result = PaintResult(current_, kDrawPaintFlags); + if (result != OpResult::kNoEffect && AccumulateUnbounded()) { + Push(0, 1); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawPaint(const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags); + drawPaint(); +} +void DisplayListBuilder::DrawColor(DlColor color, DlBlendMode mode) { + OpResult result = PaintResult(DlPaint(color).setBlendMode(mode)); + if (result != OpResult::kNoEffect && AccumulateUnbounded()) { + Push(0, 1, color, mode); + CheckLayerOpacityCompatibility(mode); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { + SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); + DisplayListAttributeFlags flags = + (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags + : kDrawHVLineFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) { + Push(0, 1, p0, p1); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawLine(const SkPoint& p0, + const SkPoint& p1, + const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags); + drawLine(p0, p1); +} +void DisplayListBuilder::drawRect(const SkRect& rect) { + DisplayListAttributeFlags flags = kDrawRectFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(rect.makeSorted(), flags)) { + Push(0, 1, rect); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawRect(const SkRect& rect, const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags); + drawRect(rect); +} +void DisplayListBuilder::drawOval(const SkRect& bounds) { + DisplayListAttributeFlags flags = kDrawOvalFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(bounds.makeSorted(), flags)) { + Push(0, 1, bounds); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawOval(const SkRect& bounds, const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags); + drawOval(bounds); +} +void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { + DisplayListAttributeFlags flags = kDrawCircleFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect) { + SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius, + center.fX + radius, center.fY + radius); + if (AccumulateOpBounds(bounds, flags)) { + Push(0, 1, center, radius); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } + } +} +void DisplayListBuilder::DrawCircle(const SkPoint& center, + SkScalar radius, + const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags); + drawCircle(center, radius); +} +void DisplayListBuilder::drawRRect(const SkRRect& rrect) { + if (rrect.isRect()) { + drawRect(rrect.rect()); + } else if (rrect.isOval()) { + drawOval(rrect.rect()); + } else { + DisplayListAttributeFlags flags = kDrawRRectFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(rrect.getBounds(), flags)) { + Push(0, 1, rrect); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } + } +} +void DisplayListBuilder::DrawRRect(const SkRRect& rrect, const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags); + drawRRect(rrect); +} +void DisplayListBuilder::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + DisplayListAttributeFlags flags = kDrawDRRectFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(outer.getBounds(), flags)) { + Push(0, 1, outer, inner); + CheckLayerOpacityCompatibility(); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags); + drawDRRect(outer, inner); +} +void DisplayListBuilder::drawPath(const SkPath& path) { + DisplayListAttributeFlags flags = kDrawPathFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect) { + bool is_visible = path.isInverseFillType() + ? AccumulateUnbounded() + : AccumulateOpBounds(path.getBounds(), flags); + if (is_visible) { + Push(0, 1, path); + CheckLayerOpacityHairlineCompatibility(); + UpdateLayerResult(result); + } + } +} +void DisplayListBuilder::DrawPath(const SkPath& path, const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPathFlags); + drawPath(path); +} + +void DisplayListBuilder::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + DisplayListAttributeFlags flags = // + useCenter // + ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags; + OpResult result = PaintResult(current_, flags); + // This could be tighter if we compute where the start and end + // angles are and then also consider the quadrants swept and + // the center if specified. + if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) { + Push(0, 1, bounds, start, sweep, useCenter); + if (useCenter) { + CheckLayerOpacityHairlineCompatibility(); + } else { + CheckLayerOpacityCompatibility(); + } + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter, + const DlPaint& paint) { + SetAttributesFromPaint( + paint, useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags); + drawArc(bounds, start, sweep, useCenter); +} + +DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode( + PointMode mode) { + switch (mode) { + case DlCanvas::PointMode::kPoints: + return kDrawPointsAsPointsFlags; + case PointMode::kLines: + return kDrawPointsAsLinesFlags; + case PointMode::kPolygon: + return kDrawPointsAsPolygonFlags; + } + FML_UNREACHABLE(); +} +void DisplayListBuilder::drawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[]) { + if (count == 0) { + return; + } + DisplayListAttributeFlags flags = FlagsForPointMode(mode); + OpResult result = PaintResult(current_, flags); + if (result == OpResult::kNoEffect) { + return; + } + + FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); + int bytes = count * sizeof(SkPoint); + RectBoundsAccumulator ptBounds; + for (size_t i = 0; i < count; i++) { + ptBounds.accumulate(pts[i]); + } + SkRect point_bounds = ptBounds.bounds(); + if (!AccumulateOpBounds(point_bounds, flags)) { + return; + } + + void* data_ptr; + switch (mode) { + case PointMode::kPoints: + data_ptr = Push(bytes, 1, count); + break; + case PointMode::kLines: + data_ptr = Push(bytes, 1, count); + break; + case PointMode::kPolygon: + data_ptr = Push(bytes, 1, count); + break; + default: + FML_UNREACHABLE(); + return; + } + CopyV(data_ptr, pts, count); + // drawPoints treats every point or line (or segment of a polygon) + // as a completely separate operation meaning we cannot ensure + // distribution of group opacity without analyzing the mode and the + // bounds of every sub-primitive. + // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c + UpdateLayerOpacityCompatibility(false); + UpdateLayerResult(result); +} +void DisplayListBuilder::DrawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[], + const DlPaint& paint) { + SetAttributesFromPaint(paint, FlagsForPointMode(mode)); + drawPoints(mode, count, pts); +} +void DisplayListBuilder::drawVertices(const DlVertices* vertices, + DlBlendMode mode) { + DisplayListAttributeFlags flags = kDrawVerticesFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && + AccumulateOpBounds(vertices->bounds(), flags)) { + void* pod = Push(vertices->size(), 1, mode); + new (pod) DlVertices(vertices); + // DrawVertices applies its colors to the paint so we have no way + // of controlling opacity using the current paint attributes. + // Although, examination of the |mode| might find some predictable + // cases. + UpdateLayerOpacityCompatibility(false); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawVertices(const DlVertices* vertices, + DlBlendMode mode, + const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawVerticesFlags); + drawVertices(vertices, mode); +} + +void DisplayListBuilder::drawImage(const sk_sp image, + const SkPoint point, + DlImageSampling sampling, + bool render_with_attributes) { + DisplayListAttributeFlags flags = render_with_attributes // + ? kDrawImageWithPaintFlags + : kDrawImageFlags; + OpResult result = PaintResult(current_, flags); + if (result == OpResult::kNoEffect) { + return; + } + SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, // + image->width(), image->height()); + if (AccumulateOpBounds(bounds, flags)) { + render_with_attributes + ? Push(0, 1, image, point, sampling) + : Push(0, 1, image, point, sampling); + CheckLayerOpacityCompatibility(render_with_attributes); + UpdateLayerResult(result); + is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); + } +} +void DisplayListBuilder::DrawImage(const sk_sp& image, + const SkPoint point, + DlImageSampling sampling, + const DlPaint* paint) { + if (paint != nullptr) { + SetAttributesFromPaint(*paint, + DisplayListOpFlags::kDrawImageWithPaintFlags); + drawImage(image, point, sampling, true); + } else { + drawImage(image, point, sampling, false); + } +} +void DisplayListBuilder::drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + bool render_with_attributes, + SrcRectConstraint constraint) { + DisplayListAttributeFlags flags = render_with_attributes + ? kDrawImageRectWithPaintFlags + : kDrawImageRectFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) { + Push(0, 1, image, src, dst, sampling, + render_with_attributes, constraint); + CheckLayerOpacityCompatibility(render_with_attributes); + UpdateLayerResult(result); + is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); + } +} +void DisplayListBuilder::DrawImageRect(const sk_sp& image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + const DlPaint* paint, + SrcRectConstraint constraint) { + if (paint != nullptr) { + SetAttributesFromPaint(*paint, + DisplayListOpFlags::kDrawImageRectWithPaintFlags); + drawImageRect(image, src, dst, sampling, true, constraint); + } else { + drawImageRect(image, src, dst, sampling, false, constraint); + } +} +void DisplayListBuilder::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + bool render_with_attributes) { + DisplayListAttributeFlags flags = render_with_attributes + ? kDrawImageNineWithPaintFlags + : kDrawImageNineFlags; + OpResult result = PaintResult(current_, flags); + if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) { + render_with_attributes + ? Push(0, 1, image, center, dst, filter) + : Push(0, 1, image, center, dst, filter); + CheckLayerOpacityCompatibility(render_with_attributes); + UpdateLayerResult(result); + is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); + } +} +void DisplayListBuilder::DrawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + const DlPaint* paint) { + if (paint != nullptr) { + SetAttributesFromPaint(*paint, + DisplayListOpFlags::kDrawImageNineWithPaintFlags); + drawImageNine(image, center, dst, filter, true); + } else { + drawImageNine(image, center, dst, filter, false); + } +} +void DisplayListBuilder::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cull_rect, + bool render_with_attributes) { + DisplayListAttributeFlags flags = render_with_attributes // + ? kDrawAtlasWithPaintFlags + : kDrawAtlasFlags; + OpResult result = PaintResult(current_, flags); + if (result == OpResult::kNoEffect) { + return; + } + SkPoint quad[4]; + RectBoundsAccumulator atlasBounds; + for (int i = 0; i < count; i++) { + const SkRect& src = tex[i]; + xform[i].toQuad(src.width(), src.height(), quad); + for (int j = 0; j < 4; j++) { + atlasBounds.accumulate(quad[j]); + } + } + if (atlasBounds.is_empty() || + !AccumulateOpBounds(atlasBounds.bounds(), flags)) { + return; + } + + int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); + void* data_ptr; + if (colors != nullptr) { + bytes += count * sizeof(DlColor); + if (cull_rect != nullptr) { + data_ptr = + Push(bytes, 1, atlas, count, mode, sampling, true, + *cull_rect, render_with_attributes); + } else { + data_ptr = Push(bytes, 1, atlas, count, mode, sampling, true, + render_with_attributes); + } + CopyV(data_ptr, xform, count, tex, count, colors, count); + } else { + if (cull_rect != nullptr) { + data_ptr = + Push(bytes, 1, atlas, count, mode, sampling, false, + *cull_rect, render_with_attributes); + } else { + data_ptr = Push(bytes, 1, atlas, count, mode, sampling, + false, render_with_attributes); + } + CopyV(data_ptr, xform, count, tex, count); + } + // drawAtlas treats each image as a separate operation so we cannot rely + // on it to distribute the opacity without overlap without checking all + // of the transforms and texture rectangles. + UpdateLayerOpacityCompatibility(false); + UpdateLayerResult(result); + is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe(); +} +void DisplayListBuilder::DrawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cull_rect, + const DlPaint* paint) { + if (paint != nullptr) { + SetAttributesFromPaint(*paint, + DisplayListOpFlags::kDrawAtlasWithPaintFlags); + drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect, + true); + } else { + drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect, + false); + } +} + +void DisplayListBuilder::DrawDisplayList(const sk_sp display_list, + SkScalar opacity) { + if (!SkScalarIsFinite(opacity) || opacity <= SK_ScalarNearlyZero || + display_list->op_count() == 0 || display_list->bounds().isEmpty() || + current_layer_->is_nop_) { + return; + } + const SkRect bounds = display_list->bounds(); + bool accumulated; + switch (accumulator()->type()) { + case BoundsAccumulatorType::kRect: + accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); + break; + case BoundsAccumulatorType::kRTree: + auto rtree = display_list->rtree(); + if (rtree) { + std::list rects = + rtree->searchAndConsolidateRects(bounds, false); + accumulated = false; + for (const SkRect& rect : rects) { + // TODO (https://github.com/flutter/flutter/issues/114919): Attributes + // are not necessarily `kDrawDisplayListFlags`. + if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) { + accumulated = true; + } + } + } else { + accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); + } + break; + } + if (!accumulated) { + return; + } + + DlPaint current_paint = current_; + Push(0, 1, display_list, + opacity < SK_Scalar1 ? opacity : SK_Scalar1); + is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe(); + // Not really necessary if the developer is interacting with us via + // our attribute-state-less DlCanvas methods, but this avoids surprises + // for those who may have been using the stateful Dispatcher methods. + SetAttributesFromPaint(current_paint, + DisplayListOpFlags::kSaveLayerWithPaintFlags); + + // The non-nested op count accumulated in the |Push| method will include + // this call to |drawDisplayList| for non-nested op count metrics. + // But, for nested op count metrics we want the |drawDisplayList| call itself + // to be transparent. So we subtract 1 from our accumulated nested count to + // balance out against the 1 that was accumulated into the regular count. + // This behavior is identical to the way SkPicture computed nested op counts. + nested_op_count_ += display_list->op_count(true) - 1; + nested_bytes_ += display_list->bytes(true); + UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity()); + // Nop DisplayLists are eliminated above so we either affect transparent + // pixels or we do not. We should not have [kNoEffect]. + UpdateLayerResult(display_list->modifies_transparent_black() + ? OpResult::kAffectsAll + : OpResult::kPreservesTransparency); +} +void DisplayListBuilder::DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity) { + FML_LOG(ERROR) << "Cannot draw Impeller Picture in to a DisplayList."; + FML_DCHECK(false); +} +void DisplayListBuilder::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + DisplayListAttributeFlags flags = kDrawTextBlobFlags; + OpResult result = PaintResult(current_, flags); + if (result == OpResult::kNoEffect) { + return; + } + bool unclipped = AccumulateOpBounds(blob->bounds().makeOffset(x, y), flags); + // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the + // unit tests can use Fuchsia's font manager instead of the empty default. + // Until then we might encounter empty bounds for otherwise valid text and + // thus we ignore the results from AccumulateOpBounds. +#if defined(OS_FUCHSIA) + unclipped = true; +#endif // OS_FUCHSIA + if (unclipped) { + Push(0, 1, blob, x, y); + // There is no way to query if the glyphs of a text blob overlap and + // there are no current guarantees from either Skia or Impeller that + // they will protect overlapping glyphs from the effects of overdraw + // so we must make the conservative assessment that this DL layer is + // not compatible with group opacity inheritance. + UpdateLayerOpacityCompatibility(false); + UpdateLayerResult(result); + } +} +void DisplayListBuilder::DrawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y, + const DlPaint& paint) { + SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags); + drawTextBlob(blob, x, y); +} +void DisplayListBuilder::DrawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { + OpResult result = PaintResult(DlPaint(color)); + if (result != OpResult::kNoEffect) { + SkRect shadow_bounds = + DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform()); + if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) { + transparent_occluder // + ? Push(0, 1, path, color, elevation, + dpr) + : Push(0, 1, path, color, elevation, dpr); + UpdateLayerOpacityCompatibility(false); + UpdateLayerResult(result); + } + } +} + +bool DisplayListBuilder::ComputeFilteredBounds(SkRect& bounds, + const DlImageFilter* filter) { + if (filter) { + if (!filter->map_local_bounds(bounds, bounds)) { + return false; + } + } + return true; +} + +bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds, + DisplayListAttributeFlags flags) { + if (flags.ignores_paint()) { + return true; + } + + if (flags.is_geometric()) { + bool is_stroked = flags.is_stroked(current_.getDrawStyle()); + + // Path effect occurs before stroking... + DisplayListSpecialGeometryFlags special_flags = + flags.WithPathEffect(current_.getPathEffectPtr(), is_stroked); + if (current_.getPathEffect()) { + auto effect_bounds = current_.getPathEffect()->effect_bounds(bounds); + if (!effect_bounds.has_value()) { + return false; + } + bounds = effect_bounds.value(); + } + + if (is_stroked) { + // Determine the max multiplier to the stroke width first. + SkScalar pad = 1.0f; + if (current_.getStrokeJoin() == DlStrokeJoin::kMiter && + special_flags.may_have_acute_joins()) { + pad = std::max(pad, current_.getStrokeMiter()); + } + if (current_.getStrokeCap() == DlStrokeCap::kSquare && + special_flags.may_have_diagonal_caps()) { + pad = std::max(pad, SK_ScalarSqrt2); + } + SkScalar min_stroke_width = 0.01; + pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width); + bounds.outset(pad, pad); + } + } + + if (flags.applies_mask_filter()) { + auto filter = current_.getMaskFilter(); + if (filter) { + switch (filter->type()) { + case DlMaskFilterType::kBlur: { + FML_DCHECK(filter->asBlur()); + SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0; + bounds.outset(mask_sigma_pad, mask_sigma_pad); + } + } + } + } + + if (flags.applies_image_filter()) { + return ComputeFilteredBounds(bounds, current_.getImageFilter().get()); + } + + return true; +} + +bool DisplayListBuilder::AccumulateUnbounded() { + SkRect clip = tracker_.device_cull_rect(); + if (clip.isEmpty()) { + return false; + } + accumulator()->accumulate(clip, op_index_); + return true; +} + +bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds, + DisplayListAttributeFlags flags) { + if (AdjustBoundsForPaint(bounds, flags)) { + return AccumulateBounds(bounds); + } else { + return AccumulateUnbounded(); + } +} +bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) { + if (!bounds.isEmpty()) { + tracker_.mapRect(&bounds); + if (bounds.intersect(tracker_.device_cull_rect())) { + accumulator()->accumulate(bounds, op_index_); + return true; + } + } + return false; +} + +bool DisplayListBuilder::paint_nops_on_transparency() { + // SkImageFilter::canComputeFastBounds tests for transparency behavior + // This test assumes that the blend mode checked down below will + // NOP on transparent black. + if (current_.getImageFilterPtr() && + current_.getImageFilterPtr()->modifies_transparent_black()) { + return false; + } + + // We filter the transparent black that is used for the background of a + // saveLayer and make sure it returns transparent black. If it does, then + // the color filter will leave all area surrounding the contents of the + // save layer untouched out to the edge of the output surface. + // This test assumes that the blend mode checked down below will + // NOP on transparent black. + if (current_.getColorFilterPtr() && + current_.getColorFilterPtr()->modifies_transparent_black()) { + return false; + } + + // Unusual blendmodes require us to process a saved layer + // even with operations outside the clip. + // For example, DstIn is used by masking layers. + // https://code.google.com/p/skia/issues/detail?id=1291 + // https://crbug.com/401593 + switch (current_.getBlendMode()) { + // For each of the following transfer modes, if the source + // alpha is zero (our transparent black), the resulting + // blended pixel is not necessarily equal to the original + // destination pixel. + // Mathematically, any time in the following equations where + // the result is not d assuming source is 0 + case DlBlendMode::kClear: // r = 0 + case DlBlendMode::kSrc: // r = s + case DlBlendMode::kSrcIn: // r = s * da + case DlBlendMode::kDstIn: // r = d * sa + case DlBlendMode::kSrcOut: // r = s * (1-da) + case DlBlendMode::kDstATop: // r = d*sa + s*(1-da) + case DlBlendMode::kModulate: // r = s*d + return false; + break; + + // And in these equations, the result must be d if the + // source is 0 + case DlBlendMode::kDst: // r = d + case DlBlendMode::kSrcOver: // r = s + (1-sa)*d + case DlBlendMode::kDstOver: // r = d + (1-da)*s + case DlBlendMode::kDstOut: // r = d * (1-sa) + case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa) + case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa) + case DlBlendMode::kPlus: // r = min(s + d, 1) + case DlBlendMode::kScreen: // r = s + d - s*d + case DlBlendMode::kOverlay: // multiply or screen, depending on dest + case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa), + // ra = kSrcOver + case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa), + // ra = kSrcOver + case DlBlendMode::kColorDodge: // brighten destination to reflect source + case DlBlendMode::kColorBurn: // darken destination to reflect source + case DlBlendMode::kHardLight: // multiply or screen, depending on source + case DlBlendMode::kSoftLight: // lighten or darken, depending on source + case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)), + // ra = kSrcOver + case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver + case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d + case DlBlendMode::kHue: // ra = kSrcOver + case DlBlendMode::kSaturation: // ra = kSrcOver + case DlBlendMode::kColor: // ra = kSrcOver + case DlBlendMode::kLuminosity: // ra = kSrcOver + return true; + break; + } +} + +DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint, + DisplayListAttributeFlags flags) { + DlColor color; + if (flags.applies_color()) { + const DlColorSource* source = paint.getColorSourcePtr(); + if (source) { + if (source->asColor()) { + color = source->asColor()->color(); + } else { + color = source->is_opaque() ? DlColor::kBlack() : kAnyColor; + } + } else { + color = paint.getColor(); + } + } else if (flags.applies_alpha()) { + // If the operation applies alpha, but not color, then the only impact + // of the alpha is to modulate the output towards transparency. + // We can not guarantee an opaque source even if the alpha is opaque + // since that would require knowing something about the colors that + // the alpha is modulating, but we can guarantee a transparent source + // if the alpha is 0. + color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor; + } else { + color = kAnyColor; + } + if (flags.applies_image_filter()) { + auto filter = paint.getImageFilterPtr(); + if (filter) { + if (!color.isTransparent() || filter->modifies_transparent_black()) { + color = kAnyColor; + } + } + } + if (flags.applies_color_filter()) { + auto filter = paint.getColorFilterPtr(); + if (filter) { + if (!color.isTransparent() || filter->modifies_transparent_black()) { + color = kAnyColor; + } + } + } + return color; +} + +DisplayListBuilder::OpResult DisplayListBuilder::PaintResult( + const DlPaint& paint, + DisplayListAttributeFlags flags) { + if (current_layer_->is_nop_) { + return OpResult::kNoEffect; + } + if (flags.applies_blend()) { + switch (paint.getBlendMode()) { + // Nop blend mode (singular, there is only one) + case DlBlendMode::kDst: + return OpResult::kNoEffect; + + // Always clears pixels blend mode (singular, there is only one) + case DlBlendMode::kClear: + return OpResult::kPreservesTransparency; + + case DlBlendMode::kHue: + case DlBlendMode::kSaturation: + case DlBlendMode::kColor: + case DlBlendMode::kLuminosity: + case DlBlendMode::kColorBurn: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kNoEffect + : OpResult::kAffectsAll; + + // kSrcIn modifies pixels towards transparency + case DlBlendMode::kSrcIn: + return OpResult::kPreservesTransparency; + + // These blend modes preserve destination alpha + case DlBlendMode::kSrcATop: + case DlBlendMode::kDstOut: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kNoEffect + : OpResult::kPreservesTransparency; + + // Always destructive blend modes, potentially not affecting transparency + case DlBlendMode::kSrc: + case DlBlendMode::kSrcOut: + case DlBlendMode::kDstATop: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kPreservesTransparency + : OpResult::kAffectsAll; + + // The kDstIn blend mode modifies the destination unless the + // source color is opaque. + case DlBlendMode::kDstIn: + return GetEffectiveColor(paint, flags).isOpaque() + ? OpResult::kNoEffect + : OpResult::kPreservesTransparency; + + // The next group of blend modes modifies the destination unless the + // source color is transparent. + case DlBlendMode::kSrcOver: + case DlBlendMode::kDstOver: + case DlBlendMode::kXor: + case DlBlendMode::kPlus: + case DlBlendMode::kScreen: + case DlBlendMode::kMultiply: + case DlBlendMode::kOverlay: + case DlBlendMode::kDarken: + case DlBlendMode::kLighten: + case DlBlendMode::kColorDodge: + case DlBlendMode::kHardLight: + case DlBlendMode::kSoftLight: + case DlBlendMode::kDifference: + case DlBlendMode::kExclusion: + return GetEffectiveColor(paint, flags).isTransparent() + ? OpResult::kNoEffect + : OpResult::kAffectsAll; + + // Modulate only leaves the pixel alone when the source is white. + case DlBlendMode::kModulate: + return GetEffectiveColor(paint, flags) == DlColor::kWhite() + ? OpResult::kNoEffect + : OpResult::kPreservesTransparency; + } + } + return OpResult::kAffectsAll; +} + +} // namespace flutter diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h new file mode 100644 index 0000000000000..617ede76f9a3d --- /dev/null +++ b/display_list/dl_builder.h @@ -0,0 +1,788 @@ +// 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. + +#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/dl_blend_mode.h" +#include "flutter/display_list/dl_canvas.h" +#include "flutter/display_list/dl_op_flags.h" +#include "flutter/display_list/dl_op_receiver.h" +#include "flutter/display_list/dl_paint.h" +#include "flutter/display_list/dl_sampling_options.h" +#include "flutter/display_list/effects/dl_path_effect.h" +#include "flutter/display_list/image/dl_image.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" +#include "flutter/display_list/utils/dl_comparable.h" +#include "flutter/display_list/utils/dl_matrix_clip_tracker.h" +#include "flutter/fml/macros.h" + +namespace impeller { +struct Picture; +} // namespace impeller + +namespace flutter { + +// The primary class used to build a display list. The list of methods +// here matches the list of methods invoked on a |DlOpReceiver| combined +// with the list of methods invoked on a |DlCanvas|. +class DisplayListBuilder final : public virtual DlCanvas, + public SkRefCnt, + virtual DlOpReceiver, + DisplayListOpFlags { + public: + static constexpr SkRect kMaxCullRect = + SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); + + explicit DisplayListBuilder(bool prepare_rtree) + : DisplayListBuilder(kMaxCullRect, prepare_rtree) {} + + explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect, + bool prepare_rtree = false); + + ~DisplayListBuilder(); + + // |DlCanvas| + SkISize GetBaseLayerSize() const override; + // |DlCanvas| + SkImageInfo GetImageInfo() const override; + + // |DlCanvas| + void Save() override; + + // |DlCanvas| + void SaveLayer(const SkRect* bounds, + const DlPaint* paint = nullptr, + const DlImageFilter* backdrop = nullptr) override; + // |DlCanvas| + void Restore() override; + // |DlCanvas| + int GetSaveCount() const override { return layer_stack_.size(); } + // |DlCanvas| + void RestoreToCount(int restore_count) override; + + // |DlCanvas| + void Translate(SkScalar tx, SkScalar ty) override; + // |DlCanvas| + void Scale(SkScalar sx, SkScalar sy) override; + // |DlCanvas| + void Rotate(SkScalar degrees) override; + // |DlCanvas| + void Skew(SkScalar sx, SkScalar sy) override; + + // clang-format off + // 2x3 2D affine subset of a 4x4 transform in row major order + // |DlCanvas| + void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) override; + // full 4x4 transform in row major order + // |DlCanvas| + void TransformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; + // clang-format on + // |DlCanvas| + void TransformReset() override; + // |DlCanvas| + void Transform(const SkMatrix* matrix) override; + // |DlCanvas| + void Transform(const SkM44* matrix44) override; + // |DlCanvas| + void SetTransform(const SkMatrix* matrix) override { + TransformReset(); + Transform(matrix); + } + // |DlCanvas| + void SetTransform(const SkM44* matrix44) override { + TransformReset(); + Transform(matrix44); + } + using DlCanvas::Transform; + + /// Returns the 4x4 full perspective transform representing all transform + /// operations executed so far in this DisplayList within the enclosing + /// save stack. + // |DlCanvas| + SkM44 GetTransformFullPerspective() const override { + return tracker_.matrix_4x4(); + } + /// Returns the 3x3 partial perspective transform representing all transform + /// operations executed so far in this DisplayList within the enclosing + /// save stack. + // |DlCanvas| + SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); } + + // |DlCanvas| + void ClipRect(const SkRect& rect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| + void ClipRRect(const SkRRect& rrect, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + // |DlCanvas| + void ClipPath(const SkPath& path, + ClipOp clip_op = ClipOp::kIntersect, + bool is_aa = false) override; + + /// Conservative estimate of the bounds of all outstanding clip operations + /// measured in the coordinate space within which this DisplayList will + /// be rendered. + // |DlCanvas| + SkRect GetDestinationClipBounds() const override { + return tracker_.device_cull_rect(); + } + /// Conservative estimate of the bounds of all outstanding clip operations + /// transformed into the local coordinate space in which currently + /// recorded rendering operations are interpreted. + // |DlCanvas| + SkRect GetLocalClipBounds() const override { + return tracker_.local_cull_rect(); + } + + /// Return true iff the supplied bounds are easily shown to be outside + /// of the current clip bounds. This method may conservatively return + /// false if it cannot make the determination. + // |DlCanvas| + bool QuickReject(const SkRect& bounds) const override; + + // |DlCanvas| + void DrawPaint(const DlPaint& paint) override; + // |DlCanvas| + void DrawColor(DlColor color, DlBlendMode mode) override; + // |DlCanvas| + void DrawLine(const SkPoint& p0, + const SkPoint& p1, + const DlPaint& paint) override; + // |DlCanvas| + void DrawRect(const SkRect& rect, const DlPaint& paint) override; + // |DlCanvas| + void DrawOval(const SkRect& bounds, const DlPaint& paint) override; + // |DlCanvas| + void DrawCircle(const SkPoint& center, + SkScalar radius, + const DlPaint& paint) override; + // |DlCanvas| + void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override; + // |DlCanvas| + void DrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const DlPaint& paint) override; + // |DlCanvas| + void DrawPath(const SkPath& path, const DlPaint& paint) override; + // |DlCanvas| + void DrawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter, + const DlPaint& paint) override; + // |DlCanvas| + void DrawPoints(PointMode mode, + uint32_t count, + const SkPoint pts[], + const DlPaint& paint) override; + // |DlCanvas| + void DrawVertices(const DlVertices* vertices, + DlBlendMode mode, + const DlPaint& paint) override; + using DlCanvas::DrawVertices; + // |DlCanvas| + void DrawImage(const sk_sp& image, + const SkPoint point, + DlImageSampling sampling, + const DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawImageRect( + const sk_sp& image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + const DlPaint* paint = nullptr, + SrcRectConstraint constraint = SrcRectConstraint::kFast) override; + using DlCanvas::DrawImageRect; + // |DlCanvas| + void DrawImageNine(const sk_sp& image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + const DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawAtlas(const sk_sp& atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cullRect, + const DlPaint* paint = nullptr) override; + // |DlCanvas| + void DrawDisplayList(const sk_sp display_list, + SkScalar opacity = SK_Scalar1) override; + // |DlCanvas| + void DrawImpellerPicture( + const std::shared_ptr& picture, + SkScalar opacity = SK_Scalar1) override; + // |DlCanvas| + void DrawTextBlob(const sk_sp& blob, + SkScalar x, + SkScalar y, + const DlPaint& paint) override; + // |DlCanvas| + void DrawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override; + + // |DlCanvas| + void Flush() override {} + + sk_sp Build(); + + private: + // This method exposes the internal stateful DlOpReceiver implementation + // of the DisplayListBuilder, primarily for testing purposes. Its use + // is obsolete and forbidden in every other case and is only shared to a + // pair of "friend" accessors in the benchmark/unittest files. + DlOpReceiver& asReceiver() { return *this; } + + friend DlOpReceiver& DisplayListBuilderBenchmarkAccessor( + DisplayListBuilder& builder); + friend DlOpReceiver& DisplayListBuilderTestingAccessor( + DisplayListBuilder& builder); + friend DlPaint DisplayListBuilderTestingAttributes( + DisplayListBuilder& builder); + + void SetAttributesFromPaint(const DlPaint& paint, + const DisplayListAttributeFlags flags); + + // |DlOpReceiver| + void setAntiAlias(bool aa) override { + if (current_.isAntiAlias() != aa) { + onSetAntiAlias(aa); + } + } + // |DlOpReceiver| + void setDither(bool dither) override { + if (current_.isDither() != dither) { + onSetDither(dither); + } + } + // |DlOpReceiver| + void setInvertColors(bool invert) override { + if (current_.isInvertColors() != invert) { + onSetInvertColors(invert); + } + } + // |DlOpReceiver| + void setStrokeCap(DlStrokeCap cap) override { + if (current_.getStrokeCap() != cap) { + onSetStrokeCap(cap); + } + } + // |DlOpReceiver| + void setStrokeJoin(DlStrokeJoin join) override { + if (current_.getStrokeJoin() != join) { + onSetStrokeJoin(join); + } + } + // |DlOpReceiver| + void setDrawStyle(DlDrawStyle style) override { + if (current_.getDrawStyle() != style) { + onSetDrawStyle(style); + } + } + // |DlOpReceiver| + void setStrokeWidth(float width) override { + if (current_.getStrokeWidth() != width) { + onSetStrokeWidth(width); + } + } + // |DlOpReceiver| + void setStrokeMiter(float limit) override { + if (current_.getStrokeMiter() != limit) { + onSetStrokeMiter(limit); + } + } + // |DlOpReceiver| + void setColor(DlColor color) override { + if (current_.getColor() != color) { + onSetColor(color); + } + } + // |DlOpReceiver| + void setBlendMode(DlBlendMode mode) override { + if (current_.getBlendMode() != mode) { + onSetBlendMode(mode); + } + } + // |DlOpReceiver| + void setColorSource(const DlColorSource* source) override { + if (NotEquals(current_.getColorSource(), source)) { + onSetColorSource(source); + } + } + // |DlOpReceiver| + void setImageFilter(const DlImageFilter* filter) override { + if (NotEquals(current_.getImageFilter(), filter)) { + onSetImageFilter(filter); + } + } + // |DlOpReceiver| + void setColorFilter(const DlColorFilter* filter) override { + if (NotEquals(current_.getColorFilter(), filter)) { + onSetColorFilter(filter); + } + } + // |DlOpReceiver| + void setPathEffect(const DlPathEffect* effect) override { + if (NotEquals(current_.getPathEffect(), effect)) { + onSetPathEffect(effect); + } + } + // |DlOpReceiver| + void setMaskFilter(const DlMaskFilter* filter) override { + if (NotEquals(current_.getMaskFilter(), filter)) { + onSetMaskFilter(filter); + } + } + + DlPaint CurrentAttributes() const { return current_; } + + // |DlOpReceiver| + void save() override { Save(); } + // Only the |renders_with_attributes()| option will be accepted here. Any + // other flags will be ignored and calculated anew as the DisplayList is + // built. Alternatively, use the |saveLayer(SkRect, bool)| method. + // |DlOpReceiver| + void saveLayer(const SkRect* bounds, + const SaveLayerOptions options, + const DlImageFilter* backdrop) override; + // |DlOpReceiver| + void restore() override { Restore(); } + + // |DlOpReceiver| + void translate(SkScalar tx, SkScalar ty) override { Translate(tx, ty); } + // |DlOpReceiver| + void scale(SkScalar sx, SkScalar sy) override { Scale(sx, sy); } + // |DlOpReceiver| + void rotate(SkScalar degrees) override { Rotate(degrees); } + // |DlOpReceiver| + void skew(SkScalar sx, SkScalar sy) override { Skew(sx, sy); } + + // clang-format off + // |DlOpReceiver| + void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) override { + Transform2DAffine(mxx, mxy, mxt, myx, myy, myt); + } + // |DlOpReceiver| + void transformFullPerspective( + SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, + SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override { + TransformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + } + // clang-format off + // |DlOpReceiver| + void transformReset() override { TransformReset(); } + + // |DlOpReceiver| + void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override { + ClipRect(rect, clip_op, is_aa); + } + // |DlOpReceiver| + void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override { + ClipRRect(rrect, clip_op, is_aa); + } + // |DlOpReceiver| + void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override { + ClipPath(path, clip_op, is_aa); + } + + // |DlOpReceiver| + void drawPaint() override; + // |DlOpReceiver| + void drawColor(DlColor color, DlBlendMode mode) override { + DrawColor(color, mode); + } + // |DlOpReceiver| + void drawLine(const SkPoint& p0, const SkPoint& p1) override; + // |DlOpReceiver| + void drawRect(const SkRect& rect) override; + // |DlOpReceiver| + void drawOval(const SkRect& bounds) override; + // |DlOpReceiver| + void drawCircle(const SkPoint& center, SkScalar radius) override; + // |DlOpReceiver| + void drawRRect(const SkRRect& rrect) override; + // |DlOpReceiver| + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + // |DlOpReceiver| + void drawPath(const SkPath& path) override; + // |DlOpReceiver| + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + // |DlOpReceiver| + void drawPoints(PointMode mode, uint32_t count, const SkPoint pts[]) override; + // |DlOpReceiver| + void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; + + // |DlOpReceiver| + void drawImage(const sk_sp image, + const SkPoint point, + DlImageSampling sampling, + bool render_with_attributes) override; + // |DlOpReceiver| + void drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + bool render_with_attributes, + SrcRectConstraint constraint = SrcRectConstraint::kFast) override; + // |DlOpReceiver| + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + bool render_with_attributes) override; + // |DlOpReceiver| + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const DlColor colors[], + int count, + DlBlendMode mode, + DlImageSampling sampling, + const SkRect* cullRect, + bool render_with_attributes) override; + + // |DlOpReceiver| + void drawDisplayList(const sk_sp display_list, + SkScalar opacity) override { + DrawDisplayList(display_list, opacity); + } + // |DlOpReceiver| + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + // |DlOpReceiver| + void drawShadow(const SkPath& path, + const DlColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override { + DrawShadow(path, color, elevation, transparent_occluder, dpr); + } + + void checkForDeferredSave(); + + DisplayListStorage storage_; + size_t used_ = 0; + size_t allocated_ = 0; + int render_op_count_ = 0; + int op_index_ = 0; + + // bytes and ops from |drawPicture| and |drawDisplayList| + size_t nested_bytes_ = 0; + int nested_op_count_ = 0; + + bool is_ui_thread_safe_ = true; + + template + void* Push(size_t extra, int op_inc, Args&&... args); + + void intersect(const SkRect& rect); + + // kInvalidSigma is used to indicate that no MaskBlur is currently set. + static constexpr SkScalar kInvalidSigma = 0.0; + static bool mask_sigma_valid(SkScalar sigma) { + return SkScalarIsFinite(sigma) && sigma > 0.0; + } + + class LayerInfo { + public: + explicit LayerInfo( + size_t save_offset = 0, + bool has_layer = false, + const std::shared_ptr& filter = nullptr) + : save_offset_(save_offset), + has_layer_(has_layer), + filter_(filter) {} + + // The offset into the memory buffer where the saveLayer DLOp record + // for this saveLayer() call is placed. This may be needed if the + // eventual restore() call has discovered important information about + // the records inside the saveLayer that may impact how the saveLayer + // is handled (e.g., |cannot_inherit_opacity| == false). + // This offset is only valid if |has_layer| is true. + size_t save_offset() const { return save_offset_; } + + bool has_layer() const { return has_layer_; } + bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; } + bool has_compatible_op() const { return has_compatible_op_; } + bool affects_transparent_layer() const { + return affects_transparent_layer_; + } + + bool is_group_opacity_compatible() const { + return !cannot_inherit_opacity_; + } + + void mark_incompatible() { cannot_inherit_opacity_ = true; } + + // For now this only allows a single compatible op to mark the + // layer as being compatible with group opacity. If we start + // computing bounds of ops in the Builder methods then we + // can upgrade this to checking for overlapping ops. + // See https://github.com/flutter/flutter/issues/93899 + void add_compatible_op() { + if (!cannot_inherit_opacity_) { + if (has_compatible_op_) { + cannot_inherit_opacity_ = true; + } else { + has_compatible_op_ = true; + } + } + } + + // Records that the current layer contains an op that produces visible + // output on a transparent surface. + void add_visible_op() { + affects_transparent_layer_ = true; + } + + // The filter to apply to the layer bounds when it is restored + std::shared_ptr filter() { return filter_; } + + // is_unbounded should be set to true if we ever encounter an operation + // on a layer that either is unrestricted (|drawColor| or |drawPaint|) + // or cannot compute its bounds (some effects and filters) and there + // was no outstanding clip op at the time. + // When the layer is restored, the outer layer may then process this + // unbounded state by accumulating its own clip or transferring the + // unbounded state to its own outer layer. + // Typically the DisplayList will have been constructed with a cull + // rect which will act as a default clip for the outermost layer and + // the unbounded state of all sub layers will eventually be caught by + // that cull rect so that the overall unbounded state of the entire + // DisplayList will never be true. + // + // For historical consistency it is worth noting that SkPicture used + // to treat these same conditions as a Nop (they accumulate the + // SkPicture cull rect, but if no cull rect was specified then it is + // an empty Rect and so has no effect on the bounds). + // + // Flutter is unlikely to ever run into this as the Dart mechanisms + // all supply a non-null cull rect for all Dart Picture objects, + // even if that cull rect is kGiantRect. + void set_unbounded() { is_unbounded_ = true; } + + // |is_unbounded| should be called after |getLayerBounds| in case + // a problem was found during the computation of those bounds, + // the layer will have one last chance to flag an unbounded state. + bool is_unbounded() const { return is_unbounded_; } + + private: + size_t save_offset_; + bool has_layer_; + bool cannot_inherit_opacity_ = false; + bool has_compatible_op_ = false; + std::shared_ptr filter_; + bool is_unbounded_ = false; + bool has_deferred_save_op_ = false; + bool is_nop_ = false; + bool affects_transparent_layer_ = false; + + friend class DisplayListBuilder; + }; + + std::vector layer_stack_; + LayerInfo* current_layer_; + DisplayListMatrixClipTracker tracker_; + std::unique_ptr accumulator_; + BoundsAccumulator* accumulator() { return accumulator_.get(); } + + // This flag indicates whether or not the current rendering attributes + // are compatible with rendering ops applying an inherited opacity. + bool current_opacity_compatibility_ = true; + + // Returns the compatibility of a given blend mode for applying an + // inherited opacity value to modulate the visibility of the op. + // For now we only accept SrcOver blend modes but this could be expanded + // in the future to include other (rarely used) modes that also modulate + // the opacity of a rendering operation at the cost of a switch statement + // or lookup table. + static bool IsOpacityCompatible(DlBlendMode mode) { + return (mode == DlBlendMode::kSrcOver); + } + + void UpdateCurrentOpacityCompatibility() { + current_opacity_compatibility_ = // + current_.getColorFilter() == nullptr && // + !current_.isInvertColors() && // + IsOpacityCompatible(current_.getBlendMode()); + } + + // Update the opacity compatibility flags of the current layer for an op + // that has determined its compatibility as indicated by |compatible|. + void UpdateLayerOpacityCompatibility(bool compatible) { + if (compatible) { + current_layer_->add_compatible_op(); + } else { + current_layer_->mark_incompatible(); + } + } + + // Check for opacity compatibility for an op that may or may not use the + // current rendering attributes as indicated by |uses_blend_attribute|. + // If the flag is false then the rendering op will be able to substitute + // a default Paint object with the opacity applied using the default SrcOver + // blend mode which is always compatible with applying an inherited opacity. + void CheckLayerOpacityCompatibility(bool uses_blend_attribute = true) { + UpdateLayerOpacityCompatibility(!uses_blend_attribute || + current_opacity_compatibility_); + } + + void CheckLayerOpacityHairlineCompatibility() { + UpdateLayerOpacityCompatibility( + current_opacity_compatibility_ && + (current_.getDrawStyle() == DlDrawStyle::kFill || + current_.getStrokeWidth() > 0)); + } + + // Check for opacity compatibility for an op that ignores the current + // attributes and uses the indicated blend |mode| to render to the layer. + // This is only used by |drawColor| currently. + void CheckLayerOpacityCompatibility(DlBlendMode mode) { + UpdateLayerOpacityCompatibility(IsOpacityCompatible(mode)); + } + + void onSetAntiAlias(bool aa); + void onSetDither(bool dither); + void onSetInvertColors(bool invert); + void onSetStrokeCap(DlStrokeCap cap); + void onSetStrokeJoin(DlStrokeJoin join); + void onSetDrawStyle(DlDrawStyle style); + void onSetStrokeWidth(SkScalar width); + void onSetStrokeMiter(SkScalar limit); + void onSetColor(DlColor color); + void onSetBlendMode(DlBlendMode mode); + void onSetColorSource(const DlColorSource* source); + void onSetImageFilter(const DlImageFilter* filter); + void onSetColorFilter(const DlColorFilter* filter); + void onSetPathEffect(const DlPathEffect* effect); + void onSetMaskFilter(const DlMaskFilter* filter); + + // The DisplayList had an unbounded call with no cull rect or clip + // to contain it. Should only be called after the stream is fully + // built. + // Unbounded operations are calls like |drawColor| which are defined + // to flood the entire surface, or calls that relied on a rendering + // attribute which is unable to compute bounds (should be rare). + // In those cases the bounds will represent only the accumulation + // of the bounded calls and this flag will be set to indicate that + // condition. + bool is_unbounded() const { + FML_DCHECK(layer_stack_.size() == 1); + return layer_stack_.front().is_unbounded(); + } + + SkRect bounds() const { + FML_DCHECK(layer_stack_.size() == 1); + if (is_unbounded()) { + FML_LOG(INFO) << "returning partial bounds for unbounded DisplayList"; + } + + return accumulator_->bounds(); + } + + std::shared_ptr rtree() { + FML_DCHECK(layer_stack_.size() == 1); + if (is_unbounded()) { + FML_LOG(INFO) << "returning partial rtree for unbounded DisplayList"; + } + + return accumulator_->rtree(); + } + + static DisplayListAttributeFlags FlagsForPointMode(PointMode mode); + + enum class OpResult { + kNoEffect, + kPreservesTransparency, + kAffectsAll, + }; + + bool paint_nops_on_transparency(); + OpResult PaintResult(const DlPaint& paint, + DisplayListAttributeFlags flags = kDrawPaintFlags); + + void UpdateLayerResult(OpResult result) { + switch (result) { + case OpResult::kNoEffect: + case OpResult::kPreservesTransparency: + break; + case OpResult::kAffectsAll: + current_layer_->add_visible_op(); + break; + } + } + + // kAnyColor is a non-opaque and non-transparent color that will not + // trigger any short-circuit tests about the results of a blend. + static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlpha(0x80); + static_assert(!kAnyColor.isOpaque()); + static_assert(!kAnyColor.isTransparent()); + static DlColor GetEffectiveColor(const DlPaint& paint, + DisplayListAttributeFlags flags); + + // Computes the bounds of an operation adjusted for a given ImageFilter + // and returns whether the computation was possible. If the method + // returns false then the caller should assume the worst about the bounds. + static bool ComputeFilteredBounds(SkRect& bounds, + const DlImageFilter* filter); + + // Adjusts the indicated bounds for the given flags and returns true if + // the calculation was possible, or false if it could not be estimated. + bool AdjustBoundsForPaint(SkRect& bounds, DisplayListAttributeFlags flags); + + // Records the fact that we encountered an op that either could not + // estimate its bounds or that fills all of the destination space. + bool AccumulateUnbounded(); + + // Records the bounds for an op after modifying them according to the + // supplied attribute flags and transforming by the current matrix. + bool AccumulateOpBounds(const SkRect& bounds, + DisplayListAttributeFlags flags) { + SkRect safe_bounds = bounds; + return AccumulateOpBounds(safe_bounds, flags); + } + + // Records the bounds for an op after modifying them according to the + // supplied attribute flags and transforming by the current matrix + // and clipping against the current clip. + bool AccumulateOpBounds(SkRect& bounds, DisplayListAttributeFlags flags); + + // Records the given bounds after transforming by the current matrix + // and clipping against the current clip. + bool AccumulateBounds(SkRect& bounds); + + DlPaint current_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h index c3346d322ca6d..fdee3121421dd 100644 --- a/display_list/dl_canvas.h +++ b/display_list/dl_canvas.h @@ -161,7 +161,7 @@ class DlCanvas { virtual void DrawVertices(const DlVertices* vertices, DlBlendMode mode, const DlPaint& paint) = 0; - void DrawVertices(const std::shared_ptr vertices, + void DrawVertices(const std::shared_ptr& vertices, DlBlendMode mode, const DlPaint& paint) { DrawVertices(vertices.get(), mode, paint); diff --git a/display_list/dl_canvas_to_receiver.cc b/display_list/dl_canvas_to_receiver.cc deleted file mode 100644 index aed34a62c189d..0000000000000 --- a/display_list/dl_canvas_to_receiver.cc +++ /dev/null @@ -1,1158 +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. - -#include "flutter/display_list/dl_canvas_to_receiver.h" - -#include "flutter/display_list/display_list.h" -#include "flutter/display_list/dl_blend_mode.h" -#include "flutter/display_list/dl_op_flags.h" -#include "flutter/display_list/dl_op_records.h" -#include "flutter/display_list/effects/dl_color_source.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" -#include "fml/logging.h" -#include "third_party/skia/include/core/SkScalar.h" - -namespace flutter { - -DlCanvasToReceiver::DlCanvasToReceiver( - std::shared_ptr receiver) - : receiver_(std::move(receiver)) { - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); -} - -SkISize DlCanvasToReceiver::GetBaseLayerSize() const { - CheckAlive(); - return receiver_->base_device_cull_rect().roundOut().size(); -} - -SkImageInfo DlCanvasToReceiver::GetImageInfo() const { - CheckAlive(); - SkISize size = GetBaseLayerSize(); - return SkImageInfo::MakeUnknown(size.width(), size.height()); -} - -bool DlCanvasToReceiver::SetAttributesFromPaint( - const DlPaint* paint, - const DisplayListAttributeFlags flags) { - if (paint == nullptr) { - return true; - } - - bool can_inherit_opacity = true; - if (flags.applies_anti_alias()) { - bool aa = paint->isAntiAlias(); - if (current_.isAntiAlias() != aa) { - current_.setAntiAlias(aa); - receiver_->setAntiAlias(aa); - } - } - if (flags.applies_dither()) { - bool dither = paint->isDither(); - if (current_.isDither() != dither) { - current_.setDither(dither); - receiver_->setDither(dither); - } - } - if (flags.applies_alpha_or_color()) { - DlColor color = paint->getColor(); - if (current_.getColor() != color) { - current_.setColor(color); - receiver_->setColor(color); - } - } - if (flags.applies_blend()) { - DlBlendMode mode = paint->getBlendMode(); - if (current_.getBlendMode() != mode) { - current_.setBlendMode(mode); - receiver_->setBlendMode(mode); - } - if (!IsOpacityCompatible(mode)) { - can_inherit_opacity = false; - } - } - if (flags.applies_style()) { - DlDrawStyle style = paint->getDrawStyle(); - if (current_.getDrawStyle() != style) { - current_.setDrawStyle(style); - receiver_->setDrawStyle(style); - } - } - if (flags.is_stroked(paint->getDrawStyle())) { - SkScalar width = paint->getStrokeWidth(); - if (current_.getStrokeWidth() != width) { - current_.setStrokeWidth(width); - receiver_->setStrokeWidth(width); - } - if (flags.may_have_trouble_with_hairlines() && width <= 0) { - can_inherit_opacity = false; - } - SkScalar miter = paint->getStrokeMiter(); - if (current_.getStrokeMiter() != miter) { - current_.setStrokeMiter(miter); - receiver_->setStrokeMiter(miter); - } - DlStrokeCap cap = paint->getStrokeCap(); - if (current_.getStrokeCap() != cap) { - current_.setStrokeCap(cap); - receiver_->setStrokeCap(cap); - } - DlStrokeJoin join = paint->getStrokeJoin(); - if (current_.getStrokeJoin() != join) { - current_.setStrokeJoin(join); - receiver_->setStrokeJoin(join); - } - } - if (flags.applies_shader()) { - auto source = paint->getColorSourcePtr(); - if (NotEquals(current_.getColorSourcePtr(), source)) { - current_.setColorSource(paint->getColorSource()); - receiver_->setColorSource(source); - } - } - if (flags.applies_color_filter()) { - bool invert = paint->isInvertColors(); - if (current_.isInvertColors() != invert) { - current_.setInvertColors(invert); - receiver_->setInvertColors(invert); - } - auto filter = paint->getColorFilterPtr(); - if (NotEquals(current_.getColorFilterPtr(), filter)) { - current_.setColorFilter(paint->getColorFilter()); - receiver_->setColorFilter(filter); - } - if (invert || filter) { - can_inherit_opacity = false; - } - } - if (flags.applies_image_filter()) { - auto filter = paint->getImageFilterPtr(); - if (NotEquals(current_.getImageFilterPtr(), filter)) { - current_.setImageFilter(paint->getImageFilter()); - receiver_->setImageFilter(filter); - } - } - if (flags.applies_path_effect()) { - auto effect = paint->getPathEffectPtr(); - if (NotEquals(current_.getPathEffectPtr(), effect)) { - current_.setPathEffect(paint->getPathEffect()); - receiver_->setPathEffect(effect); - } - } - if (flags.applies_mask_filter()) { - auto filter = paint->getMaskFilterPtr(); - if (NotEquals(current_.getMaskFilterPtr(), filter)) { - current_.setMaskFilter(paint->getMaskFilter()); - receiver_->setMaskFilter(filter); - } - } - return can_inherit_opacity; -} - -void DlCanvasToReceiver::Save() { - CheckAlive(); - bool is_nop = current_layer_->state_is_nop_; - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); - current_layer_->state_is_nop_ = is_nop; - receiver_->save(); -} -void DlCanvasToReceiver::SaveLayer(const SkRect* bounds, - const DlPaint* paint, - const DlImageFilter* backdrop) { - CheckAlive(); - SaveLayerOptions options = paint ? SaveLayerOptions::kWithAttributes // - : SaveLayerOptions::kNoAttributes; - DisplayListAttributeFlags flags = paint ? kSaveLayerWithPaintFlags // - : kSaveLayerFlags; - OpResult result = PaintResult(paint, flags); - if (result == OpResult::kNoEffect) { - Save(); - current_layer_->state_is_nop_ = true; - return; - } - - bool unbounded = false; - if (backdrop) { - // A backdrop will pre-fill up to the entire surface, bounded by - // the clip. - unbounded = true; - } else if (!paint_nops_on_transparency(paint)) { - // The actual flood of the outer layer clip based on the paint - // will occur after the (eventual) corresponding restore is called, - // but rather than remember this information in the LayerInfo - // until the restore method is processed, we just mark the unbounded - // state up front. Another reason to accumulate the clip here rather - // than in restore is so that this savelayer will be tagged in the - // rtree with its full bounds and the right op_index so that it doesn't - // get culled during rendering. - unbounded = true; - } - if (unbounded) { - // Accumulate should always return true here because if the clip - // was empty then that would have been caught up above when we - // tested the PaintResult. - [[maybe_unused]] bool unclipped = AccumulateUnbounded(); - FML_DCHECK(unclipped); - } - - auto image_filter = paint ? paint->getImageFilter() : nullptr; - - bool layer_can_inherit_opacity = SetAttributesFromPaint(paint, flags); - receiver_->saveLayer(bounds, options, backdrop); - current_layer_->Update(result, layer_can_inherit_opacity); - - layer_stack_.emplace_back(true, image_filter); - current_layer_ = &layer_stack_.back(); - - if (image_filter != nullptr || !layer_can_inherit_opacity) { - // The compatibility computed by SetAttributes does not take an - // ImageFilter into account because an individual primitive with - // an ImageFilter can apply opacity on top of it. But, if the layer - // is applying the ImageFilter then it cannot pass the opacity on - // to its children even if they are compatible. - // Also, if a layer cannot itself inherit opacity then it cannot - // pass it along to its children. Arguably this information is - // not interesting because the saveLayer will have informed its - // enclosing layers not to send it an opacity to apply in the - // first place. But some tests check to see if the layer has - // noticed this situation by testing the inheritance property on - // the layer itself. Better tests wouldn't expect this property - // to be recorded there, but we will mark our children incompatible - // to be explicit about this situation. - current_layer_->mark_incompatible(); - } - - if (image_filter != nullptr) { - // We use |resetCullRect| here because we will be accumulating bounds of - // primitives before applying the filter to those bounds. We might - // encounter a primitive whose bounds are clipped, but whose filtered - // bounds will not be clipped. If the individual rendering ops bounds - // are clipped, it will not contribute to the overall bounds which - // could lead to inaccurate (subset) bounds of the DisplayList. - // We need to reset the cull rect here to avoid this premature clipping. - // The filtered bounds will be clipped to the existing clip rect when - // this layer is restored. - // If bounds is null then the original cull_rect will be used. - receiver_->resetCullRect(bounds); - } else if (bounds != nullptr) { - // Even though Skia claims that the bounds are only a hint, they actually - // use them as the temporary layer bounds during rendering the layer, so - // we set them as if a clip operation were performed. - receiver_->intersectCullRect(*bounds); - } -} - -void DlCanvasToReceiver::Restore() { - CheckAlive(); - if (layer_stack_.size() > 1) { - // Grab the current layer info before we push the restore - // on the stack. - LayerInfo layer_info = layer_stack_.back(); - layer_stack_.pop_back(); - current_layer_ = &layer_stack_.back(); - - if (layer_info.has_layer()) { - bool can_distribute_opacity = - layer_info.has_layer() && layer_info.is_group_opacity_compatible(); - receiver_->restoreLayer(layer_info.filter().get(), - layer_info.is_unbounded(), - can_distribute_opacity); - } else { - receiver_->restore(); - - // For regular save() ops there was no protecting layer so we have to - // accumulate the opacity compatibility values into the enclosing layer. - if (layer_info.cannot_inherit_opacity()) { - current_layer_->mark_incompatible(); - } else if (layer_info.has_compatible_op()) { - current_layer_->add_compatible_op(); - } - } - } -} -void DlCanvasToReceiver::RestoreToCount(int restore_count) { - CheckAlive(); - FML_DCHECK(restore_count <= GetSaveCount()); - while (restore_count < GetSaveCount() && GetSaveCount() > 1) { - Restore(); - } -} - -void DlCanvasToReceiver::Translate(SkScalar tx, SkScalar ty) { - CheckAlive(); - if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && - (tx != 0.0 || ty != 0.0)) { - receiver_->translate(tx, ty); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } -} -void DlCanvasToReceiver::Scale(SkScalar sx, SkScalar sy) { - CheckAlive(); - if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && - (sx != 1.0 || sy != 1.0)) { - receiver_->scale(sx, sy); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } -} -void DlCanvasToReceiver::Rotate(SkScalar degrees) { - CheckAlive(); - if (SkScalarMod(degrees, 360.0) != 0.0) { - receiver_->rotate(degrees); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } -} -void DlCanvasToReceiver::Skew(SkScalar sx, SkScalar sy) { - CheckAlive(); - if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && - (sx != 0.0 || sy != 0.0)) { - receiver_->skew(sx, sy); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } -} - -// clang-format off - -// 2x3 2D affine subset of a 4x4 transform in row major order -void DlCanvasToReceiver::Transform2DAffine( - SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) { - CheckAlive(); - if (SkScalarsAreFinite(mxx, myx) && - SkScalarsAreFinite(mxy, myy) && - SkScalarsAreFinite(mxt, myt)) { - if (mxx == 1 && mxy == 0 && - myx == 0 && myy == 1) { - Translate(mxt, myt); - } else { - receiver_->transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } - } -} -// full 4x4 transform in row major order -void DlCanvasToReceiver::TransformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { - CheckAlive(); - if ( mxz == 0 && - myz == 0 && - mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 && - mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { - Transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && - SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && - SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && - SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { - receiver_->transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } -} -void DlCanvasToReceiver::TransformReset() { - CheckAlive(); - receiver_->transformReset(); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } -} -void DlCanvasToReceiver::Transform(const SkMatrix* matrix) { - CheckAlive(); - if (matrix != nullptr) { - if (matrix->hasPerspective()) { - Transform2DAffine( - matrix->getScaleX(), matrix->getSkewX(), matrix->getTranslateX(), - matrix->getSkewY(), matrix->getScaleY(), matrix->getTranslateY()); - } else { - TransformFullPerspective( - matrix->rc(0, 0), matrix->rc(0, 1), 0.0f, matrix->rc(0, 2), - matrix->rc(1, 0), matrix->rc(1, 1), 0.0f, matrix->rc(1, 2), - 0.0f, 0.0f, 1.0f, 0.0f, - matrix->rc(2, 0), matrix->rc(2, 1), 0.0f, matrix->rc(2, 2)); - } - } -} -void DlCanvasToReceiver::Transform(const SkM44* m44) { - CheckAlive(); - if (m44 != nullptr) { - TransformFullPerspective( - m44->rc(0, 0), m44->rc(0, 1), m44->rc(0, 2), m44->rc(0, 3), - m44->rc(1, 0), m44->rc(1, 1), m44->rc(1, 2), m44->rc(1, 3), - m44->rc(2, 0), m44->rc(2, 1), m44->rc(2, 2), m44->rc(2, 3), - m44->rc(3, 0), m44->rc(3, 1), m44->rc(3, 2), m44->rc(3, 3)); - } -} -// clang-format on - -void DlCanvasToReceiver::ClipRect(const SkRect& rect, - ClipOp clip_op, - bool is_aa) { - CheckAlive(); - if (!rect.isFinite()) { - return; - } - receiver_->clipRect(rect, clip_op, is_aa); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } -} -void DlCanvasToReceiver::ClipRRect(const SkRRect& rrect, - ClipOp clip_op, - bool is_aa) { - CheckAlive(); - if (rrect.isRect()) { - ClipRect(rrect.rect(), clip_op, is_aa); - } else { - receiver_->clipRRect(rrect, clip_op, is_aa); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } - } -} -void DlCanvasToReceiver::ClipPath(const SkPath& path, - ClipOp clip_op, - bool is_aa) { - CheckAlive(); - if (!path.isInverseFillType()) { - SkRect rect; - if (path.isRect(&rect)) { - this->ClipRect(rect, clip_op, is_aa); - return; - } - SkRRect rrect; - if (path.isOval(&rect)) { - rrect.setOval(rect); - this->ClipRRect(rrect, clip_op, is_aa); - return; - } - if (path.isRRect(&rrect)) { - this->ClipRRect(rrect, clip_op, is_aa); - return; - } - } - receiver_->clipPath(path, clip_op, is_aa); - if (receiver_->is_nop()) { - current_layer_->state_is_nop_ = true; - } -} - -bool DlCanvasToReceiver::QuickReject(const SkRect& bounds) const { - CheckAlive(); - return receiver_->content_culled(bounds); -} - -void DlCanvasToReceiver::DrawPaint(const DlPaint& paint) { - CheckAlive(); - OpResult result = PaintResult(paint, kDrawPaintFlags); - if (result != OpResult::kNoEffect && AccumulateUnbounded()) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, kDrawPaintFlags); - receiver_->drawPaint(); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawColor(DlColor color, DlBlendMode mode) { - CheckAlive(); - // We use DrawPaint flags here because the DrawColor flags indicate - // that they will ignore the values in the paint that we are creating. - // But we need the PaintResult to actually look at this paint object - // so DrawPaint flags tell it to check all the required values. - // Alternately, we could just call |DrawPaint(DlPaint(...));| which - // is the equivalent call and the same thing would happen. - OpResult result = - PaintResult(DlPaint(color).setBlendMode(mode), kDrawPaintFlags); - if (result != OpResult::kNoEffect && AccumulateUnbounded()) { - receiver_->drawColor(color, mode); - current_layer_->Update(result, IsOpacityCompatible(mode)); - } -} -void DlCanvasToReceiver::DrawLine(const SkPoint& p0, - const SkPoint& p1, - const DlPaint& paint) { - CheckAlive(); - SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); - DisplayListAttributeFlags flags = - (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags - : kDrawHVLineFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(bounds, &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawLine(p0, p1); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawRect(const SkRect& rect, const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawRectFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(rect.makeSorted(), &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawRect(rect); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawOval(const SkRect& bounds, const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawOvalFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(bounds.makeSorted(), &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawOval(bounds); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawCircle(const SkPoint& center, - SkScalar radius, - const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawCircleFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect) { - SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius, - center.fX + radius, center.fY + radius); - if (AccumulateOpBounds(bounds, &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawCircle(center, radius); - current_layer_->Update(result, can_inherit_opacity); - } - } -} -void DlCanvasToReceiver::DrawRRect(const SkRRect& rrect, const DlPaint& paint) { - CheckAlive(); - if (rrect.isRect()) { - DrawRect(rrect.rect(), paint); - } else if (rrect.isOval()) { - DrawOval(rrect.rect(), paint); - } else { - DisplayListAttributeFlags flags = kDrawRRectFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(rrect.getBounds(), &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawRRect(rrect); - current_layer_->Update(result, can_inherit_opacity); - } - } -} -void DlCanvasToReceiver::DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawDRRectFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(outer.getBounds(), &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawDRRect(outer, inner); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawPath(const SkPath& path, const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawPathFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect) { - bool is_visible = path.isInverseFillType() - ? AccumulateUnbounded() - : AccumulateOpBounds(path.getBounds(), &paint, flags); - if (is_visible) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawPath(path); - current_layer_->Update(result, can_inherit_opacity); - } - } -} - -void DlCanvasToReceiver::DrawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter, - const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = // - useCenter // - ? kDrawArcWithCenterFlags - : kDrawArcNoCenterFlags; - OpResult result = PaintResult(paint, flags); - // This could be tighter if we compute where the start and end - // angles are and then also consider the quadrants swept and - // the center if specified. - if (result != OpResult::kNoEffect && - AccumulateOpBounds(bounds, &paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(&paint, flags); - receiver_->drawArc(bounds, start, sweep, useCenter); - current_layer_->Update(result, can_inherit_opacity); - } -} - -DisplayListAttributeFlags DlCanvasToReceiver::FlagsForPointMode( - PointMode mode) { - switch (mode) { - case DlCanvas::PointMode::kPoints: - return kDrawPointsAsPointsFlags; - case PointMode::kLines: - return kDrawPointsAsLinesFlags; - case PointMode::kPolygon: - return kDrawPointsAsPolygonFlags; - } - FML_UNREACHABLE(); -} -void DlCanvasToReceiver::DrawPoints(PointMode mode, - uint32_t count, - const SkPoint pts[], - const DlPaint& paint) { - CheckAlive(); - if (count == 0) { - return; - } - DisplayListAttributeFlags flags = FlagsForPointMode(mode); - OpResult result = PaintResult(paint, flags); - if (result == OpResult::kNoEffect) { - return; - } - - FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); - RectBoundsAccumulator ptBounds; - for (size_t i = 0; i < count; i++) { - ptBounds.accumulate(pts[i]); - } - SkRect point_bounds = ptBounds.bounds(); - if (!AccumulateOpBounds(point_bounds, &paint, flags)) { - return; - } - - (void)SetAttributesFromPaint(&paint, flags); - receiver_->drawPoints(mode, count, pts); - // drawPoints treats every point or line (or segment of a polygon) - // as a completely separate operation meaning we cannot ensure - // distribution of group opacity without analyzing the mode and the - // bounds of every sub-primitive. - // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c - current_layer_->Update(result, false); -} -void DlCanvasToReceiver::DrawVertices(const DlVertices* vertices, - DlBlendMode mode, - const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawVerticesFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && - AccumulateOpBounds(vertices->bounds(), &paint, flags)) { - (void)SetAttributesFromPaint(&paint, flags); - receiver_->drawVertices(vertices, mode); - // DrawVertices applies its colors to the paint so we have no way - // of controlling opacity using the current paint attributes. - // Although, examination of the |mode| might find some predictable - // cases. - current_layer_->Update(result, false); - } -} - -void DlCanvasToReceiver::DrawImage(const sk_sp& image, - const SkPoint point, - DlImageSampling sampling, - const DlPaint* paint) { - CheckAlive(); - DisplayListAttributeFlags flags = paint ? kDrawImageWithPaintFlags // - : kDrawImageFlags; - OpResult result = PaintResult(paint, flags); - if (result == OpResult::kNoEffect) { - return; - } - SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, // - image->width(), image->height()); - if (AccumulateOpBounds(bounds, paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(paint, flags); - receiver_->drawImage(image, point, sampling, paint != nullptr); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawImageRect(const sk_sp& image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - const DlPaint* paint, - SrcRectConstraint constraint) { - CheckAlive(); - DisplayListAttributeFlags flags = paint ? kDrawImageRectWithPaintFlags // - : kDrawImageRectFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(paint, flags); - receiver_->drawImageRect(image, src, dst, sampling, paint != nullptr, - constraint); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawImageNine(const sk_sp& image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - const DlPaint* paint) { - CheckAlive(); - DisplayListAttributeFlags flags = paint ? kDrawImageNineWithPaintFlags // - : kDrawImageNineFlags; - OpResult result = PaintResult(paint, flags); - if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, paint, flags)) { - bool can_inherit_opacity = SetAttributesFromPaint(paint, flags); - receiver_->drawImageNine(image, center, dst, filter, paint != nullptr); - current_layer_->Update(result, can_inherit_opacity); - } -} -void DlCanvasToReceiver::DrawAtlas(const sk_sp& atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cull_rect, - const DlPaint* paint) { - CheckAlive(); - DisplayListAttributeFlags flags = paint ? kDrawAtlasWithPaintFlags // - : kDrawAtlasFlags; - OpResult result = PaintResult(paint, flags); - if (result == OpResult::kNoEffect) { - return; - } - SkPoint quad[4]; - RectBoundsAccumulator atlasBounds; - for (int i = 0; i < count; i++) { - const SkRect& src = tex[i]; - xform[i].toQuad(src.width(), src.height(), quad); - for (int j = 0; j < 4; j++) { - atlasBounds.accumulate(quad[j]); - } - } - if (atlasBounds.is_empty() || - !AccumulateOpBounds(atlasBounds.bounds(), paint, flags)) { - return; - } - - (void)SetAttributesFromPaint(paint, flags); - receiver_->drawAtlas(atlas, xform, tex, colors, count, mode, sampling, - cull_rect, paint != nullptr); - // drawAtlas treats each image as a separate operation so we cannot rely - // on it to distribute the opacity without overlap without checking all - // of the transforms and texture rectangles. - current_layer_->Update(result, false); -} - -void DlCanvasToReceiver::DrawDisplayList(const sk_sp display_list, - SkScalar opacity) { - CheckAlive(); - if (!SkScalarIsFinite(opacity) || opacity <= SK_ScalarNearlyZero || - display_list->op_count() == 0 || display_list->bounds().isEmpty() || - current_layer_->state_is_nop_) { - return; - } - - const SkRect bounds = display_list->bounds(); - bool accumulated; - if (receiver_->wants_granular_bounds()) { - auto rtree = display_list->rtree(); - if (rtree) { - std::list rects = rtree->searchAndConsolidateRects(bounds, false); - accumulated = false; - for (const SkRect& rect : rects) { - // TODO (https://github.com/flutter/flutter/issues/114919): Attributes - // are not necessarily `kDrawDisplayListFlags`. - if (AccumulateOpBounds(rect, nullptr, kDrawDisplayListFlags)) { - accumulated = true; - } - } - } else { - accumulated = AccumulateOpBounds(bounds, nullptr, kDrawDisplayListFlags); - } - } else { - accumulated = AccumulateOpBounds(bounds, nullptr, kDrawDisplayListFlags); - } - if (!accumulated) { - return; - } - - receiver_->drawDisplayList(display_list, - opacity < SK_Scalar1 ? opacity : SK_Scalar1); - - // The non-nested op count accumulated in the |Push| method will include - // this call to |drawDisplayList| for non-nested op count metrics. - // But, for nested op count metrics we want the |drawDisplayList| call itself - // to be transparent. So we subtract 1 from our accumulated nested count to - // balance out against the 1 that was accumulated into the regular count. - // This behavior is identical to the way SkPicture computed nested op counts. - // nested_op_count_ += display_list->op_count(true) - 1; - // nested_bytes_ += display_list->bytes(true); - current_layer_->Update( - // Nop DisplayLists are eliminated above so we either affect transparent - // pixels or we do not. We should not have [kNoEffect]. - display_list->modifies_transparent_black() - ? OpResult::kAffectsAll - : OpResult::kPreservesTransparency, - display_list->can_apply_group_opacity()); -} -void DlCanvasToReceiver::DrawImpellerPicture( - const std::shared_ptr& picture, - SkScalar opacity) { - FML_LOG(ERROR) << "Cannot draw Impeller Picture in to a a display list."; - FML_DCHECK(false); -} -void DlCanvasToReceiver::DrawTextBlob(const sk_sp& blob, - SkScalar x, - SkScalar y, - const DlPaint& paint) { - CheckAlive(); - DisplayListAttributeFlags flags = kDrawTextBlobFlags; - OpResult result = PaintResult(paint, flags); - if (result == OpResult::kNoEffect) { - return; - } - bool unclipped = - AccumulateOpBounds(blob->bounds().makeOffset(x, y), &paint, flags); - // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the - // unit tests can use Fuchsia's font manager instead of the empty default. - // Until then we might encounter empty bounds for otherwise valid text and - // thus we ignore the results from AccumulateOpBounds. -#if defined(OS_FUCHSIA) - unclipped = true; -#endif // OS_FUCHSIA - if (unclipped) { - (void)SetAttributesFromPaint(&paint, flags); - receiver_->drawTextBlob(blob, x, y); - // There is no way to query if the glyphs of a text blob overlap and - // there are no current guarantees from either Skia or Impeller that - // they will protect overlapping glyphs from the effects of overdraw - // so we must make the conservative assessment that this DL layer is - // not compatible with group opacity inheritance. - current_layer_->Update(result, false); - } -} -void DlCanvasToReceiver::DrawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) { - CheckAlive(); - OpResult result = PaintResult(DlPaint(color)); - if (result != OpResult::kNoEffect) { - SkRect shadow_bounds = - DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform()); - if (AccumulateOpBounds(shadow_bounds, nullptr, kDrawShadowFlags)) { - receiver_->drawShadow(path, color, elevation, transparent_occluder, dpr); - current_layer_->Update(result, false); - } - } -} - -bool DlCanvasToReceiver::ComputeFilteredBounds(SkRect& bounds, - const DlImageFilter* filter) { - if (filter) { - if (!filter->map_local_bounds(bounds, bounds)) { - return false; - } - } - return true; -} - -bool DlCanvasToReceiver::AdjustBoundsForPaint(SkRect& bounds, - const DlPaint* in_paint, - DisplayListAttributeFlags flags) { - if (flags.ignores_paint()) { - return true; - } - - const DlPaint& paint = in_paint ? *in_paint : kDefaultPaint_; - if (flags.is_geometric()) { - bool is_stroked = flags.is_stroked(paint.getDrawStyle()); - - // Path effect occurs before stroking... - DisplayListSpecialGeometryFlags special_flags = - flags.WithPathEffect(paint.getPathEffectPtr(), is_stroked); - if (paint.getPathEffect()) { - auto effect_bounds = paint.getPathEffect()->effect_bounds(bounds); - if (!effect_bounds.has_value()) { - return false; - } - bounds = effect_bounds.value(); - } - - if (is_stroked) { - // Determine the max multiplier to the stroke width first. - SkScalar pad = 1.0f; - if (paint.getStrokeJoin() == DlStrokeJoin::kMiter && - special_flags.may_have_acute_joins()) { - pad = std::max(pad, paint.getStrokeMiter()); - } - if (paint.getStrokeCap() == DlStrokeCap::kSquare && - special_flags.may_have_diagonal_caps()) { - pad = std::max(pad, SK_ScalarSqrt2); - } - SkScalar min_stroke_width = 0.01; - pad *= std::max(paint.getStrokeWidth() * 0.5f, min_stroke_width); - bounds.outset(pad, pad); - } - } - - if (flags.applies_mask_filter()) { - auto filter = paint.getMaskFilter(); - if (filter) { - switch (filter->type()) { - case DlMaskFilterType::kBlur: { - FML_DCHECK(filter->asBlur()); - SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0; - bounds.outset(mask_sigma_pad, mask_sigma_pad); - } - } - } - } - - if (flags.applies_image_filter()) { - return ComputeFilteredBounds(bounds, paint.getImageFilter().get()); - } - - return true; -} - -bool DlCanvasToReceiver::AccumulateUnbounded() { - return receiver_->accumulateUnboundedForNextOp(); -} - -bool DlCanvasToReceiver::AccumulateOpBounds(SkRect& bounds, - const DlPaint* paint, - DisplayListAttributeFlags flags) { - if (AdjustBoundsForPaint(bounds, paint, flags)) { - return AccumulateBounds(bounds); - } else { - return AccumulateUnbounded(); - } -} -bool DlCanvasToReceiver::AccumulateBounds(SkRect& bounds) { - return receiver_->accumulateLocalBoundsForNextOp(bounds); -} - -bool DlCanvasToReceiver::paint_nops_on_transparency(const DlPaint* paint) { - if (paint == nullptr) { - return true; - } - - // SkImageFilter::canComputeFastBounds tests for transparency behavior - // This test assumes that the blend mode checked down below will - // NOP on transparent black. - if (paint->getImageFilterPtr() && - paint->getImageFilterPtr()->modifies_transparent_black()) { - return false; - } - - // We filter the transparent black that is used for the background of a - // saveLayer and make sure it returns transparent black. If it does, then - // the color filter will leave all area surrounding the contents of the - // save layer untouched out to the edge of the output surface. - // This test assumes that the blend mode checked down below will - // NOP on transparent black. - if (paint->getColorFilterPtr() && - paint->getColorFilterPtr()->modifies_transparent_black()) { - return false; - } - - // Unusual blendmodes require us to process a saved layer - // even with operations outside the clip. - // For example, DstIn is used by masking layers. - // https://code.google.com/p/skia/issues/detail?id=1291 - // https://crbug.com/401593 - switch (paint->getBlendMode()) { - // For each of the following transfer modes, if the source - // alpha is zero (our transparent black), the resulting - // blended pixel is not necessarily equal to the original - // destination pixel. - // Mathematically, any time in the following equations where - // the result is not d assuming source is 0 - case DlBlendMode::kClear: // r = 0 - case DlBlendMode::kSrc: // r = s - case DlBlendMode::kSrcIn: // r = s * da - case DlBlendMode::kDstIn: // r = d * sa - case DlBlendMode::kSrcOut: // r = s * (1-da) - case DlBlendMode::kDstATop: // r = d*sa + s*(1-da) - case DlBlendMode::kModulate: // r = s*d - return false; - break; - - // And in these equations, the result must be d if the - // source is 0 - case DlBlendMode::kDst: // r = d - case DlBlendMode::kSrcOver: // r = s + (1-sa)*d - case DlBlendMode::kDstOver: // r = d + (1-da)*s - case DlBlendMode::kDstOut: // r = d * (1-sa) - case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa) - case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa) - case DlBlendMode::kPlus: // r = min(s + d, 1) - case DlBlendMode::kScreen: // r = s + d - s*d - case DlBlendMode::kOverlay: // multiply or screen, depending on dest - case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa), - // ra = kSrcOver - case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa), - // ra = kSrcOver - case DlBlendMode::kColorDodge: // brighten destination to reflect source - case DlBlendMode::kColorBurn: // darken destination to reflect source - case DlBlendMode::kHardLight: // multiply or screen, depending on source - case DlBlendMode::kSoftLight: // lighten or darken, depending on source - case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)), - // ra = kSrcOver - case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver - case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d - case DlBlendMode::kHue: // ra = kSrcOver - case DlBlendMode::kSaturation: // ra = kSrcOver - case DlBlendMode::kColor: // ra = kSrcOver - case DlBlendMode::kLuminosity: // ra = kSrcOver - return true; - break; - } -} - -DlColor DlCanvasToReceiver::GetEffectiveColor(const DlPaint& paint, - DisplayListAttributeFlags flags) { - DlColor color; - if (flags.applies_color()) { - const DlColorSource* source = paint.getColorSourcePtr(); - if (source) { - if (source->asColor()) { - color = source->asColor()->color(); - } else { - color = source->is_opaque() ? DlColor::kBlack() : kAnyColor; - } - } else { - color = paint.getColor(); - } - } else if (flags.applies_alpha()) { - // If the operation applies alpha, but not color, then the only impact - // of the alpha is to modulate the output towards transparency. - // We can not guarantee an opaque source even if the alpha is opaque - // since that would require knowing something about the colors that - // the alpha is modulating, but we can guarantee a transparent source - // if the alpha is 0. - color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor; - } else { - color = kAnyColor; - } - if (flags.applies_image_filter()) { - auto filter = paint.getImageFilterPtr(); - if (filter) { - if (!color.isTransparent() || filter->modifies_transparent_black()) { - color = kAnyColor; - } - } - } - if (flags.applies_color_filter()) { - auto filter = paint.getColorFilterPtr(); - if (filter) { - if (!color.isTransparent() || filter->modifies_transparent_black()) { - color = kAnyColor; - } - } - } - return color; -} - -DlCanvasToReceiver::OpResult DlCanvasToReceiver::PaintResult( - const DlPaint& paint, - DisplayListAttributeFlags flags) { - if (current_layer_->state_is_nop_) { - return OpResult::kNoEffect; - } - if (flags.applies_blend()) { - switch (paint.getBlendMode()) { - // Nop blend mode (singular, there is only one) - case DlBlendMode::kDst: - return OpResult::kNoEffect; - - // Always clears pixels blend mode (singular, there is only one) - case DlBlendMode::kClear: - return OpResult::kPreservesTransparency; - - case DlBlendMode::kHue: - case DlBlendMode::kSaturation: - case DlBlendMode::kColor: - case DlBlendMode::kLuminosity: - case DlBlendMode::kColorBurn: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kNoEffect - : OpResult::kAffectsAll; - - // kSrcIn modifies pixels towards transparency - case DlBlendMode::kSrcIn: - return OpResult::kPreservesTransparency; - - // These blend modes preserve destination alpha - case DlBlendMode::kSrcATop: - case DlBlendMode::kDstOut: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kNoEffect - : OpResult::kPreservesTransparency; - - // Always destructive blend modes, potentially not affecting transparency - case DlBlendMode::kSrc: - case DlBlendMode::kSrcOut: - case DlBlendMode::kDstATop: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kPreservesTransparency - : OpResult::kAffectsAll; - - // The kDstIn blend mode modifies the destination unless the - // source color is opaque. - case DlBlendMode::kDstIn: - return GetEffectiveColor(paint, flags).isOpaque() - ? OpResult::kNoEffect - : OpResult::kPreservesTransparency; - - // The next group of blend modes modifies the destination unless the - // source color is transparent. - case DlBlendMode::kSrcOver: - case DlBlendMode::kDstOver: - case DlBlendMode::kXor: - case DlBlendMode::kPlus: - case DlBlendMode::kScreen: - case DlBlendMode::kMultiply: - case DlBlendMode::kOverlay: - case DlBlendMode::kDarken: - case DlBlendMode::kLighten: - case DlBlendMode::kColorDodge: - case DlBlendMode::kHardLight: - case DlBlendMode::kSoftLight: - case DlBlendMode::kDifference: - case DlBlendMode::kExclusion: - return GetEffectiveColor(paint, flags).isTransparent() - ? OpResult::kNoEffect - : OpResult::kAffectsAll; - - // Modulate only leaves the pixel alone when the source is white. - case DlBlendMode::kModulate: - return GetEffectiveColor(paint, flags) == DlColor::kWhite() - ? OpResult::kNoEffect - : OpResult::kPreservesTransparency; - } - } - return OpResult::kAffectsAll; -} - -DlPaint DlCanvasToReceiver::kDefaultPaint_; - -} // namespace flutter diff --git a/display_list/dl_canvas_to_receiver.h b/display_list/dl_canvas_to_receiver.h deleted file mode 100644 index ea8acfaffad4c..0000000000000 --- a/display_list/dl_canvas_to_receiver.h +++ /dev/null @@ -1,573 +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. - -#ifndef FLUTTER_DISPLAY_LIST_DL_CANVAS_TO_RECEIVER_H_ -#define FLUTTER_DISPLAY_LIST_DL_CANVAS_TO_RECEIVER_H_ - -#include "flutter/display_list/dl_canvas.h" -#include "flutter/display_list/dl_op_flags.h" -#include "flutter/display_list/dl_op_receiver.h" -#include "flutter/fml/macros.h" - -namespace impeller { -struct Picture; -} - -namespace flutter { - -class DlCanvasReceiver : public DlOpReceiver { - private: - using ClipOp = DlCanvas::ClipOp; - - public: - virtual ~DlCanvasReceiver() = default; - - virtual SkRect base_device_cull_rect() const = 0; - - virtual SkM44 matrix_4x4() const = 0; - virtual SkMatrix matrix_3x3() const { return matrix_4x4().asM33(); } - - virtual SkRect device_cull_rect() const = 0; - virtual SkRect local_cull_rect() const = 0; - virtual bool is_cull_rect_empty() const = 0; - virtual bool content_culled(const SkRect& content_bounds) const = 0; - - /*--------- Methods below here are optional optimizations --------*/ - - /// Optional - only needed if performing bounds culling via the accumulate - /// methods - /// Note: The bounds are only advisory for implementing bounds culling - /// and should not trigger an actual clip operation. - /// The default implementation ignores the advisory information. - /// - /// This method should almost never be used as it breaks the encapsulation - /// of the enclosing clips. However it is needed for practical purposes in - /// some rare cases - such as when a saveLayer is collecting rendering - /// operations prior to applying a filter on the entire layer bounds and - /// some of those operations fall outside the enclosing clip, but their - /// filtered content will spread out from where they were rendered on the - /// layer into the enclosing clipped area. - /// Omitting the |cull_rect| argument, or passing nullptr, will restore the - /// cull rect to the initial value it had when the tracker was constructed. - virtual void resetCullRect(const SkRect* cull_rect = nullptr) {} - - /// Optional - only needed if performing bounds culling via the accumulate - /// methods - /// Note: The bounds are only advisory for implementing bounds culling - /// and should not trigger an actual clip operation. - /// The default implementation ignores the advisory information. - /// - /// This method is used to add an additional culling bounds without - /// actually performing a clip on the destination. The bounds will - /// help avoid recording content of a saveLayer that lies entirely - /// outside the save layer bounds if it had them. - virtual void intersectCullRect(const SkRect& cull_rect) {} - - /// Optional - only needed if implementing the accumulate methods - /// The default implementation returns false to prevent extra work in - /// the adapter. - /// - /// If the receiver is accumulating bounds into, say, an rtree format, - /// or wants granular bounds for any other reason, this query will let - /// the adapter know that it should deliver the bounds of methods such - /// as DrawDisplayList granularly if possible (i.e. if the DL being - /// drawn itself has an RTree for its bounds). - /// - /// Returns: true if the implementation can make use of granular bounds - virtual bool wants_granular_bounds() const { return false; } - - /// Optional - only needed if accumulating bounds or implementing bounds - /// culling - /// The default implementation ignores the bounds and returns true to - /// prevent culling. - /// - /// The indicated bounds were calculated for the next rendering op call. - /// The receiver can ignore them if it is not accumulating the bounds, - /// and can tag them appropriately if recording the bounds per rendering op. - /// The receiver can also indicate if the bounds are clipped out with - /// the returned boolean. - /// - /// Returns: true if the bounds were not clipped - /// or false if there is no way for these bounds to be visible - virtual bool accumulateLocalBoundsForNextOp(const SkRect& r) { return true; } - - /// Optional - only needed if accumulating bounds or implementing bounds - /// culling - /// The default implementation ignores the condition and returns true to - /// prevent culling. - /// - /// The bounds for the next rendering op will be "unbounded" by anything - /// other than the current clip, whether because it is a |drawPaint| or - /// |drawColor| call or because it is rendered with a filter that modifies - /// all pixels (even transparent) out to infinity (or the clip). - /// The receiver can ignore the information if it is not accumulating - /// the bounds, and can tag them appropriately if recording the bounds - /// per rendering op. - /// The receiver can also indicate if the bounds are clipped out with - /// the returned boolean. - /// - /// Returns: true if the bounds were not clipped - /// or false if there is no way for these bounds to be visible - virtual bool accumulateUnboundedForNextOp() { return true; } - - /// Optional - only needed if the provided information is useful to the - /// implementation - /// By default this method will simply call the regular |restore| method - /// and the information will be ignored. - /// - /// Called in lieue of a call to |restore| when the upstream code calls - /// a restore on a |saveLayer| and there is information about the contents - /// of the layer that the receiver might find interesting. - virtual void restoreLayer(const DlImageFilter* filter, - bool layer_content_was_unbounded, - bool layer_could_distribute_opacity) { - restore(); - } - - /// Optional - useful to indicate non-renderable state such as an empty - /// clip or collapsed transform to suspend rendering calls - /// until the state is overridden or popped by a |restore|. - /// By default this method returns false to prevent state culling. - /// - /// The |is_nop| method will be called after any of the clip or - /// transform methods to detect if the current conditions are - /// now a NOP. The receiver will receive no more calls until the - /// associated restore() call which will be dispatched to it, or - /// a subsequent clip or transform "reset" operation. - /// - /// Returns: true if either the clip or transform prevent rendering - virtual bool is_nop() { return false; } -}; - -// The primary class used to build a display list. The list of methods -// here matches the list of methods invoked on a |DlOpReceiver| combined -// with the list of methods invoked on a |DlCanvas|. -class DlCanvasToReceiver : public virtual DlCanvas, // - DisplayListOpFlags { - public: - explicit DlCanvasToReceiver(std::shared_ptr receiver); - - ~DlCanvasToReceiver() = default; - - // |DlCanvas| - SkISize GetBaseLayerSize() const override; - // |DlCanvas| - SkImageInfo GetImageInfo() const override; - - // |DlCanvas| - void Save() override; - - // |DlCanvas| - void SaveLayer(const SkRect* bounds, - const DlPaint* paint = nullptr, - const DlImageFilter* backdrop = nullptr) override; - // |DlCanvas| - void Restore() override; - // |DlCanvas| - int GetSaveCount() const override { - CheckAlive(); - return layer_stack_.size(); - } - // |DlCanvas| - void RestoreToCount(int restore_count) override; - - // |DlCanvas| - void Translate(SkScalar tx, SkScalar ty) override; - // |DlCanvas| - void Scale(SkScalar sx, SkScalar sy) override; - // |DlCanvas| - void Rotate(SkScalar degrees) override; - // |DlCanvas| - void Skew(SkScalar sx, SkScalar sy) override; - - // clang-format off - // 2x3 2D affine subset of a 4x4 transform in row major order - // |DlCanvas| - void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) override; - // full 4x4 transform in row major order - // |DlCanvas| - void TransformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; - // clang-format on - // |DlCanvas| - void TransformReset() override; - // |DlCanvas| - void Transform(const SkMatrix* matrix) override; - // |DlCanvas| - void Transform(const SkM44* matrix44) override; - // |DlCanvas| - void SetTransform(const SkMatrix* matrix) override { - CheckAlive(); - TransformReset(); - Transform(matrix); - } - // |DlCanvas| - void SetTransform(const SkM44* matrix44) override { - CheckAlive(); - TransformReset(); - Transform(matrix44); - } - using DlCanvas::Transform; - - /// Returns the 4x4 full perspective transform representing all transform - /// operations executed so far in this DisplayList within the enclosing - /// save stack. - // |DlCanvas| - SkM44 GetTransformFullPerspective() const override { - CheckAlive(); - return receiver_->matrix_4x4(); - } - /// Returns the 3x3 partial perspective transform representing all transform - /// operations executed so far in this DisplayList within the enclosing - /// save stack. - // |DlCanvas| - SkMatrix GetTransform() const override { - CheckAlive(); - return receiver_->matrix_3x3(); - } - - // |DlCanvas| - void ClipRect(const SkRect& rect, - ClipOp clip_op = ClipOp::kIntersect, - bool is_aa = false) override; - // |DlCanvas| - void ClipRRect(const SkRRect& rrect, - ClipOp clip_op = ClipOp::kIntersect, - bool is_aa = false) override; - // |DlCanvas| - void ClipPath(const SkPath& path, - ClipOp clip_op = ClipOp::kIntersect, - bool is_aa = false) override; - - /// Conservative estimate of the bounds of all outstanding clip operations - /// measured in the coordinate space within which this DisplayList will - /// be rendered. - // |DlCanvas| - SkRect GetDestinationClipBounds() const override { - CheckAlive(); - return receiver_->device_cull_rect(); - } - /// Conservative estimate of the bounds of all outstanding clip operations - /// transformed into the local coordinate space in which currently - /// recorded rendering operations are interpreted. - // |DlCanvas| - SkRect GetLocalClipBounds() const override { - CheckAlive(); - return receiver_->local_cull_rect(); - } - - /// Return true iff the supplied bounds are easily shown to be outside - /// of the current clip bounds. This method may conservatively return - /// false if it cannot make the determination. - // |DlCanvas| - bool QuickReject(const SkRect& bounds) const override; - - // |DlCanvas| - void DrawPaint(const DlPaint& paint) override; - // |DlCanvas| - void DrawColor(DlColor color, DlBlendMode mode) override; - // |DlCanvas| - void DrawLine(const SkPoint& p0, - const SkPoint& p1, - const DlPaint& paint) override; - // |DlCanvas| - void DrawRect(const SkRect& rect, const DlPaint& paint) override; - // |DlCanvas| - void DrawOval(const SkRect& bounds, const DlPaint& paint) override; - // |DlCanvas| - void DrawCircle(const SkPoint& center, - SkScalar radius, - const DlPaint& paint) override; - // |DlCanvas| - void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override; - // |DlCanvas| - void DrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const DlPaint& paint) override; - // |DlCanvas| - void DrawPath(const SkPath& path, const DlPaint& paint) override; - // |DlCanvas| - void DrawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter, - const DlPaint& paint) override; - // |DlCanvas| - void DrawPoints(PointMode mode, - uint32_t count, - const SkPoint pts[], - const DlPaint& paint) override; - // |DlCanvas| - void DrawVertices(const DlVertices* vertices, - DlBlendMode mode, - const DlPaint& paint) override; - using DlCanvas::DrawVertices; - // |DlCanvas| - void DrawImage(const sk_sp& image, - const SkPoint point, - DlImageSampling sampling, - const DlPaint* paint = nullptr) override; - // |DlCanvas| - void DrawImageRect( - const sk_sp& image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - const DlPaint* paint = nullptr, - SrcRectConstraint constraint = SrcRectConstraint::kFast) override; - using DlCanvas::DrawImageRect; - // |DlCanvas| - void DrawImageNine(const sk_sp& image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - const DlPaint* paint = nullptr) override; - // |DlCanvas| - void DrawAtlas(const sk_sp& atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cullRect, - const DlPaint* paint = nullptr) override; - // |DlCanvas| - void DrawDisplayList(const sk_sp display_list, - SkScalar opacity = SK_Scalar1) override; - // |DlCanvas| - void DrawImpellerPicture( - const std::shared_ptr& picture, - SkScalar opacity = SK_Scalar1) override; - // |DlCanvas| - void DrawTextBlob(const sk_sp& blob, - SkScalar x, - SkScalar y, - const DlPaint& paint) override; - // |DlCanvas| - void DrawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) override; - - // |DlCanvas| - void Flush() override { CheckAlive(); } - - protected: - inline void CheckAlive() const { FML_CHECK(receiver_ != nullptr); } - - std::shared_ptr receiver_; - - bool current_group_opacity_compatibility() { - return current_layer_->is_group_opacity_compatible(); - } - bool current_affects_transparent_layer() { - return current_layer_->affects_transparent_layer(); - } - - DlPaint CurrentAttributes() const { return current_; } - - private: - // Returns whether or not the paint was compatible with opacity inheritance - [[nodiscard]] bool SetAttributesFromPaint( - const DlPaint* paint, - const DisplayListAttributeFlags flags); - - enum class OpResult { - kNoEffect, - kPreservesTransparency, - kAffectsAll, - }; - - class LayerInfo { - public: - explicit LayerInfo( - bool has_layer = false, - const std::shared_ptr& filter = nullptr) - : has_layer_(has_layer), filter_(filter) {} - - bool has_layer() const { return has_layer_; } - bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; } - bool has_compatible_op() const { return has_compatible_op_; } - bool affects_transparent_layer() const { - return affects_transparent_layer_; - } - - void Update(OpResult result, bool can_inherit_opacity) { - switch (result) { - case OpResult::kNoEffect: - // We should have stopped processing the rendering operation - // well before we tried to update the layer information. - FML_DCHECK(result != OpResult::kNoEffect); - return; - - case OpResult::kPreservesTransparency: - break; - - case OpResult::kAffectsAll: - add_visible_op(); - break; - } - - if (can_inherit_opacity) { - add_compatible_op(); - } else { - mark_incompatible(); - } - } - - bool is_group_opacity_compatible() const { - return !cannot_inherit_opacity_; - } - - void mark_incompatible() { cannot_inherit_opacity_ = true; } - - // For now this only allows a single compatible op to mark the - // layer as being compatible with group opacity. If we start - // computing bounds of ops in the Builder methods then we - // can upgrade this to checking for overlapping ops. - // See https://github.com/flutter/flutter/issues/93899 - void add_compatible_op() { - if (!cannot_inherit_opacity_) { - if (has_compatible_op_) { - cannot_inherit_opacity_ = true; - } else { - has_compatible_op_ = true; - } - } - } - - // Records that the current layer contains an op that produces visible - // output on a transparent surface. - void add_visible_op() { affects_transparent_layer_ = true; } - - // The filter to apply to the layer bounds when it is restored - std::shared_ptr filter() { return filter_; } - - // is_unbounded should be set to true if we ever encounter an operation - // on a layer that either is unrestricted (|drawColor| or |drawPaint|) - // or cannot compute its bounds (some effects and filters) and there - // was no outstanding clip op at the time. - // When the layer is restored, the outer layer may then process this - // unbounded state by accumulating its own clip or transferring the - // unbounded state to its own outer layer. - // Typically the DisplayList will have been constructed with a cull - // rect which will act as a default clip for the outermost layer and - // the unbounded state of all sub layers will eventually be caught by - // that cull rect so that the overall unbounded state of the entire - // DisplayList will never be true. - // - // For historical consistency it is worth noting that SkPicture used - // to treat these same conditions as a Nop (they accumulate the - // SkPicture cull rect, but if no cull rect was specified then it is - // an empty Rect and so has no effect on the bounds). - // - // Flutter is unlikely to ever run into this as the Dart mechanisms - // all supply a non-null cull rect for all Dart Picture objects, - // even if that cull rect is kGiantRect. - void set_unbounded() { is_unbounded_ = true; } - - // |is_unbounded| should be called after |getLayerBounds| in case - // a problem was found during the computation of those bounds, - // the layer will have one last chance to flag an unbounded state. - bool is_unbounded() const { return is_unbounded_; } - - private: - bool has_layer_; - bool cannot_inherit_opacity_ = false; - bool has_compatible_op_ = false; - std::shared_ptr filter_; - bool is_unbounded_ = false; - bool state_is_nop_ = false; - bool affects_transparent_layer_ = false; - - friend class DlCanvasToReceiver; - }; - - std::vector layer_stack_; - LayerInfo* current_layer_; - - // Returns the compatibility of a given blend mode for applying an - // inherited opacity value to modulate the visibility of the op. - // For now we only accept SrcOver blend modes but this could be expanded - // in the future to include other (rarely used) modes that also modulate - // the opacity of a rendering operation at the cost of a switch statement - // or lookup table. - static inline bool IsOpacityCompatible(DlBlendMode mode) { - return (mode == DlBlendMode::kSrcOver); - } - - static DisplayListAttributeFlags FlagsForPointMode(PointMode mode); - - bool paint_nops_on_transparency(const DlPaint* paint); - OpResult PaintResult(const DlPaint& paint, - DisplayListAttributeFlags flags = kDrawPaintFlags); - OpResult PaintResult(const DlPaint* paint, - DisplayListAttributeFlags flags = kDrawPaintFlags) { - if (paint) { - return PaintResult(*paint, flags); - } else if (current_layer_->state_is_nop_) { - return OpResult::kNoEffect; - } else { - FML_DCHECK(PaintResult(kDefaultPaint_, flags) == OpResult::kAffectsAll); - return OpResult::kAffectsAll; - } - } - - // kAnyColor is a non-opaque and non-transparent color that will not - // trigger any short-circuit tests about the results of a blend. - static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlpha(0x80); - static_assert(!kAnyColor.isOpaque()); - static_assert(!kAnyColor.isTransparent()); - static DlColor GetEffectiveColor(const DlPaint& paint, - DisplayListAttributeFlags flags); - - // Computes the bounds of an operation adjusted for a given ImageFilter - // and returns whether the computation was possible. If the method - // returns false then the caller should assume the worst about the bounds. - static bool ComputeFilteredBounds(SkRect& bounds, - const DlImageFilter* filter); - - // Adjusts the indicated bounds for the given flags and returns true if - // the calculation was possible, or false if it could not be estimated. - bool AdjustBoundsForPaint(SkRect& bounds, - const DlPaint* paint, - DisplayListAttributeFlags flags); - - // Records the fact that we encountered an op that either could not - // estimate its bounds or that fills all of the destination space. - bool AccumulateUnbounded(); - - // Records the bounds for an op after modifying them according to the - // supplied attribute flags and transforming by the current matrix. - bool AccumulateOpBounds(const SkRect& bounds, - const DlPaint* paint, - DisplayListAttributeFlags flags) { - SkRect safe_bounds = bounds; - return AccumulateOpBounds(safe_bounds, paint, flags); - } - - // Records the bounds for an op after modifying them according to the - // supplied attribute flags and transforming by the current matrix - // and clipping against the current clip. - bool AccumulateOpBounds(SkRect& bounds, - const DlPaint* paint, - DisplayListAttributeFlags flags); - - // Records the given bounds after transforming by the current matrix - // and clipping against the current clip. - bool AccumulateBounds(SkRect& bounds); - - DlPaint current_; - static DlPaint kDefaultPaint_; -}; - -} // namespace flutter - -#endif // FLUTTER_DISPLAY_LIST_DL_CANVAS_TO_RECEIVER_H_ diff --git a/display_list/dl_op_flags.h b/display_list/dl_op_flags.h index df6aa23990649..9bb6c013f8203 100644 --- a/display_list/dl_op_flags.h +++ b/display_list/dl_op_flags.h @@ -75,13 +75,6 @@ class DisplayListFlags { // mitered extensions outside the pre-transformed bounding box. static constexpr int kMayHaveAcuteJoins_ = 1 << 8; - // Some primitives render with a different algorithm on Skia - // when the line width is a hairline and that algorithm does - // not apply inherited opacity appropriately. We mark these - // operations with a flag so that we only disallow opacity - // inheritance on hairlines for those primitives. - static constexpr int kMayHaveTroubleWithHairlines_ = 1 << 9; - static constexpr int kAnySpecialGeometryMask_ = // kMayHaveCaps_ | kMayHaveJoins_ | kButtCapIsSquare_ | // kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_; @@ -227,12 +220,6 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { (style != DlDrawStyle::kFill && has_any(kIsDrawnGeometry_))); } - /// The operation may use an implementation for hairlines that - /// does not inherit opacity properly - constexpr bool may_have_trouble_with_hairlines() const { - return has_any(kMayHaveTroubleWithHairlines_); - } - constexpr bool is_flood() const { return has_any(kFloodsSurface_); } constexpr bool operator==(DisplayListAttributeFlags const& other) const { @@ -349,13 +336,12 @@ class DisplayListOpFlags : DisplayListFlags { kBASE_StrokeOrFillFlags_ // }; static constexpr DisplayListAttributeFlags kDrawPathFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveCaps_ | // - kMayHaveDiagonalCaps_ | // - kMayHaveJoins_ | // - kMayHaveAcuteJoins_ | // - kMayHaveTroubleWithHairlines_ // + kBASE_PaintFlags_ | // + kBASE_StrokeOrFillFlags_ | // + kMayHaveCaps_ | // + kMayHaveDiagonalCaps_ | // + kMayHaveJoins_ | // + kMayHaveAcuteJoins_ // }; static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags{ kBASE_PaintFlags_ | // @@ -364,11 +350,10 @@ class DisplayListOpFlags : DisplayListFlags { kMayHaveDiagonalCaps_ // }; static constexpr DisplayListAttributeFlags kDrawArcWithCenterFlags{ - kBASE_PaintFlags_ | // - kBASE_StrokeOrFillFlags_ | // - kMayHaveJoins_ | // - kMayHaveAcuteJoins_ | // - kMayHaveTroubleWithHairlines_ // + kBASE_PaintFlags_ | // + kBASE_StrokeOrFillFlags_ | // + kMayHaveJoins_ | // + kMayHaveAcuteJoins_ // }; static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags{ kBASE_PaintFlags_ | // diff --git a/display_list/dl_op_receiver.h b/display_list/dl_op_receiver.h index 1fe13c6f84eda..23a0aaf6fd97c 100644 --- a/display_list/dl_op_receiver.h +++ b/display_list/dl_op_receiver.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_DISPLAY_LIST_DL_OP_RECEIVER_H_ -#define FLUTTER_DISPLAY_LIST_DL_OP_RECEIVER_H_ +#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ #include "flutter/display_list/display_list.h" #include "flutter/display_list/dl_blend_mode.h" @@ -49,8 +49,6 @@ class DlOpReceiver { // MaxDrawPointsCount * sizeof(SkPoint) must be less than 1 << 32 static constexpr int kMaxDrawPointsCount = ((1 << 29) - 1); - virtual ~DlOpReceiver() = default; - // The following methods are nearly 1:1 with the methods on DlPaint and // carry the same meanings. Each method sets a persistent value for the // attribute for the rest of the display list or until it is reset by @@ -229,23 +227,23 @@ class DlOpReceiver { uint32_t count, const SkPoint points[]) = 0; virtual void drawVertices(const DlVertices* vertices, DlBlendMode mode) = 0; - virtual void drawImage(const sk_sp& image, + virtual void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) = 0; virtual void drawImageRect( - const sk_sp& image, + const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint = SrcRectConstraint::kFast) = 0; - virtual void drawImageNine(const sk_sp& image, + virtual void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) = 0; - virtual void drawAtlas(const sk_sp& atlas, + virtual void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -254,9 +252,9 @@ class DlOpReceiver { DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) = 0; - virtual void drawDisplayList(const sk_sp& display_list, + virtual void drawDisplayList(const sk_sp display_list, SkScalar opacity = SK_Scalar1) = 0; - virtual void drawTextBlob(const sk_sp& blob, + virtual void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) = 0; virtual void drawShadow(const SkPath& path, @@ -268,4 +266,4 @@ class DlOpReceiver { } // namespace flutter -#endif // FLUTTER_DISPLAY_LIST_DL_OP_RECEIVER_H_ +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ diff --git a/display_list/dl_op_recorder.cc b/display_list/dl_op_recorder.cc deleted file mode 100644 index 549f996a22087..0000000000000 --- a/display_list/dl_op_recorder.cc +++ /dev/null @@ -1,691 +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. - -#include "flutter/display_list/dl_op_recorder.h" - -#include "flutter/display_list/dl_attributes.h" -#include "flutter/display_list/dl_op_records.h" -#include "flutter/display_list/effects/dl_color_source.h" - -namespace flutter { - -#define DL_BUILDER_PAGE 4096 - -// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. -static void CopyV(void* dst) {} - -template -static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { - FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) - << "Expected " << dst << " to be aligned for at least " << alignof(S) - << " bytes."; - // If n is 0, there is nothing to copy into dst from src. - if (n > 0) { - memcpy(dst, src, n * sizeof(S)); - dst = reinterpret_cast(reinterpret_cast(dst) + - n * sizeof(S)); - } - // Repeat for the next items, if any - CopyV(dst, std::forward(rest)...); -} - -DlOpRecorder::DlOpRecorder(const SkRect& cull_rect, bool keep_rtree) { - tracker_ = - std::make_shared(cull_rect, SkMatrix::I()); - if (keep_rtree) { - accumulator_ = std::make_shared(); - } else { - accumulator_ = std::make_shared(); - } - save_infos_.push_back({ - .offset = 0u, - .deferred = false, - .is_layer = false, - }); -} - -template -void* DlOpRecorder::Push(size_t pod, int render_op_inc, Args&&... args) { - size_t size = SkAlignPtr(sizeof(T) + pod); - auto op = reinterpret_cast(storage_.alloc(size)); - new (op) T{std::forward(args)...}; - op->type = T::kType; - op->size = size; - render_op_count_ += render_op_inc; - op_index_++; - return op + 1; -} - -void DlOpRecorder::setAntiAlias(bool aa) { - Push(0, 0, aa); -} -void DlOpRecorder::setDither(bool dither) { - Push(0, 0, dither); -} -void DlOpRecorder::setInvertColors(bool invert) { - Push(0, 0, invert); -} -void DlOpRecorder::setStrokeCap(DlStrokeCap cap) { - Push(0, 0, cap); -} -void DlOpRecorder::setStrokeJoin(DlStrokeJoin join) { - Push(0, 0, join); -} -void DlOpRecorder::setDrawStyle(DlDrawStyle style) { - Push(0, 0, style); -} -void DlOpRecorder::setStrokeWidth(float width) { - Push(0, 0, width); -} -void DlOpRecorder::setStrokeMiter(float limit) { - Push(0, 0, limit); -} -void DlOpRecorder::setColor(DlColor color) { - Push(0, 0, color); -} -void DlOpRecorder::setBlendMode(DlBlendMode mode) { - Push(0, 0, mode); -} - -void DlOpRecorder::setColorSource(const DlColorSource* source) { - if (source == nullptr) { - Push(0, 0); - } else { - is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe(); - switch (source->type()) { - case DlColorSourceType::kColor: { - const DlColorColorSource* color_source = source->asColor(); - setColor(color_source->color()); - break; - } - case DlColorSourceType::kImage: { - const DlImageColorSource* image_source = source->asImage(); - FML_DCHECK(image_source); - Push(0, 0, image_source); - break; - } - case DlColorSourceType::kLinearGradient: { - const DlLinearGradientColorSource* linear = source->asLinearGradient(); - FML_DCHECK(linear); - void* pod = Push(linear->size(), 0); - new (pod) DlLinearGradientColorSource(linear); - break; - } - case DlColorSourceType::kRadialGradient: { - const DlRadialGradientColorSource* radial = source->asRadialGradient(); - FML_DCHECK(radial); - void* pod = Push(radial->size(), 0); - new (pod) DlRadialGradientColorSource(radial); - break; - } - case DlColorSourceType::kConicalGradient: { - const DlConicalGradientColorSource* conical = - source->asConicalGradient(); - FML_DCHECK(conical); - void* pod = Push(conical->size(), 0); - new (pod) DlConicalGradientColorSource(conical); - break; - } - case DlColorSourceType::kSweepGradient: { - const DlSweepGradientColorSource* sweep = source->asSweepGradient(); - FML_DCHECK(sweep); - void* pod = Push(sweep->size(), 0); - new (pod) DlSweepGradientColorSource(sweep); - break; - } - case DlColorSourceType::kRuntimeEffect: { - const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect(); - FML_DCHECK(effect); - Push(0, 0, effect); - break; - } -#ifdef IMPELLER_ENABLE_3D - case DlColorSourceType::kScene: { - const DlSceneColorSource* scene = source->asScene(); - FML_DCHECK(scene); - Push(0, 0, scene); - break; - } -#endif // IMPELLER_ENABLE_3D - } - } -} -void DlOpRecorder::setImageFilter(const DlImageFilter* filter) { - if (filter == nullptr) { - Push(0, 0); - } else { - switch (filter->type()) { - case DlImageFilterType::kBlur: { - const DlBlurImageFilter* blur_filter = filter->asBlur(); - FML_DCHECK(blur_filter); - void* pod = Push(blur_filter->size(), 0); - new (pod) DlBlurImageFilter(blur_filter); - break; - } - case DlImageFilterType::kDilate: { - const DlDilateImageFilter* dilate_filter = filter->asDilate(); - FML_DCHECK(dilate_filter); - void* pod = Push(dilate_filter->size(), 0); - new (pod) DlDilateImageFilter(dilate_filter); - break; - } - case DlImageFilterType::kErode: { - const DlErodeImageFilter* erode_filter = filter->asErode(); - FML_DCHECK(erode_filter); - void* pod = Push(erode_filter->size(), 0); - new (pod) DlErodeImageFilter(erode_filter); - break; - } - case DlImageFilterType::kMatrix: { - const DlMatrixImageFilter* matrix_filter = filter->asMatrix(); - FML_DCHECK(matrix_filter); - void* pod = Push(matrix_filter->size(), 0); - new (pod) DlMatrixImageFilter(matrix_filter); - break; - } - case DlImageFilterType::kCompose: - case DlImageFilterType::kLocalMatrix: - case DlImageFilterType::kColorFilter: { - Push(0, 0, filter); - break; - } - } - } -} -void DlOpRecorder::setColorFilter(const DlColorFilter* filter) { - if (filter == nullptr) { - Push(0, 0); - } else { - switch (filter->type()) { - case DlColorFilterType::kBlend: { - const DlBlendColorFilter* blend_filter = filter->asBlend(); - FML_DCHECK(blend_filter); - void* pod = Push(blend_filter->size(), 0); - new (pod) DlBlendColorFilter(blend_filter); - break; - } - case DlColorFilterType::kMatrix: { - const DlMatrixColorFilter* matrix_filter = filter->asMatrix(); - FML_DCHECK(matrix_filter); - void* pod = Push(matrix_filter->size(), 0); - new (pod) DlMatrixColorFilter(matrix_filter); - break; - } - case DlColorFilterType::kSrgbToLinearGamma: { - void* pod = Push(filter->size(), 0); - new (pod) DlSrgbToLinearGammaColorFilter(); - break; - } - case DlColorFilterType::kLinearToSrgbGamma: { - void* pod = Push(filter->size(), 0); - new (pod) DlLinearToSrgbGammaColorFilter(); - break; - } - } - } -} -void DlOpRecorder::setPathEffect(const DlPathEffect* effect) { - if (effect == nullptr) { - Push(0, 0); - } else { - switch (effect->type()) { - case DlPathEffectType::kDash: { - const DlDashPathEffect* dash_effect = effect->asDash(); - void* pod = Push(dash_effect->size(), 0); - new (pod) DlDashPathEffect(dash_effect); - break; - } - } - } -} -void DlOpRecorder::setMaskFilter(const DlMaskFilter* filter) { - if (filter == nullptr) { - Push(0, 0); - } else { - switch (filter->type()) { - case DlMaskFilterType::kBlur: { - const DlBlurMaskFilter* blur_filter = filter->asBlur(); - FML_DCHECK(blur_filter); - void* pod = Push(blur_filter->size(), 0); - new (pod) DlBlurMaskFilter(blur_filter); - break; - } - } - } -} - -void DlOpRecorder::save() { - save_infos_.push_back({ - .offset = 0u, - .deferred = true, - .is_layer = false, - }); - tracker_->save(); - accumulator_->save(); -} -void DlOpRecorder::ResolveDeferredSave() { - SaveInfo& save_info_ref = save_infos_.back(); - if (save_info_ref.deferred) { - FML_DCHECK(save_info_ref.is_layer == false); - FML_DCHECK(save_info_ref.offset == 0u); - save_info_ref.offset = storage_.used(); - save_info_ref.deferred = false; - Push(0, 1); - } -} -void DlOpRecorder::saveLayer(const SkRect* bounds, - const SaveLayerOptions options, - const DlImageFilter* backdrop) { - save_infos_.push_back({ - .offset = storage_.used(), - .deferred = false, - .is_layer = true, - }); - tracker_->save(); - accumulator_->save(); - if (backdrop) { - bounds // - ? Push(0, 1, options, *bounds, backdrop) - : Push(0, 1, options, backdrop); - } else { - bounds // - ? Push(0, 1, options, *bounds) - : Push(0, 1, options); - } -} - -void DlOpRecorder::restore() { - FML_DCHECK(!save_infos_.empty()); - { // Ensure all uses of save_info_ref occur before pop_back() - const SaveInfo& save_info_ref = save_infos_.back(); - if (save_info_ref.is_layer) { - // This should only happen when unrolling the save stack - // in the Build() method. - restoreLayer(nullptr, false, false); - return; - } - - if (!save_info_ref.deferred) { - SaveOpBase* op = - reinterpret_cast(storage_.get() + save_info_ref.offset); - FML_DCHECK(op->type == DisplayListOpType::kSave); - op->restore_index = op_index_; - - Push(0, 1); - } - } // save_info_ref no longer accessible - save_infos_.pop_back(); - - tracker_->restore(); - accumulator_->restore(); -} -void DlOpRecorder::restoreLayer(const DlImageFilter* filter, - bool layer_content_was_unbounded, - bool layer_could_distribute_opacity) { - FML_DCHECK(!save_infos_.empty()); - { // Ensure all uses of save_info_ref occur before pop_back() - SaveInfo& save_info_ref = save_infos_.back(); - FML_DCHECK(save_info_ref.is_layer == true); - FML_DCHECK(save_info_ref.deferred == false); - - SaveOpBase* op = - reinterpret_cast(storage_.get() + save_info_ref.offset); - FML_DCHECK(op->type == DisplayListOpType::kSaveLayer || - op->type == DisplayListOpType::kSaveLayerBounds || - op->type == DisplayListOpType::kSaveLayerBackdrop || - op->type == DisplayListOpType::kSaveLayerBackdropBounds); - op->restore_index = op_index_; - if (layer_could_distribute_opacity) { - op->options = op->options.with_can_distribute_opacity(); - } - } // save_info_ref no longer accessible - save_infos_.pop_back(); - - Push(0, 1); - - // Manage the layer bounds before we push the restore op so that any - // bounds we need to adjust get tagged on the RestoreOp rather than - // the rendering op that follows it. - - // Restore the tracker before we manage the layer bounds so that we use - // the enclosing cull_rect and transform for filtering bounds. - tracker_->restore(); - const SkRect clip = tracker_->device_cull_rect(); - - // As we pop the accumulator we will adjust the bounds associated with - // the layer content by the layer filter. - // We have already restored the tracker so that the cull_rect information - // we use in the adjustment is from the environment outside of the layer. - // If there is a failure in converting the bounds within the layer due - // to an issue with the layer filter, or if the content of the layer - // was already unbounded, we will propagate the unbounded status to the - // enclosing layer. - if (filter) { - const SkMatrix matrix = tracker_->matrix_3x3(); - if (!accumulator_->restore( - [filter, matrix](const SkRect& input, SkRect& output) { - SkIRect output_bounds; - bool ret = filter->map_device_bounds(input.roundOut(), matrix, - output_bounds); - output.set(output_bounds); - return ret; - }, - &clip)) { - layer_content_was_unbounded = true; - } - } else { - accumulator_->restore(); - } - - if (layer_content_was_unbounded) { - // Ideally we would insert this back in the list of rects with the - // OpID of the original SaveLayer... - if (!clip.isEmpty()) { - accumulator_->accumulate(clip, render_op_count_); - } - } -} - -void DlOpRecorder::translate(SkScalar tx, SkScalar ty) { - ResolveDeferredSave(); - tracker_->translate(tx, ty); - Push(0, 1, tx, ty); -} -void DlOpRecorder::scale(SkScalar sx, SkScalar sy) { - ResolveDeferredSave(); - tracker_->scale(sx, sy); - Push(0, 1, sx, sy); -} -void DlOpRecorder::rotate(SkScalar degrees) { - ResolveDeferredSave(); - tracker_->rotate(degrees); - Push(0, 1, degrees); -} -void DlOpRecorder::skew(SkScalar sx, SkScalar sy) { - ResolveDeferredSave(); - tracker_->skew(sx, sy); - Push(0, 1, sx, sy); -} - -// clang-format off - -// 2x3 2D affine subset of a 4x4 transform in row major order -void DlOpRecorder::transform2DAffine( - SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) { - ResolveDeferredSave(); - tracker_->transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - Push(0, 1, - mxx, mxy, mxt, - myx, myy, myt); -} -// full 4x4 transform in row major order -void DlOpRecorder::transformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) { - ResolveDeferredSave(); - tracker_->transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - Push(0, 1, - mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); -} -// clang-format on -void DlOpRecorder::transformReset() { - ResolveDeferredSave(); - tracker_->setIdentity(); - Push(0, 0); -} - -void DlOpRecorder::clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) { - ResolveDeferredSave(); - tracker_->clipRect(rect, clip_op, is_aa); - if (!tracker_->is_cull_rect_empty()) { - switch (clip_op) { - case ClipOp::kIntersect: - Push(0, 1, rect, is_aa); - break; - case ClipOp::kDifference: - Push(0, 1, rect, is_aa); - break; - } - } -} - -void DlOpRecorder::clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) { - ResolveDeferredSave(); - tracker_->clipRRect(rrect, clip_op, is_aa); - if (!tracker_->is_cull_rect_empty()) { - switch (clip_op) { - case ClipOp::kIntersect: - Push(0, 1, rrect, is_aa); - break; - case ClipOp::kDifference: - Push(0, 1, rrect, is_aa); - break; - } - } -} - -void DlOpRecorder::clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) { - ResolveDeferredSave(); - tracker_->clipPath(path, clip_op, is_aa); - if (!tracker_->is_cull_rect_empty()) { - switch (clip_op) { - case ClipOp::kIntersect: - Push(0, 1, path, is_aa); - break; - case ClipOp::kDifference: - Push(0, 1, path, is_aa); - break; - } - } -} -void DlOpRecorder::resetCullRect(const SkRect* cull_rect) { - tracker_->resetCullRect(cull_rect); -} -void DlOpRecorder::intersectCullRect(const SkRect& cull_rect) { - tracker_->clipRect(cull_rect, DlCanvas::ClipOp::kIntersect, false); -} - -void DlOpRecorder::drawPaint() { - Push(0, 1); -} -void DlOpRecorder::drawColor(DlColor color, DlBlendMode mode) { - Push(0, 1, color, mode); -} -void DlOpRecorder::drawLine(const SkPoint& p0, const SkPoint& p1) { - Push(0, 1, p0, p1); -} -void DlOpRecorder::drawRect(const SkRect& rect) { - Push(0, 1, rect); -} -void DlOpRecorder::drawOval(const SkRect& bounds) { - Push(0, 1, bounds); -} -void DlOpRecorder::drawCircle(const SkPoint& center, SkScalar radius) { - Push(0, 1, center, radius); -} -void DlOpRecorder::drawRRect(const SkRRect& rrect) { - Push(0, 1, rrect); -} -void DlOpRecorder::drawDRRect(const SkRRect& outer, const SkRRect& inner) { - Push(0, 1, outer, inner); -} -void DlOpRecorder::drawPath(const SkPath& path) { - Push(0, 1, path); -} - -void DlOpRecorder::drawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter) { - Push(0, 1, bounds, start, sweep, useCenter); -} - -void DlOpRecorder::drawPoints(PointMode mode, - uint32_t count, - const SkPoint pts[]) { - FML_DCHECK(count > 0); - FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); - int bytes = count * sizeof(SkPoint); - void* data_ptr; - switch (mode) { - case PointMode::kPoints: - data_ptr = Push(bytes, 1, count); - break; - case PointMode::kLines: - data_ptr = Push(bytes, 1, count); - break; - case PointMode::kPolygon: - data_ptr = Push(bytes, 1, count); - break; - default: - FML_UNREACHABLE(); - return; - } - CopyV(data_ptr, pts, count); -} -void DlOpRecorder::drawVertices(const DlVertices* vertices, DlBlendMode mode) { - void* pod = Push(vertices->size(), 1, mode); - new (pod) DlVertices(vertices); -} - -void DlOpRecorder::drawImage(const sk_sp& image, - const SkPoint point, - DlImageSampling sampling, - bool render_with_attributes) { - render_with_attributes - ? Push(0, 1, image, point, sampling) - : Push(0, 1, image, point, sampling); - is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); -} -void DlOpRecorder::drawImageRect(const sk_sp& image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - bool render_with_attributes, - SrcRectConstraint constraint) { - Push(0, 1, image, src, dst, sampling, // - render_with_attributes, constraint); - is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); -} -void DlOpRecorder::drawImageNine(const sk_sp& image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - bool render_with_attributes) { - render_with_attributes - ? Push(0, 1, image, center, dst, filter) - : Push(0, 1, image, center, dst, filter); - is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe(); -} -void DlOpRecorder::drawAtlas(const sk_sp& atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cull_rect, - bool render_with_attributes) { - int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); - void* data_ptr; - if (colors != nullptr) { - bytes += count * sizeof(DlColor); - if (cull_rect != nullptr) { - data_ptr = - Push(bytes, 1, atlas, count, mode, sampling, true, - *cull_rect, render_with_attributes); - } else { - data_ptr = Push(bytes, 1, atlas, count, mode, sampling, true, - render_with_attributes); - } - CopyV(data_ptr, xform, count, tex, count, colors, count); - } else { - if (cull_rect != nullptr) { - data_ptr = - Push(bytes, 1, atlas, count, mode, sampling, false, - *cull_rect, render_with_attributes); - } else { - data_ptr = Push(bytes, 1, atlas, count, mode, sampling, - false, render_with_attributes); - } - CopyV(data_ptr, xform, count, tex, count); - } - is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe(); -} - -void DlOpRecorder::drawDisplayList(const sk_sp& display_list, - SkScalar opacity) { - Push(0, 1, display_list, opacity); - nested_op_count_ += display_list->op_count(true) - 1; - nested_bytes_ += display_list->bytes(true); - is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe(); -} -void DlOpRecorder::drawTextBlob(const sk_sp& blob, - SkScalar x, - SkScalar y) { - Push(0, 1, blob, x, y); -} -void DlOpRecorder::drawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) { - transparent_occluder // - ? Push(0, 1, path, color, elevation, dpr) - : Push(0, 1, path, color, elevation, dpr); -} - -bool DlOpRecorder::accumulateLocalBoundsForNextOp(const SkRect& r) { - if (!r.isEmpty()) { - SkRect bounds = r; - tracker_->mapRect(&bounds); - if (bounds.intersect(tracker_->device_cull_rect())) { - accumulator_->accumulate(bounds, render_op_count_); - return true; - } - } - return false; -} -bool DlOpRecorder::accumulateUnboundedForNextOp() { - SkRect clip = tracker_->device_cull_rect(); - if (!clip.isEmpty()) { - accumulator_->accumulate(clip, render_op_count_); - return true; - } - return false; -} - -sk_sp DlOpRecorder::Build(bool can_distribute_opacity, - bool affects_transparent_layer) { - if (!storage_.is_valid()) { - FML_DCHECK(storage_.is_valid()); - return nullptr; - } - - while (save_infos_.size() > 1u) { - restore(); - } - - auto rtree = accumulator_->rtree(); - // It is faster to ask the completed rtree for bounds than to ask - // the accumulator to run through all of its rects for the bounds. - auto bounds = rtree ? rtree->bounds() : accumulator_->bounds(); - - return sk_sp( - new DisplayList(storage_.take(), render_op_count_, nested_bytes_, - nested_op_count_, bounds, can_distribute_opacity, - is_ui_thread_safe_, affects_transparent_layer, rtree)); -} - -} // namespace flutter diff --git a/display_list/dl_op_recorder.h b/display_list/dl_op_recorder.h deleted file mode 100644 index 438fcfeebbb29..0000000000000 --- a/display_list/dl_op_recorder.h +++ /dev/null @@ -1,210 +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. - -#ifndef FLUTTER_DISPLAY_LIST_DL_OP_RECORDER_H_ -#define FLUTTER_DISPLAY_LIST_DL_OP_RECORDER_H_ - -#include "flutter/display_list/dl_canvas_to_receiver.h" -#include "flutter/display_list/dl_op_receiver.h" -#include "flutter/display_list/utils/dl_bounds_accumulator.h" -#include "flutter/display_list/utils/dl_matrix_clip_tracker.h" - -namespace flutter { - -class DisplayList; - -//------------------------------------------------------------------------------ -/// @brief An implementation of DlOpReceiver that records the calls into -/// a buffer, typically driven from a DisplayListBuilder. -/// -class DlOpRecorder : public DlCanvasReceiver { - private: - using ClipOp = DlCanvas::ClipOp; - - public: - static constexpr SkRect kMaxCullRect = - SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); - - DlOpRecorder(const SkRect& cull_rect = kMaxCullRect, bool keep_rtree = false); - - ~DlOpRecorder() = default; - - // | DlCanvasReceiver| - SkRect base_device_cull_rect() const override { - return tracker_->base_device_cull_rect(); - } - SkRect device_cull_rect() const override { - return tracker_->device_cull_rect(); - } - SkRect local_cull_rect() const override { - return tracker_->local_cull_rect(); - } - bool is_cull_rect_empty() const override { - return tracker_->is_cull_rect_empty(); - } - bool content_culled(const SkRect& content_bounds) const override { - return tracker_->content_culled(content_bounds); - } - - // | DlCanvasReceiver| - SkM44 matrix_4x4() const override { return tracker_->matrix_4x4(); } - SkMatrix matrix_3x3() const override { return tracker_->matrix_3x3(); } - - // | DlCanvasReceiver| - void resetCullRect(const SkRect* cull_rect = nullptr) override; - void intersectCullRect(const SkRect& cull_rect) override; - - // | DlCanvasReceiver| - bool wants_granular_bounds() const override { - return accumulator_->type() == BoundsAccumulator::Type::kRTree; - } - - // |DlOpReceiver| all set methods - void setAntiAlias(bool aa) override; - void setDither(bool dither) override; - void setDrawStyle(DlDrawStyle style) override; - void setColor(DlColor color) override; - void setStrokeWidth(float width) override; - void setStrokeMiter(float limit) override; - void setStrokeCap(DlStrokeCap cap) override; - void setStrokeJoin(DlStrokeJoin join) override; - void setColorSource(const DlColorSource* source) override; - void setColorFilter(const DlColorFilter* filter) override; - void setInvertColors(bool invert) override; - void setBlendMode(DlBlendMode mode) override; - void setPathEffect(const DlPathEffect* effect) override; - void setMaskFilter(const DlMaskFilter* filter) override; - void setImageFilter(const DlImageFilter* filter) override; - - // |DlOpReceiver| - void save() override; - // |DlOpReceiver| - void saveLayer(const SkRect* bounds, - const SaveLayerOptions options, - const DlImageFilter* backdrop = nullptr) override; - - // |DlOpReceiver| - void restore() override; - // |DlCanvasReceiver| - void restoreLayer(const DlImageFilter*, - bool layer_content_was_unbounded, - bool layer_could_distribute_opacity) override; - - // |DlOpReceiver| all transform methods - void translate(SkScalar tx, SkScalar ty) override; - void scale(SkScalar sx, SkScalar sy) override; - void rotate(SkScalar degrees) override; - void skew(SkScalar sx, SkScalar sy) override; - - // clang-format off - // |DlOpReceiver| - void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) override; - // |DlOpReceiver| - void transformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override; - // clang-format on - - // |DlOpReceiver| - void transformReset() override; - - // |DlOpReceiver| all clip methods - void clipRect(const SkRect& rect, ClipOp clip_op, bool is_aa) override; - void clipRRect(const SkRRect& rrect, ClipOp clip_op, bool is_aa) override; - void clipPath(const SkPath& path, ClipOp clip_op, bool is_aa) override; - - // |DlOpReceiver| all render methods - void drawColor(DlColor color, DlBlendMode mode) override; - void drawPaint() override; - void drawLine(const SkPoint& p0, const SkPoint& p1) override; - void drawRect(const SkRect& rect) override; - void drawOval(const SkRect& bounds) override; - void drawCircle(const SkPoint& center, SkScalar radius) override; - void drawRRect(const SkRRect& rrect) override; - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; - void drawPath(const SkPath& path) override; - void drawArc(const SkRect& oval_bounds, - SkScalar start_degrees, - SkScalar sweep_degrees, - bool use_center) override; - void drawPoints(PointMode mode, - uint32_t count, - const SkPoint points[]) override; - void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp& image, - const SkPoint point, - DlImageSampling sampling, - bool render_with_attributes) override; - void drawImageRect( - const sk_sp& image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - bool render_with_attributes, - SrcRectConstraint constraint = SrcRectConstraint::kFast) override; - void drawImageNine(const sk_sp& image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - bool render_with_attributes) override; - void drawAtlas(const sk_sp& atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cull_rect, - bool render_with_attributes) override; - void drawDisplayList(const sk_sp& display_list, - SkScalar opacity = SK_Scalar1) override; - void drawTextBlob(const sk_sp& blob, - SkScalar x, - SkScalar y) override; - void drawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) override; - - bool accumulateLocalBoundsForNextOp(const SkRect& r) override; - bool accumulateUnboundedForNextOp() override; - - bool is_nop() override { return tracker_->is_cull_rect_empty(); } - - sk_sp Build(bool can_distribute_opacity = false, - bool affects_transparent_layer = true); - - private: - std::shared_ptr tracker_; - std::shared_ptr accumulator_; - DisplayList::DlStorage storage_; - - struct SaveInfo { - size_t offset; - bool deferred; - bool is_layer; - }; - std::vector save_infos_; - void ResolveDeferredSave(); - - int render_op_count_ = 0; - int op_index_ = 0; - - // bytes and ops from |drawPicture| and |drawDisplayList| - size_t nested_bytes_ = 0; - int nested_op_count_ = 0; - - bool is_ui_thread_safe_ = true; - - template - void* Push(size_t extra, int op_inc, Args&&... args); -}; - -} // namespace flutter - -#endif // FLUTTER_DISPLAY_LIST_DL_OP_RECORDER_H_ diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index 88876f3956141..47cdeb9d0e6fb 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -19,7 +19,7 @@ namespace flutter { // Structure holding the information necessary to dispatch and // potentially cull the DLOps during playback. // -// Generally drawing ops will execute as long as |cur_render_index| +// Generally drawing ops will execute as long as |cur_index| // is at or after |next_render_index|, so setting the latter // to 0 will render all primitives and setting it to MAX_INT // will skip all remaining rendering primitives. @@ -40,7 +40,7 @@ namespace flutter { struct DispatchContext { DlOpReceiver& receiver; - int cur_render_index; + int cur_index; int next_render_index; int next_restore_index; @@ -328,7 +328,6 @@ struct SaveOpBase : DLOp { bool needed = ctx.next_render_index <= restore_index; ctx.save_infos.emplace_back(ctx.next_restore_index, needed); ctx.next_restore_index = restore_index; - ctx.cur_render_index++; return needed; } }; @@ -418,44 +417,25 @@ struct SaveLayerBackdropBoundsOp final : SaveOpBase { : DisplayListCompare::kNotEqual; } }; -// The base object for the restore() op // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) -struct RestoreOpBase : DLOp { - RestoreOpBase() {} - - inline bool restore_needed(DispatchContext& ctx) const { - bool restore_needed; - { - // ensure all use of save_infos.back happens before the pop - DispatchContext::SaveInfo& info = ctx.save_infos.back(); - restore_needed = info.save_was_needed; - ctx.next_restore_index = info.previous_restore_index; - } - ctx.cur_render_index++; - ctx.save_infos.pop_back(); - return restore_needed; - } -}; -// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) -struct RestoreOp final : RestoreOpBase { +struct RestoreOp final : DLOp { static const auto kType = DisplayListOpType::kRestore; RestoreOp() {} void dispatch(DispatchContext& ctx) const { - if (restore_needed(ctx)) { + DispatchContext::SaveInfo& info = ctx.save_infos.back(); + if (info.save_was_needed) { ctx.receiver.restore(); } + ctx.next_restore_index = info.previous_restore_index; + ctx.save_infos.pop_back(); } }; struct TransformClipOpBase : DLOp { - inline bool tx_clip_needed(DispatchContext& ctx) const { - // We only dispatch a transform or clip if we are going to render - // something before it gets erased by the next restore. - bool tx_clip_needed = (ctx.next_render_index <= ctx.next_restore_index); - ctx.cur_render_index++; - return tx_clip_needed; + inline bool op_needed(const DispatchContext& context) const { + return context.next_render_index <= context.next_restore_index; } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes @@ -469,7 +449,7 @@ struct TranslateOp final : TransformClipOpBase { const SkScalar ty; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.translate(tx, ty); } } @@ -485,7 +465,7 @@ struct ScaleOp final : TransformClipOpBase { const SkScalar sy; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.scale(sx, sy); } } @@ -499,7 +479,7 @@ struct RotateOp final : TransformClipOpBase { const SkScalar degrees; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.rotate(degrees); } } @@ -515,7 +495,7 @@ struct SkewOp final : TransformClipOpBase { const SkScalar sy; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.skew(sx, sy); } } @@ -535,7 +515,7 @@ struct Transform2DAffineOp final : TransformClipOpBase { const SkScalar myx, myy, myt; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.transform2DAffine(mxx, mxy, mxt, // myx, myy, myt); } @@ -564,7 +544,7 @@ struct TransformFullPerspectiveOp final : TransformClipOpBase { const SkScalar mwx, mwy, mwz, mwt; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.transformFullPerspective(mxx, mxy, mxz, mxt, // myx, myy, myz, myt, // mzx, mzy, mzz, mzt, // @@ -580,7 +560,7 @@ struct TransformResetOp final : TransformClipOpBase { TransformResetOp() = default; void dispatch(DispatchContext& ctx) const { - if (tx_clip_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.transformReset(); } } @@ -607,7 +587,7 @@ struct TransformResetOp final : TransformClipOpBase { const Sk##shapetype shape; \ \ void dispatch(DispatchContext& ctx) const { \ - if (tx_clip_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.clip##shapetype(shape, DlCanvas::ClipOp::k##clipop, \ is_aa); \ } \ @@ -630,7 +610,7 @@ DEFINE_CLIP_SHAPE_OP(RRect, Difference) const SkPath path; \ \ void dispatch(DispatchContext& ctx) const { \ - if (tx_clip_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.clipPath(path, DlCanvas::ClipOp::k##clipop, is_aa); \ } \ } \ @@ -646,8 +626,8 @@ DEFINE_CLIP_PATH_OP(Difference) #undef DEFINE_CLIP_PATH_OP struct DrawOpBase : DLOp { - inline bool draw_needed(DispatchContext& ctx) const { - return ctx.cur_render_index++ >= ctx.next_render_index; + inline bool op_needed(const DispatchContext& ctx) const { + return ctx.cur_index >= ctx.next_render_index; } }; @@ -658,7 +638,7 @@ struct DrawPaintOp final : DrawOpBase { DrawPaintOp() {} void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawPaint(); } } @@ -674,7 +654,7 @@ struct DrawColorOp final : DrawOpBase { const DlBlendMode mode; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawColor(color, mode); } } @@ -694,7 +674,7 @@ struct DrawColorOp final : DrawOpBase { const arg_type arg_name; \ \ void dispatch(DispatchContext& ctx) const { \ - if (draw_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.draw##op_name(arg_name); \ } \ } \ @@ -714,7 +694,7 @@ struct DrawPathOp final : DrawOpBase { const SkPath path; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawPath(path); } } @@ -742,7 +722,7 @@ struct DrawPathOp final : DrawOpBase { const type2 name2; \ \ void dispatch(DispatchContext& ctx) const { \ - if (draw_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.draw##op_name(name1, name2); \ } \ } \ @@ -765,7 +745,7 @@ struct DrawArcOp final : DrawOpBase { const bool center; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawArc(bounds, start, sweep, center); } } @@ -786,7 +766,7 @@ struct DrawArcOp final : DrawOpBase { const uint32_t count; \ \ void dispatch(DispatchContext& ctx) const { \ - if (draw_needed(ctx)) { \ + if (op_needed(ctx)) { \ const SkPoint* pts = reinterpret_cast(this + 1); \ ctx.receiver.drawPoints(DlCanvas::PointMode::mode, count, pts); \ } \ @@ -812,7 +792,7 @@ struct DrawVerticesOp final : DrawOpBase { const DlBlendMode mode; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { const DlVertices* vertices = reinterpret_cast(this + 1); ctx.receiver.drawVertices(vertices, mode); @@ -836,7 +816,7 @@ struct DrawVerticesOp final : DrawOpBase { const sk_sp image; \ \ void dispatch(DispatchContext& ctx) const { \ - if (draw_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.drawImage(image, point, sampling, with_attributes); \ } \ } \ @@ -878,7 +858,7 @@ struct DrawImageRectOp final : DrawOpBase { const sk_sp image; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawImageRect(image, src, dst, sampling, render_with_attributes, constraint); } @@ -911,7 +891,7 @@ struct DrawImageRectOp final : DrawOpBase { const sk_sp image; \ \ void dispatch(DispatchContext& ctx) const { \ - if (draw_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.drawImageNine(image, center, dst, mode, \ render_with_attributes); \ } \ @@ -993,7 +973,7 @@ struct DrawAtlasOp final : DrawAtlasBaseOp { render_with_attributes) {} void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { const SkRSXform* xform = reinterpret_cast(this + 1); const SkRect* tex = reinterpret_cast(xform + count); const DlColor* colors = @@ -1038,7 +1018,7 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp { const SkRect cull_rect; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { const SkRSXform* xform = reinterpret_cast(this + 1); const SkRect* tex = reinterpret_cast(xform + count); const DlColor* colors = @@ -1072,7 +1052,7 @@ struct DrawDisplayListOp final : DrawOpBase { const sk_sp display_list; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawDisplayList(display_list, opacity); } } @@ -1098,7 +1078,7 @@ struct DrawTextBlobOp final : DrawOpBase { const sk_sp blob; void dispatch(DispatchContext& ctx) const { - if (draw_needed(ctx)) { + if (op_needed(ctx)) { ctx.receiver.drawTextBlob(blob, x, y); } } @@ -1121,7 +1101,7 @@ struct DrawTextBlobOp final : DrawOpBase { const SkPath path; \ \ void dispatch(DispatchContext& ctx) const { \ - if (draw_needed(ctx)) { \ + if (op_needed(ctx)) { \ ctx.receiver.drawShadow(path, color, elevation, transparent_occluder, \ dpr); \ } \ diff --git a/display_list/dl_op_spy.cc b/display_list/dl_op_spy.cc index 993fd09c17ced..6597bfd847cad 100644 --- a/display_list/dl_op_spy.cc +++ b/display_list/dl_op_spy.cc @@ -81,13 +81,13 @@ void DlOpSpy::drawVertices(const DlVertices* vertices, DlBlendMode mode) { // transparent needs examine all the pixels in the image object, which is slow. // Drawing a completely transparent image is not a valid use case, thus, such // case is ignored. -void DlOpSpy::drawImage(const sk_sp& image, +void DlOpSpy::drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { did_draw_ = true; } -void DlOpSpy::drawImageRect(const sk_sp& image, +void DlOpSpy::drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -95,14 +95,14 @@ void DlOpSpy::drawImageRect(const sk_sp& image, SrcRectConstraint constraint) { did_draw_ = true; } -void DlOpSpy::drawImageNine(const sk_sp& image, +void DlOpSpy::drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) { did_draw_ = true; } -void DlOpSpy::drawAtlas(const sk_sp& atlas, +void DlOpSpy::drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -113,7 +113,7 @@ void DlOpSpy::drawAtlas(const sk_sp& atlas, bool render_with_attributes) { did_draw_ = true; } -void DlOpSpy::drawDisplayList(const sk_sp& display_list, +void DlOpSpy::drawDisplayList(const sk_sp display_list, SkScalar opacity) { if (did_draw_ || opacity == 0) { return; @@ -122,7 +122,7 @@ void DlOpSpy::drawDisplayList(const sk_sp& display_list, display_list->Dispatch(receiver); did_draw_ |= receiver.did_draw(); } -void DlOpSpy::drawTextBlob(const sk_sp& blob, +void DlOpSpy::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { did_draw_ |= will_draw_; diff --git a/display_list/dl_op_spy.h b/display_list/dl_op_spy.h index 501fe37dbc262..30a05786fad06 100644 --- a/display_list/dl_op_spy.h +++ b/display_list/dl_op_spy.h @@ -59,23 +59,23 @@ class DlOpSpy final : public virtual DlOpReceiver, uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; void drawImageRect( - const sk_sp& image, + const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint = SrcRectConstraint::kFast) override; - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawAtlas(const sk_sp& atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -84,9 +84,9 @@ class DlOpSpy final : public virtual DlOpReceiver, DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) override; - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity = SK_Scalar1) override; - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/dl_op_spy_unittests.cc b/display_list/dl_op_spy_unittests.cc index a976e2cde80a6..b1d65989d1498 100644 --- a/display_list/dl_op_spy_unittests.cc +++ b/display_list/dl_op_spy_unittests.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_op_spy.h" #include "flutter/testing/testing.h" #include "third_party/skia/include/core/SkBitmap.h" diff --git a/display_list/dl_paint.h b/display_list/dl_paint.h index 88be6ccea9638..3d9220f57e3d6 100644 --- a/display_list/dl_paint.h +++ b/display_list/dl_paint.h @@ -176,7 +176,7 @@ class DlPaint { return maskFilter_; } const DlMaskFilter* getMaskFilterPtr() const { return maskFilter_.get(); } - DlPaint& setMaskFilter(std::shared_ptr filter) { + DlPaint& setMaskFilter(std::shared_ptr filter) { maskFilter_ = filter; return *this; } @@ -189,7 +189,7 @@ class DlPaint { return pathEffect_; } const DlPathEffect* getPathEffectPtr() const { return pathEffect_.get(); } - DlPaint& setPathEffect(std::shared_ptr pathEffect) { + DlPaint& setPathEffect(std::shared_ptr pathEffect) { pathEffect_ = pathEffect; return *this; } diff --git a/display_list/dl_vertices.h b/display_list/dl_vertices.h index b6749fcfe960c..7c3ed34253713 100644 --- a/display_list/dl_vertices.h +++ b/display_list/dl_vertices.h @@ -279,7 +279,7 @@ class DlVertices { return static_cast(base) + offset; } - friend class DlOpRecorder; + friend class DisplayListBuilder; }; } // namespace flutter diff --git a/display_list/dl_vertices_unittests.cc b/display_list/dl_vertices_unittests.cc index 4a18db7d2f903..f30f473b1c31f 100644 --- a/display_list/dl_vertices_unittests.cc +++ b/display_list/dl_vertices_unittests.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_vertices.h" #include "flutter/display_list/testing/dl_test_equality.h" #include "flutter/display_list/utils/dl_comparable.h" diff --git a/display_list/effects/dl_color_source.h b/display_list/effects/dl_color_source.h index 343e36031f462..11a141da9c905 100644 --- a/display_list/effects/dl_color_source.h +++ b/display_list/effects/dl_color_source.h @@ -418,7 +418,7 @@ class DlLinearGradientColorSource final : public DlGradientColorSourceBase { SkPoint end_point_; friend class DlColorSource; - friend class DlOpRecorder; + friend class DisplayListBuilder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlLinearGradientColorSource); }; @@ -481,7 +481,7 @@ class DlRadialGradientColorSource final : public DlGradientColorSourceBase { SkScalar radius_; friend class DlColorSource; - friend class DlOpRecorder; + friend class DisplayListBuilder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlRadialGradientColorSource); }; @@ -557,7 +557,7 @@ class DlConicalGradientColorSource final : public DlGradientColorSourceBase { SkScalar end_radius_; friend class DlColorSource; - friend class DlOpRecorder; + friend class DisplayListBuilder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlConicalGradientColorSource); }; @@ -625,7 +625,7 @@ class DlSweepGradientColorSource final : public DlGradientColorSourceBase { SkScalar end_; friend class DlColorSource; - friend class DlOpRecorder; + friend class DisplayListBuilder; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlSweepGradientColorSource); }; diff --git a/display_list/effects/dl_path_effect.h b/display_list/effects/dl_path_effect.h index 5d27ada061655..0f9d0be60fa54 100644 --- a/display_list/effects/dl_path_effect.h +++ b/display_list/effects/dl_path_effect.h @@ -114,7 +114,7 @@ class DlDashPathEffect final : public DlPathEffect { int count_; SkScalar phase_; - friend class DlOpRecorder; + friend class DisplayListBuilder; friend class DlPathEffect; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlDashPathEffect); diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc index 41bb175296325..def08fa8886a3 100644 --- a/display_list/skia/dl_sk_dispatcher.cc +++ b/display_list/skia/dl_sk_dispatcher.cc @@ -182,14 +182,14 @@ void DlSkCanvasDispatcher::drawVertices(const DlVertices* vertices, DlBlendMode mode) { canvas_->drawVertices(ToSk(vertices), ToSk(mode), paint()); } -void DlSkCanvasDispatcher::drawImage(const sk_sp& image, +void DlSkCanvasDispatcher::drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { canvas_->drawImage(image ? image->skia_image() : nullptr, point.fX, point.fY, ToSk(sampling), safe_paint(render_with_attributes)); } -void DlSkCanvasDispatcher::drawImageRect(const sk_sp& image, +void DlSkCanvasDispatcher::drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -199,7 +199,7 @@ void DlSkCanvasDispatcher::drawImageRect(const sk_sp& image, ToSk(sampling), safe_paint(render_with_attributes), ToSk(constraint)); } -void DlSkCanvasDispatcher::drawImageNine(const sk_sp& image, +void DlSkCanvasDispatcher::drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -214,7 +214,7 @@ void DlSkCanvasDispatcher::drawImageNine(const sk_sp& image, canvas_->drawImageNine(skia_image.get(), center, dst, ToSk(filter), safe_paint(render_with_attributes)); } -void DlSkCanvasDispatcher::drawAtlas(const sk_sp& atlas, +void DlSkCanvasDispatcher::drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -236,7 +236,7 @@ void DlSkCanvasDispatcher::drawAtlas(const sk_sp& atlas, safe_paint(render_with_attributes)); } void DlSkCanvasDispatcher::drawDisplayList( - const sk_sp& display_list, + const sk_sp display_list, SkScalar opacity) { const int restore_count = canvas_->getSaveCount(); @@ -264,7 +264,7 @@ void DlSkCanvasDispatcher::drawDisplayList( // Restore canvas state to what it was before dispatching. canvas_->restoreToCount(restore_count); } -void DlSkCanvasDispatcher::drawTextBlob(const sk_sp& blob, +void DlSkCanvasDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { canvas_->drawTextBlob(blob, x, y, paint()); diff --git a/display_list/skia/dl_sk_dispatcher.h b/display_list/skia/dl_sk_dispatcher.h index a5356f5075610..a7fc73bbe2a54 100644 --- a/display_list/skia/dl_sk_dispatcher.h +++ b/display_list/skia/dl_sk_dispatcher.h @@ -69,22 +69,22 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, bool useCenter) override; void drawPoints(PointMode mode, uint32_t count, const SkPoint pts[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageRect(const sk_sp& image, + void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint) override; - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawAtlas(const sk_sp& atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -93,9 +93,9 @@ class DlSkCanvasDispatcher : public virtual DlOpReceiver, DlImageSampling sampling, const SkRect* cullRect, bool render_with_attributes) override; - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/display_list/testing/dl_rendering_unittests.cc b/display_list/testing/dl_rendering_unittests.cc index a5a95b6d85004..23a80c2f13d92 100644 --- a/display_list/testing/dl_rendering_unittests.cc +++ b/display_list/testing/dl_rendering_unittests.cc @@ -5,7 +5,7 @@ #include #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_op_flags.h" #include "flutter/display_list/dl_sampling_options.h" #include "flutter/display_list/skia/dl_sk_canvas.h" diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc index bd7cb1a288e7c..0a53f0337afd9 100644 --- a/display_list/testing/dl_test_snippets.cc +++ b/display_list/testing/dl_test_snippets.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/display_list/testing/dl_test_snippets.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_op_receiver.h" namespace flutter { @@ -46,17 +46,17 @@ std::vector CreateAllAttributesOps() { {"SetAntiAlias", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(true); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(false); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setAntiAlias(false); }}, }}, {"SetDither", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setDither(true); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setDither(false); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setDither(false); }}, }}, {"SetInvertColors", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(true); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(false); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setInvertColors(false); }}, }}, {"SetStrokeCap", { @@ -64,7 +64,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kRound); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kSquare); }}, - {0, 8, 0, 0, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeCap(DlStrokeCap::kButt); }}, }}, {"SetStrokeJoin", @@ -73,7 +73,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kBevel); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kRound); }}, - {0, 8, 0, 0, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeJoin(DlStrokeJoin::kMiter); }}, }}, {"SetStyle", @@ -84,26 +84,26 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setDrawStyle(DlDrawStyle::kStrokeAndFill); }}, - {0, 8, 0, 0, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setDrawStyle(DlDrawStyle::kFill); }}, }}, {"SetStrokeWidth", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(1.0); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(5.0); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(0.0); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeWidth(0.0); }}, }}, {"SetStrokeMiter", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(0.0); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(5.0); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(4.0); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setStrokeMiter(4.0); }}, }}, {"SetColor", { {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorGREEN); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLUE); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLACK); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColor(SK_ColorBLACK); }}, }}, {"SetBlendMode", { @@ -111,7 +111,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kSrcIn); }}, {0, 8, 0, 0, [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kDstIn); }}, - {0, 8, 0, 0, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setBlendMode(DlBlendMode::kSrcOver); }}, }}, {"SetColorSource", @@ -127,7 +127,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setColorSource(kTestSource4.get()); }}, {0, 80 + 6 * 4, 0, 0, [](DlOpReceiver& r) { r.setColorSource(kTestSource5.get()); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColorSource(nullptr); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorSource(nullptr); }}, }}, {"SetImageFilter", { @@ -185,7 +185,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setImageFilter(&kTestCFImageFilter1); }}, {0, 24, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(&kTestCFImageFilter2); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(nullptr); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setImageFilter(nullptr); }}, {0, 24, 0, 0, [](DlOpReceiver& r) { r.setImageFilter( @@ -218,7 +218,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setColorFilter(DlLinearToSrgbGammaColorFilter::instance.get()); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setColorFilter(nullptr); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setColorFilter(nullptr); }}, }}, {"SetPathEffect", { @@ -227,7 +227,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setPathEffect(kTestPathEffect1.get()); }}, {0, 32, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(kTestPathEffect2.get()); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(nullptr); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setPathEffect(nullptr); }}, }}, {"SetMaskFilter", { @@ -241,7 +241,7 @@ std::vector CreateAllAttributesOps() { [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter4); }}, {0, 32, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(&kTestMaskFilter5); }}, - {0, 8, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(nullptr); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.setMaskFilter(nullptr); }}, }}, }; } @@ -354,7 +354,7 @@ std::vector CreateAllTransformOps() { {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(10, 10); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(10, 15); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(15, 10); }}, - {1, 16, 1, 16, [](DlOpReceiver& r) { r.translate(0, 0); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.translate(0, 0); }}, }}, {"Scale", { @@ -362,15 +362,15 @@ std::vector CreateAllTransformOps() { {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(2, 2); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(2, 3); }}, {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(3, 2); }}, - {1, 16, 1, 16, [](DlOpReceiver& r) { r.scale(1, 1); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.scale(1, 1); }}, }}, {"Rotate", { // cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix) {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(30); }}, {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(45); }}, - {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(0); }}, - {1, 8, 1, 32, [](DlOpReceiver& r) { r.rotate(360); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.rotate(0); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.rotate(360); }}, }}, {"Skew", { @@ -379,14 +379,14 @@ std::vector CreateAllTransformOps() { {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.1, 0.1); }}, {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.1, 0.2); }}, {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0.2, 0.1); }}, - {1, 16, 1, 32, [](DlOpReceiver& r) { r.skew(0, 0); }}, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.skew(0, 0); }}, }}, {"Transform2DAffine", { {1, 32, 1, 32, [](DlOpReceiver& r) { r.transform2DAffine(0, 1, 12, 1, 0, 33); }}, // r.transform(identity) is ignored - {1, 32, 1, 32, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.transform2DAffine(1, 0, 0, 0, 1, 0); }}, }}, {"TransformFullPerspective", @@ -397,13 +397,13 @@ std::vector CreateAllTransformOps() { 0, 0, 0, 12); }}, // r.transform(2D affine) is reduced to 2x3 - {1, 72, 1, 72, + {1, 32, 1, 32, [](DlOpReceiver& r) { r.transformFullPerspective(2, 1, 0, 4, 1, 3, 0, 5, 0, 0, 1, 0, 0, 0, 0, 1); }}, // r.transform(identity) is ignored - {1, 72, 1, 72, + {0, 0, 0, 0, [](DlOpReceiver& r) { r.transformFullPerspective(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); @@ -488,11 +488,13 @@ std::vector CreateAllClipOps() { [](DlOpReceiver& r) { r.clipPath(kTestPath1, DlCanvas::ClipOp::kDifference, false); }}, + // clipPath(rect) becomes clipRect {1, 24, 1, 24, [](DlOpReceiver& r) { r.clipPath(kTestPathRect, DlCanvas::ClipOp::kIntersect, true); }}, - {1, 24, 1, 24, + // clipPath(oval) becomes clipRRect + {1, 64, 1, 64, [](DlOpReceiver& r) { r.clipPath(kTestPathOval, DlCanvas::ClipOp::kIntersect, true); }}, diff --git a/display_list/testing/dl_test_snippets.h b/display_list/testing/dl_test_snippets.h index b9f9f52b606e8..536cb224e8ef9 100644 --- a/display_list/testing/dl_test_snippets.h +++ b/display_list/testing/dl_test_snippets.h @@ -6,7 +6,7 @@ #define FLUTTER_DISPLAY_LIST_TESTING_DL_TEST_SNIPPETS_H_ #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" diff --git a/display_list/utils/dl_bounds_accumulator.h b/display_list/utils/dl_bounds_accumulator.h index e741f11277c7e..5e2067f1894da 100644 --- a/display_list/utils/dl_bounds_accumulator.h +++ b/display_list/utils/dl_bounds_accumulator.h @@ -7,7 +7,6 @@ #include -#include "flutter/display_list/dl_canvas_to_receiver.h" #include "flutter/display_list/geometry/dl_rtree.h" #include "flutter/fml/logging.h" @@ -22,13 +21,13 @@ namespace flutter { +enum class BoundsAccumulatorType { + kRect, + kRTree, +}; + class BoundsAccumulator { public: - enum class Type { - kRect, - kRTree, - }; - /// function definition for modifying the bounds of a rectangle /// during a restore operation. The function is used primarily /// to account for the bounds impact of an ImageFilter on a @@ -49,8 +48,6 @@ class BoundsAccumulator { virtual ~BoundsAccumulator() = default; - virtual Type type() const = 0; - virtual void accumulate(const SkRect& r, int index = 0) = 0; /// Save aside the rects/bounds currently being accumulated and start @@ -88,6 +85,8 @@ class BoundsAccumulator { virtual SkRect bounds() const = 0; virtual std::shared_ptr rtree() const = 0; + + virtual BoundsAccumulatorType type() const = 0; }; class RectBoundsAccumulator final : public virtual BoundsAccumulator { @@ -109,7 +108,9 @@ class RectBoundsAccumulator final : public virtual BoundsAccumulator { return rect_.bounds(); } - Type type() const override { return Type::kRect; } + BoundsAccumulatorType type() const override { + return BoundsAccumulatorType::kRect; + } std::shared_ptr rtree() const override { return nullptr; } @@ -152,7 +153,9 @@ class RTreeBoundsAccumulator final : public virtual BoundsAccumulator { std::shared_ptr rtree() const override; - Type type() const override { return Type::kRTree; } + BoundsAccumulatorType type() const override { + return BoundsAccumulatorType::kRTree; + } private: std::vector rects_; diff --git a/display_list/utils/dl_matrix_clip_tracker.cc b/display_list/utils/dl_matrix_clip_tracker.cc index f9e30590303d2..2dd0e8fa31a39 100644 --- a/display_list/utils/dl_matrix_clip_tracker.cc +++ b/display_list/utils/dl_matrix_clip_tracker.cc @@ -4,7 +4,7 @@ #include "flutter/display_list/utils/dl_matrix_clip_tracker.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/fml/logging.h" namespace flutter { diff --git a/display_list/utils/dl_receiver_utils.h b/display_list/utils/dl_receiver_utils.h index 26c3db8ef774b..e2d07310368ae 100644 --- a/display_list/utils/dl_receiver_utils.h +++ b/display_list/utils/dl_receiver_utils.h @@ -100,22 +100,22 @@ class IgnoreDrawDispatchHelper : public virtual DlOpReceiver { uint32_t count, const SkPoint points[]) override {} void drawVertices(const DlVertices* vertices, DlBlendMode mode) override {} - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override {} - void drawImageRect(const sk_sp& image, + void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint) override {} - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override {} - void drawAtlas(const sk_sp& atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -124,9 +124,9 @@ class IgnoreDrawDispatchHelper : public virtual DlOpReceiver { DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) override {} - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity) override {} - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override {} void drawShadow(const SkPath& path, diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 9e0f2291743e7..ccaac58255225 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -8,7 +8,7 @@ #include #include -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/skia/dl_sk_canvas.h" #include "flutter/flow/surface_frame.h" #include "flutter/fml/memory/ref_counted.h" diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index 32f8396536512..61489a6ad38c8 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -68,6 +68,7 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } + reset_display_list(); layer->Paint(checkerboard_context()); { DisplayListBuilder expected_builder; @@ -88,8 +89,8 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { } expected_builder.Restore(); } - EXPECT_TRUE(DisplayListsEQ_Verbose(checkerboard_display_list(), - expected_builder.Build())); + EXPECT_TRUE( + DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } } @@ -144,6 +145,7 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } + reset_display_list(); layer->Paint(checkerboard_context()); { DisplayListBuilder expected_builder; @@ -164,8 +166,8 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { } expected_builder.Restore(); } - EXPECT_TRUE(DisplayListsEQ_Verbose(checkerboard_display_list(), - expected_builder.Build())); + EXPECT_TRUE( + DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } } @@ -219,6 +221,7 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } + reset_display_list(); layer->Paint(checkerboard_context()); { DisplayListBuilder expected_builder; @@ -239,8 +242,8 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { } expected_builder.Restore(); } - EXPECT_TRUE(DisplayListsEQ_Verbose(checkerboard_display_list(), - expected_builder.Build())); + EXPECT_TRUE( + DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); } } diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index e74daca596c94..03eaf0c672239 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -6,6 +6,7 @@ #include +#include "flutter/display_list/dl_builder.h" #include "flutter/flow/layer_snapshot_store.h" #include "flutter/flow/layers/cacheable_layer.h" #include "flutter/flow/layers/offscreen_surface.h" diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 2b215b23d059c..286021e028ac8 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -6,7 +6,7 @@ #include "flutter/flow/layers/display_list_layer.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/fml/macros.h" diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index 823bad96f2fa7..64046f5461f92 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -561,11 +561,9 @@ TEST_F(ImageFilterLayerTest, CacheImageFilterLayerSelf) { } // frame 2. - reset_display_list(); layer->Preroll(preroll_context()); layer->Paint(display_list_paint_context()); // frame 3. - reset_display_list(); layer->Preroll(preroll_context()); layer->Paint(display_list_paint_context()); diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 41604b681f215..e0dfda6593c8c 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -4,7 +4,7 @@ #include "flutter/display_list/benchmarking/dl_complexity.h" #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/testing/dl_test_snippets.h" #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/display_list_layer.h" diff --git a/flow/surface_frame.h b/flow/surface_frame.h index c1187588465eb..b653729e3ed5d 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -9,7 +9,7 @@ #include #include "flutter/common/graphics/gl_context_switch.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/skia/dl_sk_canvas.h" #include "flutter/fml/macros.h" #include "flutter/fml/time/time_point.h" diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc index 74ba1b6746e88..26b2a67f28d09 100644 --- a/flow/testing/diff_context_test.cc +++ b/flow/testing/diff_context_test.cc @@ -5,8 +5,7 @@ #include "diff_context_test.h" #include - -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" namespace flutter { namespace testing { diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index a85dd6f936559..178c307426c19 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -72,11 +72,11 @@ class LayerTestBase : public CanvasTestBase { .raster_cache = nullptr, // clang-format on }, + display_list_builder_(kDlBounds), display_list_paint_context_{ // clang-format off .state_stack = display_list_state_stack_, - // canvas is set below by resetting the display_list - .canvas = nullptr, + .canvas = &display_list_builder_, .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, @@ -88,8 +88,7 @@ class LayerTestBase : public CanvasTestBase { checkerboard_context_{ // clang-format off .state_stack = checkerboard_state_stack_, - // canvas is set below by resetting the display_list - .canvas = nullptr, + .canvas = &display_list_builder_, .gr_context = nullptr, .view_embedder = nullptr, .raster_time = raster_time_, @@ -101,8 +100,8 @@ class LayerTestBase : public CanvasTestBase { use_null_raster_cache(); preroll_state_stack_.set_preroll_delegate(kGiantRect, SkMatrix::I()); paint_state_stack_.set_delegate(&TestT::mock_canvas()); - reset_display_list(); - reset_checkerboard_display_list(); + display_list_state_stack_.set_delegate(&display_list_builder_); + checkerboard_state_stack_.set_delegate(&display_list_builder_); checkerboard_state_stack_.set_checkerboard_func(draw_checkerboard); checkerboard_paint_.setColor(checkerboard_color_); } @@ -171,44 +170,19 @@ class LayerTestBase : public CanvasTestBase { PaintContext& checkerboard_context() { return checkerboard_context_; } LayerSnapshotStore& layer_snapshot_store() { return snapshot_store_; } - sk_sp checkerboard_display_list() { - if (checkerboard_display_list_ == nullptr) { - if (checkerboard_builder_.get()) { - checkerboard_state_stack_.clear_delegate(); - checkerboard_display_list_ = checkerboard_builder_->Build(); - checkerboard_builder_.reset(); - checkerboard_context_.canvas = nullptr; - } - } - return checkerboard_display_list_; - } - sk_sp display_list() { if (display_list_ == nullptr) { - if (display_list_builder_.get()) { - display_list_state_stack_.clear_delegate(); - display_list_ = display_list_builder_->Build(); - display_list_builder_.reset(); - display_list_paint_context_.canvas = nullptr; - } + display_list_ = display_list_builder_.Build(); } return display_list_; } - void reset_checkerboard_display_list() { - checkerboard_display_list_ = nullptr; - checkerboard_state_stack_.clear_delegate(); - checkerboard_builder_.reset(new DisplayListBuilder(kDlBounds)); - checkerboard_state_stack_.set_delegate(checkerboard_builder_.get()); - checkerboard_context_.canvas = checkerboard_builder_.get(); - } - void reset_display_list() { display_list_ = nullptr; - display_list_state_stack_.clear_delegate(); - display_list_builder_.reset(new DisplayListBuilder(kDlBounds)); - display_list_state_stack_.set_delegate(display_list_builder_.get()); - display_list_paint_context_.canvas = display_list_builder_.get(); + // Build() will leave the builder in a state to start recording a new DL + display_list_builder_.Build(); + // Make sure we are starting from a fresh state stack + FML_DCHECK(display_list_state_stack_.is_empty()); } void enable_leaf_layer_tracing() { @@ -246,7 +220,6 @@ class LayerTestBase : public CanvasTestBase { } LayerStateStack preroll_state_stack_; - LayerStateStack display_list_state_stack_; LayerStateStack paint_state_stack_; LayerStateStack checkerboard_state_stack_; FixedRefreshRateStopwatch raster_time_; @@ -256,11 +229,10 @@ class LayerTestBase : public CanvasTestBase { std::unique_ptr raster_cache_; PrerollContext preroll_context_; PaintContext paint_context_; - std::unique_ptr display_list_builder_; + DisplayListBuilder display_list_builder_; + LayerStateStack display_list_state_stack_; sk_sp display_list_; PaintContext display_list_paint_context_; - std::unique_ptr checkerboard_builder_; - sk_sp checkerboard_display_list_; DlPaint checkerboard_paint_; PaintContext checkerboard_context_; LayerSnapshotStore snapshot_store_; diff --git a/flow/testing/mock_texture_unittests.cc b/flow/testing/mock_texture_unittests.cc index 6226446a5fd90..5f5bab254dd6e 100644 --- a/flow/testing/mock_texture_unittests.cc +++ b/flow/testing/mock_texture_unittests.cc @@ -4,7 +4,7 @@ #include "flutter/flow/testing/mock_texture.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/testing/display_list_testing.h" #include "gtest/gtest.h" diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 545d26c69dc7d..44d93adb4be13 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -972,7 +972,7 @@ void DlDispatcher::drawVertices(const flutter::DlVertices* vertices, } // |flutter::DlOpReceiver| -void DlDispatcher::drawImage(const sk_sp& image, +void DlDispatcher::drawImage(const sk_sp image, const SkPoint point, flutter::DlImageSampling sampling, bool render_with_attributes) { @@ -1001,7 +1001,7 @@ void DlDispatcher::drawImage(const sk_sp& image, // |flutter::DlOpReceiver| void DlDispatcher::drawImageRect( - const sk_sp& image, + const sk_sp image, const SkRect& src, const SkRect& dst, flutter::DlImageSampling sampling, @@ -1017,7 +1017,7 @@ void DlDispatcher::drawImageRect( } // |flutter::DlOpReceiver| -void DlDispatcher::drawImageNine(const sk_sp& image, +void DlDispatcher::drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, flutter::DlFilterMode filter, @@ -1031,7 +1031,7 @@ void DlDispatcher::drawImageNine(const sk_sp& image, } // |flutter::DlOpReceiver| -void DlDispatcher::drawAtlas(const sk_sp& atlas, +void DlDispatcher::drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const flutter::DlColor colors[], @@ -1050,7 +1050,7 @@ void DlDispatcher::drawAtlas(const sk_sp& atlas, // |flutter::DlOpReceiver| void DlDispatcher::drawDisplayList( - const sk_sp& display_list, + const sk_sp display_list, SkScalar opacity) { // Save all values that must remain untouched after the operation. Paint saved_paint = paint_; @@ -1108,7 +1108,7 @@ void DlDispatcher::drawDisplayList( } // |flutter::DlOpReceiver| -void DlDispatcher::drawTextBlob(const sk_sp& blob, +void DlDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { const auto maybe_text_frame = MakeTextFrameFromTextBlobSkia(blob); diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h index 150ad8c36b524..d9a36e2239980 100644 --- a/impeller/display_list/dl_dispatcher.h +++ b/impeller/display_list/dl_dispatcher.h @@ -172,13 +172,13 @@ class DlDispatcher final : public flutter::DlOpReceiver { flutter::DlBlendMode dl_mode) override; // |flutter::DlOpReceiver| - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, flutter::DlImageSampling sampling, bool render_with_attributes) override; // |flutter::DlOpReceiver| - void drawImageRect(const sk_sp& image, + void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, flutter::DlImageSampling sampling, @@ -186,14 +186,14 @@ class DlDispatcher final : public flutter::DlOpReceiver { SrcRectConstraint constraint) override; // |flutter::DlOpReceiver| - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, flutter::DlFilterMode filter, bool render_with_attributes) override; // |flutter::DlOpReceiver| - void drawAtlas(const sk_sp& atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const flutter::DlColor colors[], @@ -204,11 +204,11 @@ class DlDispatcher final : public flutter::DlOpReceiver { bool render_with_attributes) override; // |flutter::DlOpReceiver| - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity) override; // |flutter::DlOpReceiver| - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; diff --git a/impeller/display_list/dl_playground.h b/impeller/display_list/dl_playground.h index b7c1730ced436..f6ad193eb0241 100644 --- a/impeller/display_list/dl_playground.h +++ b/impeller/display_list/dl_playground.h @@ -5,7 +5,7 @@ #pragma once #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/fml/macros.h" #include "impeller/playground/playground_test.h" #include "third_party/skia/include/core/SkFont.h" diff --git a/impeller/display_list/dl_unittests.cc b/impeller/display_list/dl_unittests.cc index be073917a483d..f79148465c4e7 100644 --- a/impeller/display_list/dl_unittests.cc +++ b/impeller/display_list/dl_unittests.cc @@ -7,8 +7,8 @@ #include #include -#include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/dl_blend_mode.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/display_list/dl_color.h" #include "flutter/display_list/dl_paint.h" #include "flutter/display_list/dl_tile_mode.h" diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 57ebd4d52f732..be005f8131137 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/paint.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/fml/logging.h" #include "flutter/lib/ui/floating_point.h" #include "flutter/lib/ui/painting/color_filter.h" diff --git a/lib/ui/painting/picture_recorder.h b/lib/ui/painting/picture_recorder.h index 079294b86314d..ff0c3790d5fca 100644 --- a/lib/ui/painting/picture_recorder.h +++ b/lib/ui/painting/picture_recorder.h @@ -7,7 +7,7 @@ #include -#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/dl_builder.h" #include "flutter/lib/ui/dart_wrapper.h" #if IMPELLER_SUPPORTS_RENDERING diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 7b6102306355d..f54a90c192d19 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -791,7 +791,7 @@ void DisplayListStreamDispatcher::drawVertices(const DlVertices* vertices, out_array("indices", vertices->index_count(), vertices->indices()) << "), " << mode << ");" << std::endl; } -void DisplayListStreamDispatcher::drawImage(const sk_sp& image, +void DisplayListStreamDispatcher::drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) { @@ -801,7 +801,7 @@ void DisplayListStreamDispatcher::drawImage(const sk_sp& image, << "with attributes: " << render_with_attributes << ");" << std::endl; } -void DisplayListStreamDispatcher::drawImageRect(const sk_sp& image, +void DisplayListStreamDispatcher::drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, @@ -815,7 +815,7 @@ void DisplayListStreamDispatcher::drawImageRect(const sk_sp& image, << constraint << ");" << std::endl; } -void DisplayListStreamDispatcher::drawImageNine(const sk_sp& image, +void DisplayListStreamDispatcher::drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, @@ -827,7 +827,7 @@ void DisplayListStreamDispatcher::drawImageNine(const sk_sp& image, << "with attributes: " << render_with_attributes << ");" << std::endl; } -void DisplayListStreamDispatcher::drawAtlas(const sk_sp& atlas, +void DisplayListStreamDispatcher::drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -845,14 +845,14 @@ void DisplayListStreamDispatcher::drawAtlas(const sk_sp& atlas, << ");" << std::endl; } void DisplayListStreamDispatcher::drawDisplayList( - const sk_sp& display_list, SkScalar opacity) { + const sk_sp display_list, SkScalar opacity) { startl() << "drawDisplayList(" << "ID: " << display_list->unique_id() << ", " << "bounds: " << display_list->bounds() << ", " << "opacity: " << opacity << ");" << std::endl; } -void DisplayListStreamDispatcher::drawTextBlob(const sk_sp& blob, +void DisplayListStreamDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { startl() << "drawTextBlob(" diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index 3fde199fa271e..4f3830a204b44 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -115,22 +115,22 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { uint32_t count, const SkPoint points[]) override; void drawVertices(const DlVertices* vertices, DlBlendMode mode) override; - void drawImage(const sk_sp& image, + void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override; - void drawImageRect(const sk_sp& image, + void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint) override; - void drawImageNine(const sk_sp& image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, bool render_with_attributes) override; - void drawAtlas(const sk_sp& atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], @@ -139,9 +139,9 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { DlImageSampling sampling, const SkRect* cull_rect, bool render_with_attributes) override; - void drawDisplayList(const sk_sp& display_list, + void drawDisplayList(const sk_sp display_list, SkScalar opacity) override; - void drawTextBlob(const sk_sp& blob, + void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; void drawShadow(const SkPath& path, diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index b24918d10fa55..64d9a725f99ce 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -27,7 +27,7 @@ namespace flutter { namespace testing { -[[maybe_unused]] static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); +static constexpr SkRect kEmptyRect = SkRect::MakeEmpty(); // Mock |SkCanvas|, useful for writing tests that use Skia but do not interact // with the GPU. diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 9e13492bcc1fc..d427f2acab27d 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include -#include "display_list/display_list_builder.h" +#include "display_list/dl_builder.h" #include "display_list/utils/dl_receiver_utils.h" #include "gtest/gtest.h" #include "runtime/test_font_data.h" From 261e7a3b2f2f679778d698d6dd7d6ce8c305f440 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 5 Sep 2023 11:05:40 -0700 Subject: [PATCH 3/3] MakeVertices merge fix --- impeller/display_list/dl_aiks_canvas.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/dl_aiks_canvas.cc b/impeller/display_list/dl_aiks_canvas.cc index 78c972c56ee21..29e7f491e358c 100644 --- a/impeller/display_list/dl_aiks_canvas.cc +++ b/impeller/display_list/dl_aiks_canvas.cc @@ -963,8 +963,8 @@ void DlAiksCanvas::DrawPoints(PointMode mode, void DlAiksCanvas::DrawVertices(const flutter::DlVertices* vertices, flutter::DlBlendMode dl_mode, const flutter::DlPaint& paint) { - canvas_.DrawVertices(DlVerticesGeometry::MakeVertices(vertices), - ToBlendMode(dl_mode), ToPaint(paint)); + canvas_.DrawVertices(MakeVertices(vertices), ToBlendMode(dl_mode), + ToPaint(paint)); } // |flutter::DlCanvas|