From 75ea3e244542fcc7d18504f641b33d7e13cdfbd8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 19 Nov 2019 10:35:41 -0800 Subject: [PATCH] Expose the platform view mutator stack to custom compositors. (#13731) This allows custom compositors to affect scene builder modifications made to the platform view. Fixes https://github.com/flutter/flutter/issues/44211 Fixes b/143612326 --- ci/licenses_golden/licenses_flutter | 2 + flow/embedded_views.h | 1 + shell/platform/embedder/BUILD.gn | 2 + shell/platform/embedder/embedder.h | 70 +++- .../embedder_external_view_embedder.cc | 115 +----- shell/platform/embedder/embedder_layers.cc | 215 ++++++++++++ shell/platform/embedder/embedder_layers.h | 52 +++ shell/platform/embedder/fixtures/main.dart | 43 +++ .../embedder/tests/embedder_assertions.h | 166 ++++++++- .../embedder/tests/embedder_unittests.cc | 332 +++++++++++++++++- 10 files changed, 866 insertions(+), 132 deletions(-) create mode 100644 shell/platform/embedder/embedder_layers.cc create mode 100644 shell/platform/embedder/embedder_layers.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c6bcdb323..40add69d3 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -856,6 +856,8 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.cc FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.h FILE: ../../../flutter/shell/platform/embedder/embedder_include.c +FILE: ../../../flutter/shell/platform/embedder/embedder_layers.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_layers.h FILE: ../../../flutter/shell/platform/embedder/embedder_platform_message_response.cc FILE: ../../../flutter/shell/platform/embedder/embedder_platform_message_response.h FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.cc diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 13c3feefd..919ee83a8 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -19,6 +19,7 @@ namespace flutter { +// TODO(chinmaygarde): Make these enum names match the style guide. enum MutatorType { clip_rect, clip_rrect, clip_path, transform, opacity }; // Stores mutation information like clipping or transform. diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index a6429c6f8..abb7fe16c 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -32,6 +32,8 @@ template("embedder_source_set") { "embedder_external_view_embedder.cc", "embedder_external_view_embedder.h", "embedder_include.c", + "embedder_layers.cc", + "embedder_layers.h", "embedder_platform_message_response.cc", "embedder_platform_message_response.h", "embedder_render_target.cc", diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 2913e85ec..95724c423 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -464,6 +464,24 @@ typedef struct { double bottom; } FlutterRect; +typedef struct { + double x; + double y; +} FlutterPoint; + +typedef struct { + double width; + double height; +} FlutterSize; + +typedef struct { + FlutterRect rect; + FlutterSize upper_left_corner_radius; + FlutterSize upper_right_corner_radius; + FlutterSize lower_right_corner_radius; + FlutterSize lower_left_corner_radius; +} FlutterRoundedRect; + /// The identifier of the platform view. This identifier is specified by the /// application when a platform view is added to the scene via the /// `SceneBuilder.addPlatformView` call. @@ -667,6 +685,32 @@ typedef struct { VoidCallback destruction_callback; } FlutterSoftwareBackingStore; +typedef enum { + /// Indicates that the Flutter application requested that an opacity be + /// applied to the platform view. + kFlutterPlatformViewMutationTypeOpacity, + /// Indicates that the Flutter application requested that the platform view be + /// clipped using a rectangle. + kFlutterPlatformViewMutationTypeClipRect, + /// Indicates that the Flutter application requested that the platform view be + /// clipped using a rounded rectangle. + kFlutterPlatformViewMutationTypeClipRoundedRect, + /// Indicates that the Flutter application requested that the platform view be + /// transformed before composition. + kFlutterPlatformViewMutationTypeTransformation, +} FlutterPlatformViewMutationType; + +typedef struct { + /// The type of the mutation described by the subsequent union. + FlutterPlatformViewMutationType type; + union { + double opacity; + FlutterRect clip_rect; + FlutterRoundedRect clip_rounded_rect; + FlutterTransformation transformation; + }; +} FlutterPlatformViewMutation; + typedef struct { /// The size of this struct. Must be sizeof(FlutterPlatformView). size_t struct_size; @@ -674,6 +718,22 @@ typedef struct { /// application when a platform view is added to the scene via the /// `SceneBuilder.addPlatformView` call. FlutterPlatformViewIdentifier identifier; + /// The number of mutations to be applied to the platform view by the embedder + /// before on-screen composition. + size_t mutations_count; + /// The mutations to be applied by this platform view before it is composited + /// on-screen. The Flutter application may transform the platform view but + /// these transformations cannot be affected by the Flutter compositor because + /// it does not render platform views. Since the embedder is responsible for + /// composition of these views, it is also the embedder's responsibility to + /// affect the appropriate transformation. + /// + /// The mutations must be applied in order. The mutations done in the + /// collection don't take into account the device pixel ratio or the root + /// surface transformation. If these exist, the first mutation in the list + /// will be a transformation mutation to make sure subsequent mutations are in + /// the correct coordinate space. + const FlutterPlatformViewMutation** mutations; } FlutterPlatformView; typedef enum { @@ -704,16 +764,6 @@ typedef struct { }; } FlutterBackingStore; -typedef struct { - double x; - double y; -} FlutterPoint; - -typedef struct { - double width; - double height; -} FlutterSize; - typedef struct { /// The size of this struct. Must be sizeof(FlutterBackingStoreConfig). size_t struct_size; diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index dcb6f977b..ac20b010a 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -6,6 +6,7 @@ #include +#include "flutter/shell/platform/embedder/embedder_layers.h" #include "flutter/shell/platform/embedder/embedder_render_target.h" namespace flutter { @@ -146,71 +147,6 @@ SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) { return found->second->GetSpyingCanvas(); } -static FlutterLayer MakeBackingStoreLayer( - const SkISize& frame_size, - const FlutterBackingStore* store, - const SkMatrix& surface_transformation) { - FlutterLayer layer = {}; - - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypeBackingStore; - layer.backing_store = store; - - const auto layer_bounds = - SkRect::MakeWH(frame_size.width(), frame_size.height()); - - const auto transformed_layer_bounds = - surface_transformation.mapRect(layer_bounds); - - layer.offset.x = transformed_layer_bounds.x(); - layer.offset.y = transformed_layer_bounds.y(); - layer.size.width = transformed_layer_bounds.width(); - layer.size.height = transformed_layer_bounds.height(); - - return layer; -} - -static FlutterPlatformView MakePlatformView( - FlutterPlatformViewIdentifier identifier) { - FlutterPlatformView view = {}; - - view.struct_size = sizeof(view); - - view.identifier = identifier; - - return view; -} - -static FlutterLayer MakePlatformViewLayer( - const EmbeddedViewParams& params, - const FlutterPlatformView& platform_view, - const SkMatrix& surface_transformation, - double device_pixel_ratio) { - FlutterLayer layer = {}; - - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypePlatformView; - layer.platform_view = &platform_view; - - const auto layer_bounds = SkRect::MakeXYWH(params.offsetPixels.x(), // - params.offsetPixels.y(), // - params.sizePoints.width(), // - params.sizePoints.height() // - ); - - const auto transformed_layer_bounds = - SkMatrix::Concat(surface_transformation, - SkMatrix::MakeScale(device_pixel_ratio)) - .mapRect(layer_bounds); - - layer.offset.x = transformed_layer_bounds.x(); - layer.offset.y = transformed_layer_bounds.y(); - layer.size.width = transformed_layer_bounds.width(); - layer.size.height = transformed_layer_bounds.height(); - - return layer; -} - bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget( sk_sp picture, const EmbedderRenderTarget* render_target) const { @@ -240,11 +176,10 @@ bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget( // |ExternalViewEmbedder| bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { - std::map - presented_platform_views; - // Layers may contain pointers to platform views in the collection above. - std::vector presented_layers; Registry render_targets_used; + EmbedderLayers presented_layers(pending_frame_size_, + pending_device_pixel_ratio_, + pending_surface_transformation_); if (!root_render_target_) { FML_LOG(ERROR) @@ -264,11 +199,8 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { { // The root surface is expressed as a layer. - presented_layers.push_back(MakeBackingStoreLayer( - pending_frame_size_, // frame size - root_render_target_->GetBackingStore(), // backing store - pending_surface_transformation_ // surface transformation - )); + presented_layers.PushBackingStoreLayer( + root_render_target_->GetBackingStore()); } const auto surface_size = TransformedSurfaceSize( @@ -289,21 +221,12 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { return false; } - // Indicate a layer for the platform view. Add to `presented_platform_views` - // in order to keep at allocated just for the scope of the current method. - // The layers presented to the embedder will contain a back pointer to this - // struct. It is safe to deallocate when the embedder callback is done. - presented_platform_views[view_id] = MakePlatformView(view_id); - presented_layers.push_back(MakePlatformViewLayer( - params, // embedded view params - presented_platform_views.at(view_id), // platform view - pending_surface_transformation_, // surface transformation - pending_device_pixel_ratio_ // device pixel ratio - )); + // Tell the embedder that a platform view layer is present at this point. + presented_layers.PushPlatformViewLayer(view_id, params); if (!pending_canvas_spies_.at(view_id)->DidDrawIntoCanvas()) { - // Nothing was drawn into the overlay canvas, we don't need to composite - // it. + // Nothing was drawn into the overlay canvas, we don't need to tell the + // embedder to composite it. continue; } @@ -340,22 +263,14 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { // Indicate a layer for the backing store containing contents rendered by // Flutter. - presented_layers.push_back(MakeBackingStoreLayer( - pending_frame_size_, // frame size - render_target->GetBackingStore(), // backing store - pending_surface_transformation_ // surface transformation - )); + presented_layers.PushBackingStoreLayer(render_target->GetBackingStore()); } - { - std::vector presented_layers_pointers; - presented_layers_pointers.reserve(presented_layers.size()); - for (const auto& layer : presented_layers) { - presented_layers_pointers.push_back(&layer); - } - present_callback_(std::move(presented_layers_pointers)); - } + // Flush the layer description down to the embedder for presentation. + presented_layers.InvokePresentCallback(present_callback_); + // Keep the previously used render target around in case they are required + // next frame. registry_ = std::move(render_targets_used); return true; diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc new file mode 100644 index 000000000..6b26edbcb --- /dev/null +++ b/shell/platform/embedder/embedder_layers.cc @@ -0,0 +1,215 @@ +// 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/shell/platform/embedder/embedder_layers.h" + +#include + +namespace flutter { + +EmbedderLayers::EmbedderLayers(SkISize frame_size, + double device_pixel_ratio, + SkMatrix root_surface_transformation) + : frame_size_(frame_size), + device_pixel_ratio_(device_pixel_ratio), + root_surface_transformation_(root_surface_transformation) {} + +EmbedderLayers::~EmbedderLayers() = default; + +void EmbedderLayers::PushBackingStoreLayer(const FlutterBackingStore* store) { + FlutterLayer layer = {}; + + layer.struct_size = sizeof(FlutterLayer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = store; + + const auto layer_bounds = + SkRect::MakeWH(frame_size_.width(), frame_size_.height()); + + const auto transformed_layer_bounds = + root_surface_transformation_.mapRect(layer_bounds); + + layer.offset.x = transformed_layer_bounds.x(); + layer.offset.y = transformed_layer_bounds.y(); + layer.size.width = transformed_layer_bounds.width(); + layer.size.height = transformed_layer_bounds.height(); + + presented_layers_.push_back(layer); +} + +static std::unique_ptr ConvertMutation( + double opacity) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = opacity; + return std::make_unique(mutation); +} + +static std::unique_ptr ConvertMutation( + const SkRect& rect) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect.left = rect.left(); + mutation.clip_rect.top = rect.top(); + mutation.clip_rect.right = rect.right(); + mutation.clip_rect.bottom = rect.bottom(); + return std::make_unique(mutation); +} + +static FlutterSize VectorToSize(const SkVector& vector) { + FlutterSize size = {}; + size.width = vector.x(); + size.height = vector.y(); + return size; +} + +static std::unique_ptr ConvertMutation( + const SkRRect& rrect) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeClipRoundedRect; + const auto& rect = rrect.rect(); + mutation.clip_rounded_rect.rect.left = rect.left(); + mutation.clip_rounded_rect.rect.top = rect.top(); + mutation.clip_rounded_rect.rect.right = rect.right(); + mutation.clip_rounded_rect.rect.bottom = rect.bottom(); + mutation.clip_rounded_rect.upper_left_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kUpperLeft_Corner)); + mutation.clip_rounded_rect.upper_right_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kUpperRight_Corner)); + mutation.clip_rounded_rect.lower_right_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kLowerRight_Corner)); + mutation.clip_rounded_rect.lower_left_corner_radius = + VectorToSize(rrect.radii(SkRRect::Corner::kLowerLeft_Corner)); + return std::make_unique(mutation); +} + +static std::unique_ptr ConvertMutation( + const SkMatrix& matrix) { + FlutterPlatformViewMutation mutation = {}; + mutation.type = kFlutterPlatformViewMutationTypeTransformation; + mutation.transformation.scaleX = matrix[SkMatrix::kMScaleX]; + mutation.transformation.skewX = matrix[SkMatrix::kMSkewX]; + mutation.transformation.transX = matrix[SkMatrix::kMTransX]; + mutation.transformation.skewY = matrix[SkMatrix::kMSkewY]; + mutation.transformation.scaleY = matrix[SkMatrix::kMScaleY]; + mutation.transformation.transY = matrix[SkMatrix::kMTransY]; + mutation.transformation.pers0 = matrix[SkMatrix::kMPersp0]; + mutation.transformation.pers1 = matrix[SkMatrix::kMPersp1]; + mutation.transformation.pers2 = matrix[SkMatrix::kMPersp2]; + return std::make_unique(mutation); +} + +void EmbedderLayers::PushPlatformViewLayer( + FlutterPlatformViewIdentifier identifier, + const EmbeddedViewParams& params) { + { + FlutterPlatformView view = {}; + view.struct_size = sizeof(FlutterPlatformView); + view.identifier = identifier; + + const auto& mutators = params.mutatorsStack; + + std::vector mutations_array; + + if (std::distance(mutators.Bottom(), mutators.Top()) > 0) { + // If there are going to be any mutations, they must first take into + // account the transformation for the device pixel ratio and root surface + // transformation. + auto base_xformation = + SkMatrix::Concat(root_surface_transformation_, + SkMatrix::MakeScale(device_pixel_ratio_)); + if (!base_xformation.isIdentity()) { + mutations_array.push_back( + mutations_referenced_.emplace_back(ConvertMutation(base_xformation)) + .get()); + } + } + + for (auto i = mutators.Bottom(); i != mutators.Top(); ++i) { + const auto& mutator = *i; + switch (mutator->GetType()) { + case MutatorType::clip_rect: { + mutations_array.push_back( + mutations_referenced_ + .emplace_back(ConvertMutation(mutator->GetRect())) + .get()); + } break; + case MutatorType::clip_rrect: { + mutations_array.push_back( + mutations_referenced_ + .emplace_back(ConvertMutation(mutator->GetRRect())) + .get()); + } break; + case MutatorType::clip_path: { + // Unsupported mutation. + } break; + case MutatorType::transform: { + const auto& matrix = mutator->GetMatrix(); + if (!matrix.isIdentity()) { + mutations_array.push_back( + mutations_referenced_.emplace_back(ConvertMutation(matrix)) + .get()); + } + } break; + case MutatorType::opacity: { + const double opacity = + std::clamp(mutator->GetAlphaFloat(), 0.0f, 1.0f); + if (opacity < 1.0) { + mutations_array.push_back( + mutations_referenced_.emplace_back(ConvertMutation(opacity)) + .get()); + } + } break; + } + } + + if (mutations_array.size() > 0) { + auto mutations = + std::make_unique>( + mutations_array); + mutations_arrays_referenced_.emplace_back(std::move(mutations)); + + view.mutations_count = mutations_array.size(); + view.mutations = mutations_arrays_referenced_.back().get()->data(); + } + + platform_views_referenced_.emplace_back( + std::make_unique(view)); + } + + FlutterLayer layer = {}; + + layer.struct_size = sizeof(FlutterLayer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = platform_views_referenced_.back().get(); + + const auto layer_bounds = SkRect::MakeXYWH(params.offsetPixels.x(), // + params.offsetPixels.y(), // + params.sizePoints.width(), // + params.sizePoints.height() // + ); + + const auto transformed_layer_bounds = + SkMatrix::Concat(root_surface_transformation_, + SkMatrix::MakeScale(device_pixel_ratio_)) + .mapRect(layer_bounds); + + layer.offset.x = transformed_layer_bounds.x(); + layer.offset.y = transformed_layer_bounds.y(); + layer.size.width = transformed_layer_bounds.width(); + layer.size.height = transformed_layer_bounds.height(); + + presented_layers_.push_back(layer); +} // namespace flutter + +void EmbedderLayers::InvokePresentCallback(PresentCallback callback) const { + std::vector presented_layers_pointers; + presented_layers_pointers.reserve(presented_layers_.size()); + for (const auto& layer : presented_layers_) { + presented_layers_pointers.push_back(&layer); + } + callback(std::move(presented_layers_pointers)); +} + +} // namespace flutter diff --git a/shell/platform/embedder/embedder_layers.h b/shell/platform/embedder/embedder_layers.h new file mode 100644 index 000000000..8f179f3c7 --- /dev/null +++ b/shell/platform/embedder/embedder_layers.h @@ -0,0 +1,52 @@ +// 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_SHELL_PLATFORM_EMBEDDER_EMBEDDER_FLUTTER_LAYERS_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_FLUTTER_LAYERS_H_ + +#include +#include + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flutter { + +class EmbedderLayers { + public: + EmbedderLayers(SkISize frame_size, + double device_pixel_ratio, + SkMatrix root_surface_transformation); + + ~EmbedderLayers(); + + void PushBackingStoreLayer(const FlutterBackingStore* store); + + void PushPlatformViewLayer(FlutterPlatformViewIdentifier identifier, + const EmbeddedViewParams& params); + + using PresentCallback = + std::function& layers)>; + void InvokePresentCallback(PresentCallback callback) const; + + private: + const SkISize frame_size_; + const double device_pixel_ratio_; + const SkMatrix root_surface_transformation_; + std::vector> platform_views_referenced_; + std::vector> + mutations_referenced_; + std::vector>> + mutations_arrays_referenced_; + std::vector presented_layers_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderLayers); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_FLUTTER_LAYERS_H_ diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index aa388c128..5e8b0e2c6 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -557,3 +557,46 @@ void push_frames_over_and_over() { }; window.scheduleFrame(); } + + +@pragma('vm:entry-point') +void platform_view_mutators() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.pushOffset(0.0, 0.0); // base + builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(800.0, 600.0))); + + builder.pushOpacity(128); + builder.pushClipRect(Rect.fromLTWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0)); + builder.pushClipRRect(RRect.fromLTRBR(10.0, 10.0, 800.0 - 10.0, 600.0 - 10.0, Radius.circular(14.0))); + builder.addPlatformView(42, width: 800.0, height: 600.0); + builder.pop(); // clip rrect + builder.pop(); // clip rect + builder.pop(); // opacity + + builder.pop(); // base + window.render(builder.build()); + }; + window.scheduleFrame(); +} + +@pragma('vm:entry-point') +void platform_view_mutators_with_pixel_ratio() { + window.onBeginFrame = (Duration duration) { + SceneBuilder builder = SceneBuilder(); + builder.pushOffset(0.0, 0.0); // base + builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(400.0, 300.0))); + + builder.pushOpacity(128); + builder.pushClipRect(Rect.fromLTWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + builder.pushClipRRect(RRect.fromLTRBR(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0, Radius.circular(7.0))); + builder.addPlatformView(42, width: 400.0, height: 300.0); + builder.pop(); // clip rrect + builder.pop(); // clip rect + builder.pop(); // opacity + + builder.pop(); // base + window.render(builder.build()); + }; + window.scheduleFrame(); +} diff --git a/shell/platform/embedder/tests/embedder_assertions.h b/shell/platform/embedder/tests/embedder_assertions.h index d0c69015b..a5da239e5 100644 --- a/shell/platform/embedder/tests/embedder_assertions.h +++ b/shell/platform/embedder/tests/embedder_assertions.h @@ -23,11 +23,34 @@ inline bool operator==(const FlutterPoint& a, const FlutterPoint& b) { flutter::testing::NumberNear(a.y, b.y); } +inline bool operator==(const FlutterRect& a, const FlutterRect& b) { + return flutter::testing::NumberNear(a.left, b.left) && + flutter::testing::NumberNear(a.top, b.top) && + flutter::testing::NumberNear(a.right, b.right) && + flutter::testing::NumberNear(a.bottom, b.bottom); +} + inline bool operator==(const FlutterSize& a, const FlutterSize& b) { return flutter::testing::NumberNear(a.width, b.width) && flutter::testing::NumberNear(a.height, b.height); } +inline bool operator==(const FlutterRoundedRect& a, + const FlutterRoundedRect& b) { + return a.rect == b.rect && + a.upper_left_corner_radius == b.upper_left_corner_radius && + a.upper_right_corner_radius == b.upper_right_corner_radius && + a.lower_right_corner_radius == b.lower_right_corner_radius && + a.lower_left_corner_radius == b.lower_left_corner_radius; +} + +inline bool operator==(const FlutterTransformation& a, + const FlutterTransformation& b) { + return a.scaleX == b.scaleX && a.skewX == b.skewX && a.transX == b.transX && + a.skewY == b.skewY && a.scaleY == b.scaleY && a.transY == b.transY && + a.pers0 == b.pers0 && a.pers1 == b.pers1 && a.pers2 == b.pers2; +} + inline bool operator==(const FlutterOpenGLTexture& a, const FlutterOpenGLTexture& b) { return a.target == b.target && a.name == b.name && a.format == b.format && @@ -82,9 +105,40 @@ inline bool operator==(const FlutterBackingStore& a, return false; } +inline bool operator==(const FlutterPlatformViewMutation& a, + const FlutterPlatformViewMutation& b) { + if (a.type != b.type) { + return false; + } + + switch (a.type) { + case kFlutterPlatformViewMutationTypeOpacity: + return flutter::testing::NumberNear(a.opacity, b.opacity); + case kFlutterPlatformViewMutationTypeClipRect: + return a.clip_rect == b.clip_rect; + case kFlutterPlatformViewMutationTypeClipRoundedRect: + return a.clip_rounded_rect == b.clip_rounded_rect; + case kFlutterPlatformViewMutationTypeTransformation: + return a.transformation == b.transformation; + } + + return false; +} + inline bool operator==(const FlutterPlatformView& a, const FlutterPlatformView& b) { - return a.struct_size == b.struct_size && a.identifier == b.identifier; + if (!(a.struct_size == b.struct_size && a.identifier == b.identifier && + a.mutations_count == b.mutations_count)) { + return false; + } + + for (size_t i = 0; i < a.mutations_count; ++i) { + if (!(*a.mutations[i] == *b.mutations[i])) { + return false; + } + } + + return true; } inline bool operator==(const FlutterLayer& a, const FlutterLayer& b) { @@ -111,10 +165,39 @@ inline std::ostream& operator<<(std::ostream& out, const FlutterPoint& point) { return out << "(" << point.x << ", " << point.y << ")"; } +inline std::ostream& operator<<(std::ostream& out, const FlutterRect& r) { + return out << "LTRB (" << r.left << ", " << r.top << ", " << r.right << ", " + << r.bottom << ")"; +} + inline std::ostream& operator<<(std::ostream& out, const FlutterSize& size) { return out << "(" << size.width << ", " << size.height << ")"; } +inline std::ostream& operator<<(std::ostream& out, + const FlutterRoundedRect& r) { + out << "Rect: " << r.rect << ", "; + out << "Upper Left Corner Radius: " << r.upper_left_corner_radius << ", "; + out << "Upper Right Corner Radius: " << r.upper_right_corner_radius << ", "; + out << "Lower Right Corner Radius: " << r.lower_right_corner_radius << ", "; + out << "Lower Left Corner Radius: " << r.lower_left_corner_radius; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const FlutterTransformation& t) { + out << "Scale X: " << t.scaleX << ", "; + out << "Skew X: " << t.skewX << ", "; + out << "Trans X: " << t.transX << ", "; + out << "Skew Y: " << t.skewY << ", "; + out << "Scale Y: " << t.scaleY << ", "; + out << "Trans Y: " << t.transY << ", "; + out << "Pers 0: " << t.pers0 << ", "; + out << "Pers 1: " << t.pers1 << ", "; + out << "Pers 2: " << t.pers2; + return out; +} + inline std::string FlutterLayerContentTypeToString( FlutterLayerContentType type) { switch (type) { @@ -153,11 +236,56 @@ inline std::ostream& operator<<(std::ostream& out, << " Destruction Callback: " << item.destruction_callback; } +inline std::string FlutterPlatformViewMutationTypeToString( + FlutterPlatformViewMutationType type) { + switch (type) { + case kFlutterPlatformViewMutationTypeOpacity: + return "kFlutterPlatformViewMutationTypeOpacity"; + case kFlutterPlatformViewMutationTypeClipRect: + return "kFlutterPlatformViewMutationTypeClipRect"; + case kFlutterPlatformViewMutationTypeClipRoundedRect: + return "kFlutterPlatformViewMutationTypeClipRoundedRect"; + case kFlutterPlatformViewMutationTypeTransformation: + return "kFlutterPlatformViewMutationTypeTransformation"; + } + return "Unknown"; +} + +inline std::ostream& operator<<(std::ostream& out, + const FlutterPlatformViewMutation& m) { + out << "(FlutterPlatformViewMutation) Type: " + << FlutterPlatformViewMutationTypeToString(m.type) << " "; + switch (m.type) { + case kFlutterPlatformViewMutationTypeOpacity: + out << "Opacity: " << m.opacity; + case kFlutterPlatformViewMutationTypeClipRect: + out << "Clip Rect: " << m.clip_rect; + case kFlutterPlatformViewMutationTypeClipRoundedRect: + out << "Clip Rounded Rect: " << m.clip_rounded_rect; + case kFlutterPlatformViewMutationTypeTransformation: + out << "Transformation: " << m.transformation; + } + return out; +} + inline std::ostream& operator<<(std::ostream& out, const FlutterPlatformView& platform_view) { - return out << "(FlutterPlatformView) Struct Size: " - << platform_view.struct_size - << " Identifier: " << platform_view.identifier; + out << "[" + << "(FlutterPlatformView) Struct Size: " << platform_view.struct_size + << " Identifier: " << platform_view.identifier + << " Mutations Count: " << platform_view.mutations_count; + + if (platform_view.mutations_count > 0) { + out << std::endl; + for (size_t i = 0; i < platform_view.mutations_count; i++) { + out << "Mutation " << i << ": " << *platform_view.mutations[i] + << std::endl; + } + } + + out << "]"; + + return out; } inline std::string FlutterOpenGLTargetTypeToString( @@ -248,6 +376,13 @@ inline FlutterSize FlutterSizeMake(double width, double height) { return size; } +inline FlutterSize FlutterSizeMake(const SkVector& vector) { + FlutterSize size = {}; + size.width = vector.x(); + size.height = vector.y(); + return size; +} + inline FlutterTransformation FlutterTransformationMake(const SkMatrix& matrix) { FlutterTransformation transformation = {}; transformation.scaleX = matrix[SkMatrix::kMScaleX]; @@ -266,4 +401,27 @@ inline flutter::EmbedderEngine* ToEmbedderEngine(const FlutterEngine& engine) { return reinterpret_cast(engine); } +inline FlutterRect FlutterRectMake(const SkRect& rect) { + FlutterRect r = {}; + r.left = rect.left(); + r.top = rect.top(); + r.right = rect.right(); + r.bottom = rect.bottom(); + return r; +} + +inline FlutterRoundedRect FlutterRoundedRectMake(const SkRRect& rect) { + FlutterRoundedRect r = {}; + r.rect = FlutterRectMake(rect.rect()); + r.upper_left_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kUpperLeft_Corner)); + r.upper_right_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kUpperRight_Corner)); + r.lower_right_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kLowerRight_Corner)); + r.lower_left_corner_radius = + FlutterSizeMake(rect.radii(SkRRect::Corner::kLowerLeft_Corner)); + return r; +} + #endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_ASSERTIONS_H_ diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 4fe669e66..fddca5e99 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -608,7 +608,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -701,7 +701,7 @@ TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -874,7 +874,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -968,7 +968,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -1189,7 +1189,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1222,7 +1222,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) { // Layer 3 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[3]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 2; @@ -1365,7 +1365,7 @@ TEST_F(EmbedderTest, // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1398,7 +1398,7 @@ TEST_F(EmbedderTest, // Layer 3 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[3]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 2; @@ -1549,7 +1549,7 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) { } { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -1740,7 +1740,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1863,7 +1863,7 @@ TEST_F(EmbedderTest, // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -1896,7 +1896,7 @@ TEST_F(EmbedderTest, // Layer 3 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[3]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 2; @@ -2258,7 +2258,7 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayer) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -2377,7 +2377,7 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1; @@ -2507,7 +2507,7 @@ TEST_F(EmbedderTest, VerifyB141980393) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1337; @@ -2794,7 +2794,7 @@ TEST_F(EmbedderTest, // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -2893,7 +2893,7 @@ TEST_F( // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -3041,7 +3041,7 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { // Layer 1 { - FlutterPlatformView platform_view = {}; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -3203,5 +3203,301 @@ TEST_F(EmbedderTest, ASSERT_EQ(frames_expected, frames_seen); } +TEST_F(EmbedderTest, PlatformViewMutatorsAreValid) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("platform_view_mutators"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + fml::CountDownLatch latch(1); + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 2u); + + // Layer 0 (Root) + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 2 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + platform_view.mutations_count = 3; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[1], layer); + + // There are no ordering guarantees. + for (size_t i = 0; i < platform_view.mutations_count; i++) { + FlutterPlatformViewMutation mutation = *platform_view.mutations[i]; + switch (mutation.type) { + case kFlutterPlatformViewMutationTypeClipRoundedRect: + mutation.clip_rounded_rect = + FlutterRoundedRectMake(SkRRect::MakeRectXY( + SkRect::MakeLTRB(10.0, 10.0, 800.0 - 10.0, + 600.0 - 10.0), + 14.0, 14.0)); + break; + case kFlutterPlatformViewMutationTypeClipRect: + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect = FlutterRectMake( + SkRect::MakeXYWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0)); + break; + case kFlutterPlatformViewMutationTypeOpacity: + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = 128.0 / 255.0; + break; + case kFlutterPlatformViewMutationTypeTransformation: + FML_CHECK(false) + << "There should be no transformation in the test."; + break; + } + + ASSERT_EQ(*platform_view.mutations[i], mutation); + } + } + latch.CountDown(); + }); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + +TEST_F(EmbedderTest, PlatformViewMutatorsAreValidWithPixelRatio) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("platform_view_mutators_with_pixel_ratio"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + fml::CountDownLatch latch(1); + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 2u); + + // Layer 0 (Root) + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 2 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + platform_view.mutations_count = 4; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(800.0, 600.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[1], layer); + + // There are no ordering guarantees. + for (size_t i = 0; i < platform_view.mutations_count; i++) { + FlutterPlatformViewMutation mutation = *platform_view.mutations[i]; + switch (mutation.type) { + case kFlutterPlatformViewMutationTypeClipRoundedRect: + mutation.clip_rounded_rect = + FlutterRoundedRectMake(SkRRect::MakeRectXY( + SkRect::MakeLTRB(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0), + 7.0, 7.0)); + break; + case kFlutterPlatformViewMutationTypeClipRect: + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect = FlutterRectMake( + SkRect::MakeXYWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + break; + case kFlutterPlatformViewMutationTypeOpacity: + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = 128.0 / 255.0; + break; + case kFlutterPlatformViewMutationTypeTransformation: + mutation.type = kFlutterPlatformViewMutationTypeTransformation; + mutation.transformation = + FlutterTransformationMake(SkMatrix::MakeScale(2.0)); + break; + } + + ASSERT_EQ(*platform_view.mutations[i], mutation); + } + } + latch.CountDown(); + }); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 2.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + +TEST_F(EmbedderTest, + PlatformViewMutatorsAreValidWithPixelRatioAndRootSurfaceTransformation) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); + builder.SetCompositor(); + builder.SetDartEntrypoint("platform_view_mutators_with_pixel_ratio"); + + context.GetCompositor().SetRenderTargetType( + EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); + + static const auto root_surface_transformation = + SkMatrix().preTranslate(0, 800).preRotate(-90, 0, 0); + + context.SetRootSurfaceTransformation(root_surface_transformation); + + fml::CountDownLatch latch(1); + context.GetCompositor().SetNextPresentCallback( + [&](const FlutterLayer** layers, size_t layers_count) { + ASSERT_EQ(layers_count, 2u); + + // Layer 0 (Root) + { + FlutterBackingStore backing_store = *layers[0]->backing_store; + backing_store.type = kFlutterBackingStoreTypeOpenGL; + backing_store.did_update = true; + backing_store.open_gl.type = kFlutterOpenGLTargetTypeTexture; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + layer.backing_store = &backing_store; + layer.size = FlutterSizeMake(600.0, 800.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 2 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; + platform_view.struct_size = sizeof(platform_view); + platform_view.identifier = 42; + platform_view.mutations_count = 4; + + FlutterLayer layer = {}; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypePlatformView; + layer.platform_view = &platform_view; + layer.size = FlutterSizeMake(600.0, 800.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[1], layer); + + // There are no ordering guarantees. + for (size_t i = 0; i < platform_view.mutations_count; i++) { + FlutterPlatformViewMutation mutation = *platform_view.mutations[i]; + switch (mutation.type) { + case kFlutterPlatformViewMutationTypeClipRoundedRect: + mutation.clip_rounded_rect = + FlutterRoundedRectMake(SkRRect::MakeRectXY( + SkRect::MakeLTRB(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0), + 7.0, 7.0)); + break; + case kFlutterPlatformViewMutationTypeClipRect: + mutation.type = kFlutterPlatformViewMutationTypeClipRect; + mutation.clip_rect = FlutterRectMake( + SkRect::MakeXYWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + break; + case kFlutterPlatformViewMutationTypeOpacity: + mutation.type = kFlutterPlatformViewMutationTypeOpacity; + mutation.opacity = 128.0 / 255.0; + break; + case kFlutterPlatformViewMutationTypeTransformation: + mutation.type = kFlutterPlatformViewMutationTypeTransformation; + mutation.transformation = + FlutterTransformationMake(SkMatrix::Concat( + root_surface_transformation, SkMatrix::MakeScale(2.0))); + + break; + } + + ASSERT_EQ(*platform_view.mutations[i], mutation); + } + } + latch.CountDown(); + }); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 2.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + latch.Wait(); +} + } // namespace testing } // namespace flutter -- GitLab