diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 242783e947b27..4cd541e0fed4f 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -26,6 +26,7 @@ FILE: ../../../flutter/common/task_runners.cc FILE: ../../../flutter/common/task_runners.h FILE: ../../../flutter/flow/compositor_context.cc FILE: ../../../flutter/flow/compositor_context.h +FILE: ../../../flutter/flow/embedded_view_params_unittests.cc FILE: ../../../flutter/flow/embedded_views.cc FILE: ../../../flutter/flow/embedded_views.h FILE: ../../../flutter/flow/gl_context_switch.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index a8c3592ccecbf..85c55d2cfba0b 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -130,6 +130,7 @@ executable("flow_unittests") { testonly = true sources = [ + "embedded_view_params_unittests.cc", "flow_run_all_unittests.cc", "flow_test_utils.cc", "flow_test_utils.h", diff --git a/flow/embedded_view_params_unittests.cc b/flow/embedded_view_params_unittests.cc new file mode 100644 index 0000000000000..cb94936e17ddf --- /dev/null +++ b/flow/embedded_view_params_unittests.cc @@ -0,0 +1,98 @@ +// 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/embedded_views.h" +#include "flutter/fml/logging.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithNoMutations) { + MutatorsStack stack; + SkMatrix matrix; + + EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); + const SkRect& rect = params.finalBoundingRect(); + ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), 0)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 0)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), 1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.height(), 1)); +} + +TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithScale) { + MutatorsStack stack; + SkMatrix matrix = SkMatrix::Scale(2, 2); + stack.PushTransform(matrix); + + EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); + const SkRect& rect = params.finalBoundingRect(); + ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), 0)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 0)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), 2)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.height(), 2)); +} + +TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithTranslate) { + MutatorsStack stack; + SkMatrix matrix = SkMatrix::MakeTrans(1, 1); + stack.PushTransform(matrix); + + EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); + const SkRect& rect = params.finalBoundingRect(); + ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), 1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), 1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.height(), 1)); +} + +TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithRotation90) { + MutatorsStack stack; + SkMatrix matrix; + matrix.setRotate(90); + stack.PushTransform(matrix); + + EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); + const SkRect& rect = params.finalBoundingRect(); + + FML_DLOG(ERROR) << rect.x(); + ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), -1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 0)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), 1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.height(), 1)); +} + +TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithRotation45) { + MutatorsStack stack; + SkMatrix matrix; + matrix.setRotate(45); + stack.PushTransform(matrix); + + EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); + const SkRect& rect = params.finalBoundingRect(); + ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), -sqrt(2) / 2)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 0)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), sqrt(2))); + ASSERT_TRUE(SkScalarNearlyEqual(rect.height(), sqrt(2))); +} + +TEST(EmbeddedViewParams, + GetBoundingRectAfterMutationsWithTranslateScaleAndRotation) { + SkMatrix matrix = SkMatrix::MakeTrans(2, 2); + matrix.preScale(3, 3); + matrix.preRotate(90); + + MutatorsStack stack; + stack.PushTransform(matrix); + + EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); + const SkRect& rect = params.finalBoundingRect(); + ASSERT_TRUE(SkScalarNearlyEqual(rect.x(), -1)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.y(), 2)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.width(), 3)); + ASSERT_TRUE(SkScalarNearlyEqual(rect.height(), 3)); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 9cc90860101a8..3493f02be7415 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -186,21 +186,49 @@ class EmbeddedViewParams { public: EmbeddedViewParams() = default; + EmbeddedViewParams(SkMatrix matrix, + SkSize size_points, + MutatorsStack mutators_stack) + : matrix_(matrix), + size_points_(size_points), + mutators_stack_(mutators_stack) { + SkPath path; + SkRect starting_rect = SkRect::MakeSize(size_points); + path.addRect(starting_rect); + path.transform(matrix); + final_bounding_rect_ = path.computeTightBounds(); + } + EmbeddedViewParams(const EmbeddedViewParams& other) { - offsetPixels = other.offsetPixels; - sizePoints = other.sizePoints; - mutatorsStack = other.mutatorsStack; + size_points_ = other.size_points_; + mutators_stack_ = other.mutators_stack_; + matrix_ = other.matrix_; + final_bounding_rect_ = other.final_bounding_rect_; }; - SkPoint offsetPixels; - SkSize sizePoints; - MutatorsStack mutatorsStack; + // The original size of the platform view before any mutation matrix is + // applied. + const SkSize& sizePoints() const { return size_points_; }; + // The mutators stack contains the detailed step by step mutations for this + // platform view. + const MutatorsStack& mutatorsStack() const { return mutators_stack_; }; + // The bounding rect of the platform view after applying all the mutations. + // + // Clippings are ignored. + const SkRect& finalBoundingRect() const { return final_bounding_rect_; } bool operator==(const EmbeddedViewParams& other) const { - return offsetPixels == other.offsetPixels && - sizePoints == other.sizePoints && - mutatorsStack == other.mutatorsStack; + return size_points_ == other.size_points_ && + mutators_stack_ == other.mutators_stack_ && + final_bounding_rect_ == other.final_bounding_rect_ && + matrix_ == other.matrix_; } + + private: + SkMatrix matrix_; + SkSize size_points_; + MutatorsStack mutators_stack_; + SkRect final_bounding_rect_; }; enum class PostPrerollResult { kResubmitFrame, kSuccess }; diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 81c40a99f0d89..551a270b16542 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -27,11 +27,8 @@ void PlatformViewLayer::Preroll(PrerollContext* context, } context->has_platform_view = true; std::unique_ptr params = - std::make_unique(); - params->offsetPixels = - SkPoint::Make(matrix.getTranslateX(), matrix.getTranslateY()); - params->sizePoints = size_; - params->mutatorsStack = context->mutators_stack; + std::make_unique(matrix, size_, + context->mutators_stack); context->view_embedder->PrerollCompositeEmbeddedView(view_id_, std::move(params)); } 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 9485eedff5491..f1f46ab21b49c 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -63,10 +63,10 @@ SkRect AndroidExternalViewEmbedder::GetViewRect(int view_id) const { const EmbeddedViewParams& params = view_params_.at(view_id); // TODO(egarciad): The rect should be computed from the mutator stack. // https://github.com/flutter/flutter/issues/59821 - return SkRect::MakeXYWH(params.offsetPixels.x(), // - params.offsetPixels.y(), // - params.sizePoints.width() * device_pixel_ratio_, // - params.sizePoints.height() * device_pixel_ratio_ // + return SkRect::MakeXYWH(params.finalBoundingRect().x(), // + params.finalBoundingRect().y(), // + params.sizePoints().width() * device_pixel_ratio_, // + params.sizePoints().height() * device_pixel_ratio_ // ); } 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 bba1c126564a1..a0551a0a39dea 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 @@ -198,9 +198,11 @@ TEST(AndroidExternalViewEmbedder, PlatformViewRect) { embedder->BeginFrame(SkISize::Make(100, 100), nullptr, 1.5, raster_thread_merger); - auto view_params = std::make_unique(); - view_params->offsetPixels = SkPoint::Make(10, 20); - view_params->sizePoints = SkSize::Make(30, 40); + MutatorsStack stack; + SkMatrix matrix = SkMatrix::MakeTrans(10, 20); + stack.PushTransform(matrix); + auto view_params = + std::make_unique(matrix, SkSize::Make(30, 40), stack); auto view_id = 0; embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params)); @@ -219,14 +221,21 @@ TEST(AndroidExternalViewEmbedder, PlatformViewRect__ChangedParams) { raster_thread_merger); auto view_id = 0; - auto view_params_1 = std::make_unique(); - view_params_1->offsetPixels = SkPoint::Make(10, 20); - view_params_1->sizePoints = SkSize::Make(30, 40); + + MutatorsStack stack1; + SkMatrix matrix1 = SkMatrix::MakeTrans(10, 20); + stack1.PushTransform(matrix1); + auto view_params_1 = std::make_unique( + matrix1, SkSize::Make(30, 40), stack1); + embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params_1)); - auto view_params_2 = std::make_unique(); - view_params_2->offsetPixels = SkPoint::Make(50, 60); - view_params_2->sizePoints = SkSize::Make(70, 80); + MutatorsStack stack2; + SkMatrix matrix2 = SkMatrix::MakeTrans(50, 60); + stack2.PushTransform(matrix2); + auto view_params_2 = std::make_unique( + matrix2, SkSize::Make(70, 80), stack2); + embedder->PrerollCompositeEmbeddedView(view_id, std::move(view_params_2)); ASSERT_EQ(SkRect::MakeXYWH(50, 60, 105, 120), embedder->GetViewRect(view_id)); @@ -281,11 +290,14 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame__RecycleSurfaces) { embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger); // Add an Android view. - auto view_params_1 = std::make_unique(); - view_params_1->offsetPixels = SkPoint::Make(100, 100); + MutatorsStack stack1; + SkMatrix matrix1 = SkMatrix::MakeTrans(100, 100); + stack1.PushTransform(matrix1); // TODO(egarciad): Investigate why Flow applies the device pixel ratio to // the offsetPixels, but not the sizePoints. - view_params_1->sizePoints = SkSize::Make(200, 200); + auto view_params_1 = std::make_unique( + matrix1, SkSize::Make(200, 200), stack1); + embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1)); // This is the recording canvas flow writes to. auto canvas_1 = embedder->CompositeEmbeddedView(0); @@ -329,11 +341,14 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame__RecycleSurfaces) { embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger); // Add an Android view. - auto view_params_1 = std::make_unique(); - view_params_1->offsetPixels = SkPoint::Make(100, 100); + MutatorsStack stack1; + SkMatrix matrix1 = SkMatrix::MakeTrans(100, 100); + stack1.PushTransform(matrix1); // TODO(egarciad): Investigate why Flow applies the device pixel ratio to // the offsetPixels, but not the sizePoints. - view_params_1->sizePoints = SkSize::Make(200, 200); + auto view_params_1 = std::make_unique( + matrix1, SkSize::Make(200, 200), stack1); + embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1)); // This is the recording canvas flow writes to. auto canvas_1 = embedder->CompositeEmbeddedView(0); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index f590701e7e2cb..474e9f084c1ee 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -407,13 +407,14 @@ void FlutterPlatformViewsController::CompositeWithParams(int view_id, const EmbeddedViewParams& params) { - CGRect frame = CGRectMake(0, 0, params.sizePoints.width(), params.sizePoints.height()); + CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); UIView* touchInterceptor = touch_interceptors_[view_id].get(); touchInterceptor.layer.transform = CATransform3DIdentity; touchInterceptor.frame = frame; touchInterceptor.alpha = 1; - int currentClippingCount = CountClips(params.mutatorsStack); + const MutatorsStack& mutatorStack = params.mutatorsStack(); + int currentClippingCount = CountClips(mutatorStack); int previousClippingCount = clip_count_[view_id]; if (currentClippingCount != previousClippingCount) { clip_count_[view_id] = currentClippingCount; @@ -424,7 +425,7 @@ ReconstructClipViewsChain(currentClippingCount, touchInterceptor, oldPlatformViewRoot); root_views_[view_id] = fml::scoped_nsobject([newPlatformViewRoot retain]); } - ApplyMutators(params.mutatorsStack, touchInterceptor); + ApplyMutators(mutatorStack, touchInterceptor); } SkCanvas* FlutterPlatformViewsController::CompositeEmbeddedView(int view_id) { diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc index 3b634235f820a..4716ea15683dd 100644 --- a/shell/platform/embedder/embedder_layers.cc +++ b/shell/platform/embedder/embedder_layers.cc @@ -108,7 +108,7 @@ void EmbedderLayers::PushPlatformViewLayer( view.struct_size = sizeof(FlutterPlatformView); view.identifier = identifier; - const auto& mutators = params.mutatorsStack; + const auto& mutators = params.mutatorsStack(); std::vector mutations_array; @@ -180,10 +180,10 @@ void EmbedderLayers::PushPlatformViewLayer( layer.platform_view = platform_views_referenced_.back().get(); const auto layer_bounds = - SkRect::MakeXYWH(params.offsetPixels.x(), // - params.offsetPixels.y(), // - params.sizePoints.width() * device_pixel_ratio_, // - params.sizePoints.height() * device_pixel_ratio_ // + SkRect::MakeXYWH(params.finalBoundingRect().x(), // + params.finalBoundingRect().y(), // + params.sizePoints().width() * device_pixel_ratio_, // + params.sizePoints().height() * device_pixel_ratio_ // ); const auto transformed_layer_bounds =