Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
FlutterPlatformViewsController.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
8
14#include "flutter/fml/logging.h"
17#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
22
25using flutter::DlRect;
27
28static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
29
30static NSString* const kGestureBlockingPolicyEagerValue = @"eager";
31static NSString* const kGestureBlockingPolicyWaitUntilTouchesEndedValue = @"waitUntilTouchesEnded";
32static NSString* const kGestureBlockingPolicyDoNotBlockGesture = @"doNotBlockGesture";
33static NSString* const kGestureBlockingPolicyFallbackToPluginDefault = @"fallbackToPluginDefault";
34
35struct LayerData {
36 DlRect rect;
37 int64_t view_id;
38 int64_t overlay_id;
39 std::shared_ptr<flutter::OverlayLayer> layer;
40};
41using LayersMap = std::unordered_map<int64_t, LayerData>;
42
43/// Each of the following structs stores part of the platform view hierarchy according to its
44/// ID.
45///
46/// This data must only be accessed on the platform thread.
48 NSObject<FlutterPlatformView>* view;
50 UIView* root_view;
51};
52
53// Converts a DlMatrix to CATransform3D.
54static CATransform3D GetCATransform3DFromDlMatrix(const DlMatrix& matrix) {
55 CATransform3D transform = CATransform3DIdentity;
56 transform.m11 = matrix.m[0];
57 transform.m12 = matrix.m[1];
58 transform.m13 = matrix.m[2];
59 transform.m14 = matrix.m[3];
60
61 transform.m21 = matrix.m[4];
62 transform.m22 = matrix.m[5];
63 transform.m23 = matrix.m[6];
64 transform.m24 = matrix.m[7];
65
66 transform.m31 = matrix.m[8];
67 transform.m32 = matrix.m[9];
68 transform.m33 = matrix.m[10];
69 transform.m34 = matrix.m[11];
70
71 transform.m41 = matrix.m[12];
72 transform.m42 = matrix.m[13];
73 transform.m43 = matrix.m[14];
74 transform.m44 = matrix.m[15];
75 return transform;
76}
77
78// Reset the anchor of `layer` to match the transform operation from flow.
79//
80// The position of the `layer` should be unchanged after resetting the anchor.
81static void ResetAnchor(CALayer* layer) {
82 // Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz.
83 layer.anchorPoint = CGPointZero;
84 layer.position = CGPointZero;
85}
86
87static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
88 return CGRectMake(clipDlRect.GetLeft(), //
89 clipDlRect.GetTop(), //
90 clipDlRect.GetWidth(), //
91 clipDlRect.GetHeight());
92}
93
95 auto iter = params.mutatorsStack().Begin();
96 while (iter != params.mutatorsStack().End()) {
97 switch ((*iter)->GetType()) {
101 return true;
102 default:
103 break;
104 }
105 ++iter;
106 }
107 return false;
108}
109
110// Overlay canvas needs to be clipped to the shape of platform view to ensure
111// underlay shows up correctly, so that when there's backdrop filter, the region outside of platform
112// view's shape is blurred. See: https://github.com/flutter/flutter/issues/150660
116 auto iter = params.mutatorsStack().Begin();
117 while (iter != params.mutatorsStack().End()) {
118 switch ((*iter)->GetType()) {
120 transform = transform * (*iter)->GetMatrix();
121 break;
123 if (transform.IsIdentity()) {
124 overlay_canvas->ClipRoundRect((*iter)->GetRRect(), flutter::DlClipOp::kIntersect, true);
125 } else {
126 auto path = flutter::DlPath::MakeRoundRect((*iter)->GetRRect());
127 auto transformed_path =
128 flutter::DlPath(path.GetSkPath().makeTransform(flutter::ToSkMatrix(transform)));
129 overlay_canvas->ClipPath(transformed_path, flutter::DlClipOp::kIntersect, true);
130 }
131 break;
132 }
134 if (transform.IsIdentity()) {
135 overlay_canvas->ClipRoundSuperellipse((*iter)->GetRSE(), flutter::DlClipOp::kIntersect,
136 true);
137 } else {
138 auto path = flutter::DlPath::MakeRoundSuperellipse((*iter)->GetRSE());
139 auto transformed_path =
140 flutter::DlPath(path.GetSkPath().makeTransform(flutter::ToSkMatrix(transform)));
141 overlay_canvas->ClipPath(transformed_path, flutter::DlClipOp::kIntersect, true);
142 }
143 break;
144 }
146 if (transform.IsIdentity()) {
147 overlay_canvas->ClipPath((*iter)->GetPath(), flutter::DlClipOp::kIntersect, true);
148 } else {
149 auto transformed_path = flutter::DlPath(
150 (*iter)->GetPath().GetSkPath().makeTransform(flutter::ToSkMatrix(transform)));
151 overlay_canvas->ClipPath(transformed_path, flutter::DlClipOp::kIntersect, true);
152 }
153 break;
154 }
155 default:
156 break;
157 }
158 ++iter;
159 }
160}
161
163
164// The pool of reusable view layers. The pool allows to recycle layer in each frame.
165@property(nonatomic, readonly) flutter::OverlayLayerPool* layerPool;
166
167// The platform view's |EmbedderViewSlice| keyed off the view id, which contains any subsequent
168// operation until the next platform view or the end of the last leaf node in the layer tree.
169//
170// The Slices are deleted by the PlatformViewsController.reset().
171@property(nonatomic, readonly)
172 std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>& slices;
173
174@property(nonatomic, readonly) FlutterClippingMaskViewPool* maskViewPool;
175
176@property(nonatomic, readonly)
177 std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>& factories;
178
179// The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view.
180@property(nonatomic, readonly)
181 std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&
182 gestureRecognizersBlockingPoliciesByType;
183
184/// The size of the current onscreen surface in physical pixels.
185@property(nonatomic, assign) DlISize frameSize;
186
187/// The task runner for posting tasks to the platform thread.
188@property(nonatomic, readonly) FlutterFMLTaskRunner* platformTaskRunner;
189
190/// This data must only be accessed on the platform thread.
191@property(nonatomic, readonly) std::unordered_map<int64_t, PlatformViewData>& platformViews;
192
193/// The composition parameters for each platform view.
194///
195/// This state is only modified on the raster thread.
196@property(nonatomic, readonly)
197 std::unordered_map<int64_t, flutter::EmbeddedViewParams>& currentCompositionParams;
198
199/// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on
200/// the next frame.
201///
202/// This state is modified on both the platform and raster thread.
203@property(nonatomic, readonly) std::unordered_set<int64_t>& viewsToDispose;
204
205/// view IDs in composition order.
206///
207/// This state is only modified on the raster thread.
208@property(nonatomic, readonly) std::vector<int64_t>& compositionOrder;
209
210/// platform view IDs visited during layer tree composition.
211///
212/// This state is only modified on the raster thread.
213@property(nonatomic, readonly) std::vector<int64_t>& visitedPlatformViews;
214
215/// Only composite platform views in this set.
216///
217/// This state is only modified on the raster thread.
218@property(nonatomic, readonly) std::unordered_set<int64_t>& viewsToRecomposite;
219
220/// Whether the previous frame had any platform views in active composition order.
221///
222/// This state is tracked so that the first frame after removing the last platform view
223/// runs through the platform view rendering code path, giving us a chance to remove the
224/// platform view from the UIView hierarchy.
225///
226/// Only accessed from the raster thread.
227@property(nonatomic, assign) BOOL hadPlatformViews;
228
229/// Whether blurred backdrop filters can be applied.
230///
231/// Defaults to YES, but becomes NO if blurred backdrop filters cannot be applied.
232@property(nonatomic, assign) BOOL canApplyBlurBackdrop;
233
234/// Populate any missing overlay layers.
235///
236/// This requires posting a task to the platform thread and blocking on its completion.
237- (void)createMissingOverlays:(size_t)requiredOverlayLayers
238 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext;
239
240/// Update the buffers and mutate the platform views in CATransaction on the platform thread.
241- (void)performSubmit:(const LayersMap&)platformViewLayers
242 currentCompositionParams:
243 (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
244 viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
245 compositionOrder:(const std::vector<int64_t>&)compositionOrder
246 unusedLayers:
247 (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
248 surfaceFrames:
249 (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames;
250
251- (void)onCreate:(FlutterMethodCall*)call result:(FlutterResult)result;
252- (void)onDispose:(FlutterMethodCall*)call result:(FlutterResult)result;
253- (void)onAcceptGesture:(FlutterMethodCall*)call result:(FlutterResult)result;
254- (void)onRejectGesture:(FlutterMethodCall*)call result:(FlutterResult)result;
255
256- (void)clipViewSetMaskView:(UIView*)clipView;
257
258// Applies the mutators in the mutatorsStack to the UIView chain that was constructed by
259// `ReconstructClipViewsChain`
260//
261// Clips are applied to the `embeddedView`'s super view(|ChildClippingView|) using a
262// |FlutterClippingMaskView|. Transforms are applied to `embeddedView`
263//
264// The `boundingRect` is the final bounding rect of the PlatformView
265// (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding
266// rect of the PlatformView, the clip mutator is not applied for performance optimization.
267//
268// This method is only called when the `embeddedView` needs to be re-composited at the current
269// frame. See: `compositeView:withParams:` for details.
270- (void)applyMutators:(const flutter::MutatorsStack&)mutatorsStack
271 embeddedView:(UIView*)embeddedView
272 boundingRect:(const DlRect&)boundingRect;
273
274// Appends the overlay views and platform view and sets their z index based on the composition
275// order.
276- (void)bringLayersIntoView:(const LayersMap&)layerMap
277 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder;
278
279- (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool;
280
281/// Runs on the platform thread.
282- (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
283 pixelFormat:(MTLPixelFormat)pixelFormat;
284
285/// Removes overlay views and platform views that aren't needed in the current frame.
286/// Must run on the platform thread.
287- (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
288 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder;
289
290/// Computes and returns all views to be disposed on the platform thread, removes them from
291/// self.platformViews, self.viewsToRecomposite, and self.currentCompositionParams. Any views that
292/// still require compositing are not returned, but instead added to `viewsToDelayDispose` for
293/// disposal on the next call.
294- (std::vector<UIView*>)computeViewsToDispose;
295
296/// Resets the state of the frame.
297- (void)resetFrameState;
298@end
299
300@implementation FlutterPlatformViewsController {
301 // TODO(cbracken): Replace with Obj-C types and use @property declarations to automatically
302 // synthesize the ivars.
303 //
304 // These ivars are required because we're transitioning the previous C++ implementation to Obj-C.
305 // We require ivars to declare the concrete types and then wrap with @property declarations that
306 // return a reference to the ivar, allowing for use like `self.layerPool` and
307 // `self.slices[viewId] = x`.
308 std::unique_ptr<flutter::OverlayLayerPool> _layerPool;
309 std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>> _slices;
310 std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*> _factories;
311 std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>
314 std::unordered_map<int64_t, PlatformViewData> _platformViews;
315 std::unordered_map<int64_t, flutter::EmbeddedViewParams> _currentCompositionParams;
316 std::unordered_set<int64_t> _viewsToDispose;
317 std::vector<int64_t> _compositionOrder;
318 std::vector<int64_t> _visitedPlatformViews;
319 std::unordered_set<int64_t> _viewsToRecomposite;
320 std::vector<int64_t> _previousCompositionOrder;
321}
322
323- (id)init {
324 if (self = [super init]) {
325 _layerPool = std::make_unique<flutter::OverlayLayerPool>();
326 _maskViewPool =
327 [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity];
328 _hadPlatformViews = NO;
329 _canApplyBlurBackdrop = YES;
330 }
331 return self;
332}
333
334- (FlutterFMLTaskRunner*)taskRunner {
335 return _platformTaskRunner;
336}
337
338- (void)setTaskRunner:(FlutterFMLTaskRunner*)platformTaskRunner {
339 _platformTaskRunner = platformTaskRunner;
340}
341
342- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
343 if ([[call method] isEqualToString:@"create"]) {
344 [self onCreate:call result:result];
345 } else if ([[call method] isEqualToString:@"dispose"]) {
346 [self onDispose:call result:result];
347 } else if ([[call method] isEqualToString:@"acceptGesture"]) {
348 [self onAcceptGesture:call result:result];
349 } else if ([[call method] isEqualToString:@"rejectGesture"]) {
350 [self onRejectGesture:call result:result];
351 } else {
353 }
354}
355
356- (void)onCreate:(FlutterMethodCall*)call result:(FlutterResult)result {
357 NSDictionary<NSString*, id>* args = [call arguments];
358
359 int64_t viewId = [args[@"id"] longLongValue];
360 NSString* viewTypeString = args[@"viewType"];
361 std::string viewType(viewTypeString.UTF8String);
362
363 if (self.platformViews.count(viewId) != 0) {
364 result([FlutterError errorWithCode:@"recreating_view"
365 message:@"trying to create an already created view"
366 details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
367 return;
368 }
369
370 NSObject<FlutterPlatformViewFactory>* factory = self.factories[viewType];
371 if (factory == nil) {
372 result([FlutterError
373 errorWithCode:@"unregistered_view_type"
374 message:[NSString stringWithFormat:@"A UIKitView widget is trying to create a "
375 @"PlatformView with an unregistered type: < %@ >",
376 viewTypeString]
377 details:@"If you are the author of the PlatformView, make sure `registerViewFactory` "
378 @"is invoked.\n"
379 @"See: "
380 @"https://docs.flutter.dev/development/platform-integration/"
381 @"platform-views#on-the-platform-side-1 for more details.\n"
382 @"If you are not the author of the PlatformView, make sure to call "
383 @"`GeneratedPluginRegistrant.register`."]);
384 return;
385 }
386
387 id params = nil;
388 if ([factory respondsToSelector:@selector(createArgsCodec)]) {
389 NSObject<FlutterMessageCodec>* codec = [factory createArgsCodec];
390 if (codec != nil && args[@"params"] != nil) {
391 FlutterStandardTypedData* paramsData = args[@"params"];
392 params = [codec decode:paramsData.data];
393 }
394 }
395
396 NSObject<FlutterPlatformView>* embeddedView = [factory createWithFrame:CGRectZero
397 viewIdentifier:viewId
398 arguments:params];
399 UIView* platformView = [embeddedView view];
400 // Set a unique view identifier, so the platform view can be identified in unit tests.
401 platformView.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld]", viewId];
402
403 NSString* gestureBlockingPolicyValue = args[@"gestureBlockingPolicy"];
405 if ([gestureBlockingPolicyValue isEqualToString:kGestureBlockingPolicyDoNotBlockGesture]) {
407 } else if ([gestureBlockingPolicyValue isEqualToString:kGestureBlockingPolicyEagerValue]) {
409 } else if ([gestureBlockingPolicyValue
411 gestureBlockingPolicy =
413 } else if ([gestureBlockingPolicyValue
415 gestureBlockingPolicy = self.gestureRecognizersBlockingPoliciesByType[viewType];
416 } else {
417 result([FlutterError
418 errorWithCode:@"unknown_gesture_blocking_policy"
419 message:@"Trying to create a platform view with an unknown gesture blocking policy"
420 details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
421 return;
422 }
423
424 FlutterTouchInterceptingView* touchInterceptor =
425 [[FlutterTouchInterceptingView alloc] initWithEmbeddedView:platformView
426 platformViewsController:self
427 gestureRecognizersBlockingPolicy:gestureBlockingPolicy];
428
429 ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero];
430 [clippingView addSubview:touchInterceptor];
431
432 self.platformViews.emplace(viewId, PlatformViewData{
433 .view = embeddedView, //
434 .touch_interceptor = touchInterceptor, //
435 .root_view = clippingView //
436 });
437
438 result(nil);
439}
440
441- (void)onDispose:(FlutterMethodCall*)call result:(FlutterResult)result {
442 NSNumber* arg = [call arguments];
443 int64_t viewId = [arg longLongValue];
444
445 if (self.platformViews.count(viewId) == 0) {
446 result([FlutterError errorWithCode:@"unknown_view"
447 message:@"trying to dispose an unknown"
448 details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
449 return;
450 }
451 // We wait for next submitFrame to dispose views.
452 self.viewsToDispose.insert(viewId);
453 result(nil);
454}
455
456- (void)onAcceptGesture:(FlutterMethodCall*)call result:(FlutterResult)result {
457 NSDictionary<NSString*, id>* args = [call arguments];
458 int64_t viewId = [args[@"id"] longLongValue];
459
460 if (self.platformViews.count(viewId) == 0) {
461 result([FlutterError errorWithCode:@"unknown_view"
462 message:@"trying to set gesture state for an unknown view"
463 details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
464 return;
465 }
466
467 FlutterTouchInterceptingView* view = self.platformViews[viewId].touch_interceptor;
468 [view releaseGesture];
469
470 result(nil);
471}
472
473- (void)onRejectGesture:(FlutterMethodCall*)call result:(FlutterResult)result {
474 NSDictionary<NSString*, id>* args = [call arguments];
475 int64_t viewId = [args[@"id"] longLongValue];
476
477 if (self.platformViews.count(viewId) == 0) {
478 result([FlutterError errorWithCode:@"unknown_view"
479 message:@"trying to set gesture state for an unknown view"
480 details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
481 return;
482 }
483
484 FlutterTouchInterceptingView* view = self.platformViews[viewId].touch_interceptor;
485 [view blockGesture];
486
487 result(nil);
488}
489
490- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
491 withId:(NSString*)factoryId
492 gestureRecognizersBlockingPolicy:
493 (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizerBlockingPolicy {
494 std::string idString([factoryId UTF8String]);
495 FML_CHECK(self.factories.count(idString) == 0);
496 self.factories[idString] = factory;
497 self.gestureRecognizersBlockingPoliciesByType[idString] = gestureRecognizerBlockingPolicy;
498}
499
500- (void)beginFrameWithSize:(DlISize)frameSize {
501 [self resetFrameState];
502 self.frameSize = frameSize;
503}
504
505- (void)cancelFrame {
506 [self resetFrameState];
507}
508
509- (flutter::PostPrerollResult)postPrerollActionWithThreadMerger:
510 (const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger {
512}
513
514- (void)endFrameWithResubmit:(BOOL)shouldResubmitFrame
515 threadMerger:(const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger {
516}
517
518- (void)pushFilterToVisitedPlatformViews:(const std::shared_ptr<flutter::DlImageFilter>&)filter
519 withRect:(const flutter::DlRect&)filterRect {
520 for (int64_t id : self.visitedPlatformViews) {
521 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
522 params.PushImageFilter(filter, filterRect);
523 self.currentCompositionParams[id] = params;
524 }
525}
526
527- (void)prerollCompositeEmbeddedView:(int64_t)viewId
528 withParams:(std::unique_ptr<flutter::EmbeddedViewParams>)params {
529 DlRect viewBounds = DlRect::MakeSize(self.frameSize);
530 std::unique_ptr<flutter::EmbedderViewSlice> view;
531 view = std::make_unique<flutter::DisplayListEmbedderViewSlice>(viewBounds);
532 self.slices.insert_or_assign(viewId, std::move(view));
533
534 self.compositionOrder.push_back(viewId);
535
536 if (self.currentCompositionParams.count(viewId) == 1 &&
537 self.currentCompositionParams[viewId] == *params.get()) {
538 // Do nothing if the params didn't change.
539 return;
540 }
541 self.currentCompositionParams[viewId] = flutter::EmbeddedViewParams(*params.get());
542 self.viewsToRecomposite.insert(viewId);
543}
544
545- (size_t)embeddedViewCount {
546 return self.compositionOrder.size();
547}
548
549- (UIView*)platformViewForId:(int64_t)viewId {
550 return [self flutterTouchInterceptingViewForId:viewId].embeddedView;
551}
552
553- (FlutterTouchInterceptingView*)flutterTouchInterceptingViewForId:(int64_t)viewId {
554 if (self.platformViews.empty()) {
555 return nil;
556 }
557 return self.platformViews[viewId].touch_interceptor;
558}
559
560- (long)firstResponderPlatformViewId {
561 for (auto const& [id, platformViewData] : self.platformViews) {
562 UIView* rootView = platformViewData.root_view;
563 if (rootView.flt_hasFirstResponderInViewHierarchySubtree) {
564 return id;
565 }
566 }
567 return -1;
568}
569
570- (void)clipViewSetMaskView:(UIView*)clipView {
571 FML_DCHECK([[NSThread currentThread] isMainThread]);
572 if (clipView.maskView) {
573 return;
574 }
575 CGRect frame =
576 CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y,
577 CGRectGetWidth(self.flutterView.bounds), CGRectGetHeight(self.flutterView.bounds));
578 clipView.maskView = [self.maskViewPool getMaskViewWithFrame:frame];
579}
580
581- (void)applyMutators:(const flutter::MutatorsStack&)mutatorsStack
582 embeddedView:(UIView*)embeddedView
583 boundingRect:(const DlRect&)boundingRect {
584 if (self.flutterView == nil) {
585 return;
586 }
587
588 ResetAnchor(embeddedView.layer);
589 ChildClippingView* clipView = (ChildClippingView*)embeddedView.superview;
590
591 DlMatrix transformMatrix;
592 NSMutableArray* blurFilters = [[NSMutableArray alloc] init];
593 NSMutableArray<PendingRRectClip*>* pendingClipRRects = [[NSMutableArray alloc] init];
594
595 FML_DCHECK(!clipView.maskView ||
596 [clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]);
597 if (clipView.maskView) {
598 [self.maskViewPool insertViewToPoolIfNeeded:(FlutterClippingMaskView*)(clipView.maskView)];
599 clipView.maskView = nil;
600 }
601 CGFloat screenScale = [UIScreen mainScreen].scale;
602 auto iter = mutatorsStack.Begin();
603 while (iter != mutatorsStack.End()) {
604 switch ((*iter)->GetType()) {
606 transformMatrix = transformMatrix * (*iter)->GetMatrix();
607 break;
608 }
611 (*iter)->GetRect(), transformMatrix, boundingRect)) {
612 break;
613 }
614 [self clipViewSetMaskView:clipView];
615 [(FlutterClippingMaskView*)clipView.maskView clipRect:(*iter)->GetRect()
616 matrix:transformMatrix];
617 break;
618 }
621 (*iter)->GetRRect(), transformMatrix, boundingRect)) {
622 break;
623 }
624 [self clipViewSetMaskView:clipView];
625 [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRRect()
626 matrix:transformMatrix];
627 break;
628 }
631 (*iter)->GetRSE(), transformMatrix, boundingRect)) {
632 break;
633 }
634 [self clipViewSetMaskView:clipView];
635 [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRSEApproximation()
636 matrix:transformMatrix];
637 break;
638 }
640 // TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning
641 // rect. See `ClipRRectContainsPlatformViewBoundingRect`.
642 // https://github.com/flutter/flutter/issues/118650
643 [self clipViewSetMaskView:clipView];
644 [(FlutterClippingMaskView*)clipView.maskView clipPath:(*iter)->GetPath()
645 matrix:transformMatrix];
646 break;
647 }
649 embeddedView.alpha = (*iter)->GetAlphaFloat() * embeddedView.alpha;
650 break;
652 // Only support DlBlurImageFilter for BackdropFilter.
653 if (!self.canApplyBlurBackdrop || !(*iter)->GetFilterMutation().GetFilter().asBlur()) {
654 break;
655 }
656 CGRect filterRect = GetCGRectFromDlRect((*iter)->GetFilterMutation().GetFilterRect());
657 // `filterRect` is in global coordinates. We need to convert to local space.
658 filterRect = CGRectApplyAffineTransform(
659 filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale));
660 // `filterRect` reprents the rect that should be filtered inside the `_flutterView`.
661 // The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be
662 // filtered.
663 if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
664 break;
665 }
666 CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
667 CGRect frameInClipView = [self.flutterView convertRect:intersection toView:clipView];
668 // sigma_x is arbitrarily chosen as the radius value because Quartz sets
669 // sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
670 // is not supported in Quartz's gaussianBlur CAFilter, so it is not used
671 // to blur the PlatformView.
672 CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
673 UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
674 initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
675
676 // TODO(https://github.com/flutter/flutter/issues/179126)
677 CGFloat cornerRadius = 0.0;
678 BOOL isRoundedSuperellipse = NO;
679 // If there's multiple clips, this uses the innermost to decide if its
680 // rse or not. The assumption being the innermost will be the tightest
681 if ([pendingClipRRects count] > 0) {
682 cornerRadius = pendingClipRRects.lastObject.topLeftRadius;
683 isRoundedSuperellipse = pendingClipRRects.lastObject.isRoundedSuperellipse;
684 [pendingClipRRects removeAllObjects];
685 }
686 visualEffectView.layer.cornerRadius = cornerRadius;
687 if (@available(iOS 13.0, *)) {
688 visualEffectView.layer.cornerCurve =
689 isRoundedSuperellipse ? kCACornerCurveContinuous : kCACornerCurveCircular;
690 }
691 visualEffectView.clipsToBounds = YES;
692
693 PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
694 blurRadius:blurRadius
695 cornerRadius:cornerRadius
696 isRoundedSuperellipse:isRoundedSuperellipse
697 visualEffectView:visualEffectView];
698 if (!filter) {
699 self.canApplyBlurBackdrop = NO;
700 } else {
701 [blurFilters addObject:filter];
702 }
703 break;
704 }
706 // The frame already handles cropping into the rect so this can
707 // no-op
708 break;
709 }
711 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
712 DlRoundRect rrect = (*iter)->GetBackdropClipRRect().rrect;
713
714 clip.rect = boundingRect;
715 impeller::RoundingRadii radii = rrect.GetRadii();
716 clip.topLeftRadius = radii.top_left.width;
717 clip.topRightRadius = radii.top_right.width;
720 [pendingClipRRects addObject:clip];
721 break;
722 }
724 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
725 flutter::DlRoundSuperellipse rse = (*iter)->GetBackdropClipRSuperellipse().rse;
726
727 clip.rect = boundingRect;
728 impeller::RoundingRadii radii = rse.GetRadii();
729 clip.topLeftRadius = radii.top_left.width;
730 clip.topRightRadius = radii.top_right.width;
733 clip.isRoundedSuperellipse = YES;
734 [pendingClipRRects addObject:clip];
735 break;
736 }
738 // TODO(https://github.com/flutter/flutter/issues/179127)
739 break;
740 }
741 }
742 ++iter;
743 }
744
745 if (self.canApplyBlurBackdrop) {
746 [clipView applyBlurBackdropFilters:blurFilters];
747 }
748
749 // The UIKit frame is set based on the logical resolution (points) instead of physical.
750 // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
751 // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
752 // 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
753 // down to the logical resoltion before applying it to the layer of PlatformView.
754 flutter::DlScalar pointScale = 1.0 / screenScale;
755 transformMatrix = DlMatrix::MakeScale({pointScale, pointScale, 1}) * transformMatrix;
756
757 // Reverse the offset of the clipView.
758 // The clipView's frame includes the final translate of the final transform matrix.
759 // Thus, this translate needs to be reversed so the platform view can layout at the correct
760 // offset.
761 //
762 // Note that the transforms are not applied to the clipping paths because clipping paths happen on
763 // the mask view, whose origin is always (0,0) to the _flutterView.
764 impeller::Vector3 origin = impeller::Vector3(clipView.frame.origin.x, clipView.frame.origin.y);
765 transformMatrix = DlMatrix::MakeTranslation(-origin) * transformMatrix;
766
767 embeddedView.layer.transform = GetCATransform3DFromDlMatrix(transformMatrix);
768}
769
770- (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params {
771 // TODO(https://github.com/flutter/flutter/issues/109700)
772 CGRect frame = CGRectMake(0, 0, params.sizePoints().width, params.sizePoints().height);
773 FlutterTouchInterceptingView* touchInterceptor = self.platformViews[viewId].touch_interceptor;
774 touchInterceptor.layer.transform = CATransform3DIdentity;
775 touchInterceptor.frame = frame;
776 touchInterceptor.alpha = 1;
777
778 const flutter::MutatorsStack& mutatorStack = params.mutatorsStack();
779 UIView* clippingView = self.platformViews[viewId].root_view;
780 // The frame of the clipping view should be the final bounding rect.
781 // Because the translate matrix in the Mutator Stack also includes the offset,
782 // when we apply the transforms matrix in |applyMutators:embeddedView:boundingRect|, we need
783 // to remember to do a reverse translate.
784 const DlRect& rect = params.finalBoundingRect();
785 CGFloat screenScale = [UIScreen mainScreen].scale;
786 clippingView.frame = CGRectMake(rect.GetX() / screenScale, rect.GetY() / screenScale,
787 rect.GetWidth() / screenScale, rect.GetHeight() / screenScale);
788 [self applyMutators:mutatorStack embeddedView:touchInterceptor boundingRect:rect];
789}
790
791- (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId {
792 FML_DCHECK(self.slices.find(viewId) != self.slices.end());
793 return self.slices[viewId]->canvas();
794}
795
796- (void)reset {
797 // Reset will only be called from the raster thread or a merged raster/platform thread.
798 // _platformViews must only be modified on the platform thread, and any operations that
799 // read or modify platform views should occur there.
800 std::vector<int64_t> compositionOrder = self.compositionOrder;
801 [self.taskRunner runNowOrPostTask:^{
802 for (int64_t viewId : compositionOrder) {
803 [self.platformViews[viewId].root_view removeFromSuperview];
804 }
805 self.platformViews.clear();
806 _previousCompositionOrder.clear();
807 }];
808
809 self.compositionOrder.clear();
810 self.slices.clear();
811 self.currentCompositionParams.clear();
812 self.viewsToRecomposite.clear();
813 self.layerPool->RecycleLayers();
814 self.visitedPlatformViews.clear();
815}
816
817- (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)background_frame
818 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
819 TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
820
821 // No platform views to render.
822 if (self.flutterView == nil || (self.compositionOrder.empty() && !self.hadPlatformViews)) {
823 // No platform views to render but the FlutterView may need to be resized.
824 __weak FlutterPlatformViewsController* weakSelf = self;
825 if (self.flutterView != nil) {
826 // Pass frameSize by value since self.frameSize is mutated both here (on the platform
827 // thread) and in beginFrameWithSize: (on the raster thread).
828 const flutter::DlISize frameSize = self.frameSize;
829 [self.taskRunner runNowOrPostTask:^{
830 FlutterPlatformViewsController* strongSelf = weakSelf;
831 if (!strongSelf) {
832 return;
833 }
834 [strongSelf performResize:frameSize];
835 }];
836 }
837
838 self.hadPlatformViews = NO;
839 return background_frame->Submit();
840 }
841 self.hadPlatformViews = !self.compositionOrder.empty();
842
843 bool didEncode = true;
844 LayersMap platformViewLayers;
845 std::vector<std::unique_ptr<flutter::SurfaceFrame>> surfaceFrames;
846 surfaceFrames.reserve(self.compositionOrder.size());
847 std::unordered_map<int64_t, DlRect> viewRects;
848 std::unordered_set<int64_t> viewsWithUnderlayPreserved;
849
850 for (int64_t viewId : self.compositionOrder) {
851 const flutter::EmbeddedViewParams& params = self.currentCompositionParams[viewId];
852 viewRects[viewId] = params.finalBoundingRect();
854 viewsWithUnderlayPreserved.insert(viewId);
855 }
856 }
857
858 std::unordered_map<int64_t, DlRect> overlayLayers =
859 SliceViews(background_frame->Canvas(), self.compositionOrder, self.slices, viewRects,
860 viewsWithUnderlayPreserved);
861
862 size_t requiredOverlayLayers = 0;
863 for (int64_t viewId : self.compositionOrder) {
864 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
865 if (overlay == overlayLayers.end()) {
866 continue;
867 }
868 requiredOverlayLayers++;
869 }
870
871 // If there are not sufficient overlay layers, we must construct them on the platform
872 // thread, at least until we've refactored iOS surface creation to use IOSurfaces
873 // instead of CALayers.
874 [self createMissingOverlays:requiredOverlayLayers withIosContext:iosContext];
875
876 int64_t overlayId = 0;
877 for (int64_t viewId : self.compositionOrder) {
878 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
879 if (overlay == overlayLayers.end()) {
880 continue;
881 }
882 std::shared_ptr<flutter::OverlayLayer> layer = self.nextLayerInPool;
883 if (!layer) {
884 continue;
885 }
886
887 std::unique_ptr<flutter::SurfaceFrame> frame = layer->surface->AcquireFrame(self.frameSize);
888 // If frame is null, AcquireFrame already printed out an error message.
889 if (!frame) {
890 continue;
891 }
892 flutter::DlCanvas* overlayCanvas = frame->Canvas();
893 int restoreCount = overlayCanvas->GetSaveCount();
894 overlayCanvas->Save();
895 overlayCanvas->ClipRect(overlay->second);
896 if (viewsWithUnderlayPreserved.find(viewId) != viewsWithUnderlayPreserved.end()) {
897 ApplyNonRectClipToOverlayCanvas(overlayCanvas, self.currentCompositionParams[viewId]);
898 }
899 overlayCanvas->Clear(flutter::DlColor::kTransparent());
900 self.slices[viewId]->render_into(overlayCanvas);
901 overlayCanvas->RestoreToCount(restoreCount);
902
903 // This flutter view is never the last in a frame, since we always submit the
904 // underlay view last.
905 frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
906 layer->did_submit_last_frame = frame->Encode();
907
908 didEncode &= layer->did_submit_last_frame;
909 platformViewLayers[viewId] = LayerData{
910 .rect = overlay->second, //
911 .view_id = viewId, //
912 .overlay_id = overlayId, //
913 .layer = layer //
914 };
915 surfaceFrames.push_back(std::move(frame));
916 overlayId++;
917 }
918
919 auto previousSubmitInfo = background_frame->submit_info();
920 background_frame->set_submit_info({
921 .frame_damage = previousSubmitInfo.frame_damage,
922 .buffer_damage = previousSubmitInfo.buffer_damage,
923 .present_with_transaction = true,
924 });
925 background_frame->Encode();
926 surfaceFrames.push_back(std::move(background_frame));
927
928 // Mark all layers as available, so they can be used in the next frame.
929 std::vector<std::shared_ptr<flutter::OverlayLayer>> unusedLayers =
930 self.layerPool->RemoveUnusedLayers();
931 self.layerPool->RecycleLayers();
932 auto task = fml::MakeCopyable([self, //
933 platformViewLayers = std::move(platformViewLayers), //
934 currentCompositionParams = self.currentCompositionParams, //
935 viewsToRecomposite = self.viewsToRecomposite, //
936 compositionOrder = self.compositionOrder, //
937 unusedLayers = std::move(unusedLayers), //
938 surfaceFrames = std::move(surfaceFrames)]() mutable {
939 [self performSubmit:platformViewLayers
940 currentCompositionParams:currentCompositionParams
941 viewsToRecomposite:viewsToRecomposite
942 compositionOrder:compositionOrder
943 unusedLayers:unusedLayers
944 surfaceFrames:surfaceFrames];
945 });
946
947 [self.taskRunner runNowOrPostTask:^{
948 task();
949 }];
950 return didEncode;
951}
952
953- (void)createMissingOverlays:(size_t)requiredOverlayLayers
954 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
955 TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
956
957 if (requiredOverlayLayers <= self.layerPool->size()) {
958 return;
959 }
960 auto missingLayerCount = requiredOverlayLayers - self.layerPool->size();
961
962 // If the raster thread isn't merged, create layers on the platform thread and block until
963 // complete. The self-capture here is fine since this is effectively synchronous (we block on the
964 // latch right below).
965 auto latch = std::make_shared<fml::CountDownLatch>(1u);
966 [self.taskRunner runNowOrPostTask:^{
967 for (auto i = 0u; i < missingLayerCount; i++) {
968 [self createLayerWithIosContext:iosContext
969 pixelFormat:((FlutterView*)self.flutterView).pixelFormat];
970 }
971 latch->CountDown();
972 }];
973 if (![[NSThread currentThread] isMainThread]) {
974 latch->Wait();
975 }
976}
977
978- (void)performResize:(const flutter::DlISize&)frameSize {
979 TRACE_EVENT0("flutter", "PlatformViewsController::PerformResize");
980 FML_DCHECK([[NSThread currentThread] isMainThread]);
981
982 if (self.flutterView != nil) {
983 [(FlutterView*)self.flutterView
984 setIntrinsicContentSize:CGSizeMake(frameSize.width, frameSize.height)];
985 }
986}
987
988- (void)performSubmit:(const LayersMap&)platformViewLayers
989 currentCompositionParams:
990 (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
991 viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
992 compositionOrder:(const std::vector<int64_t>&)compositionOrder
993 unusedLayers:
994 (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
995 surfaceFrames:
996 (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames {
997 TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
998 FML_DCHECK([[NSThread currentThread] isMainThread]);
999
1000 [CATransaction begin];
1001
1002 // Configure Flutter overlay views.
1003 for (const auto& [viewId, layerData] : platformViewLayers) {
1004 layerData.layer->UpdateViewState(self.flutterView, //
1005 layerData.rect, //
1006 layerData.view_id, //
1007 layerData.overlay_id //
1008 );
1009 }
1010
1011 // Dispose unused Flutter Views.
1012 for (auto& view : [self computeViewsToDispose]) {
1013 [view removeFromSuperview];
1014 }
1015
1016 // Composite Platform Views.
1017 for (int64_t viewId : viewsToRecomposite) {
1018 [self compositeView:viewId withParams:currentCompositionParams[viewId]];
1019 }
1020
1021 // Present callbacks.
1022 for (const auto& frame : surfaceFrames) {
1023 frame->Submit();
1024 }
1025
1026 // If a layer was allocated in the previous frame, but it's not used in the current frame,
1027 // then it can be removed from the scene.
1028 [self removeUnusedLayers:unusedLayers withCompositionOrder:compositionOrder];
1029
1030 // Organize the layers by their z indexes.
1031 [self bringLayersIntoView:platformViewLayers withCompositionOrder:compositionOrder];
1032
1033 [CATransaction commit];
1034}
1035
1036- (void)bringLayersIntoView:(const LayersMap&)layerMap
1037 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
1038 FML_DCHECK(self.flutterView);
1039 UIView* flutterView = self.flutterView;
1040
1042 NSMutableArray* desiredPlatformSubviews = [NSMutableArray array];
1043 for (int64_t platformViewId : compositionOrder) {
1044 _previousCompositionOrder.push_back(platformViewId);
1045 UIView* platformViewRoot = self.platformViews[platformViewId].root_view;
1046 if (platformViewRoot != nil) {
1047 [desiredPlatformSubviews addObject:platformViewRoot];
1048 }
1049
1050 auto maybeLayerData = layerMap.find(platformViewId);
1051 if (maybeLayerData != layerMap.end()) {
1052 auto view = maybeLayerData->second.layer->overlay_view_wrapper;
1053 if (view != nil) {
1054 [desiredPlatformSubviews addObject:view];
1055 }
1056 }
1057 }
1058
1059 NSSet* desiredPlatformSubviewsSet = [NSSet setWithArray:desiredPlatformSubviews];
1060 NSArray* existingPlatformSubviews = [flutterView.subviews
1061 filteredArrayUsingPredicate:[NSPredicate
1062 predicateWithBlock:^BOOL(id object, NSDictionary* bindings) {
1063 return [desiredPlatformSubviewsSet containsObject:object];
1064 }]];
1065
1066 // Manipulate view hierarchy only if needed, to address a performance issue where
1067 // this method is called even when view hierarchy stays the same.
1068 // See: https://github.com/flutter/flutter/issues/121833
1069 // TODO(hellohuanlin): investigate if it is possible to skip unnecessary bringLayersIntoView.
1070 if (![desiredPlatformSubviews isEqualToArray:existingPlatformSubviews]) {
1071 for (UIView* subview in desiredPlatformSubviews) {
1072 // `addSubview` will automatically reorder subview if it is already added.
1073 [flutterView addSubview:subview];
1074 }
1075 }
1076}
1077
1078- (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool {
1079 return self.layerPool->GetNextLayer();
1080}
1081
1082- (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
1083 pixelFormat:(MTLPixelFormat)pixelFormat {
1084 self.layerPool->CreateLayer(iosContext, pixelFormat);
1085}
1086
1087- (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
1088 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
1089 for (const std::shared_ptr<flutter::OverlayLayer>& layer : unusedLayers) {
1090 [layer->overlay_view_wrapper removeFromSuperview];
1091 }
1092
1093 std::unordered_set<int64_t> compositionOrderSet;
1094 for (int64_t viewId : compositionOrder) {
1095 compositionOrderSet.insert(viewId);
1096 }
1097 // Remove unused platform views.
1098 for (int64_t viewId : _previousCompositionOrder) {
1099 if (compositionOrderSet.find(viewId) == compositionOrderSet.end()) {
1100 UIView* platformViewRoot = self.platformViews[viewId].root_view;
1101 [platformViewRoot removeFromSuperview];
1102 }
1103 }
1104}
1105
1106- (std::vector<UIView*>)computeViewsToDispose {
1107 std::vector<UIView*> views;
1108 if (self.viewsToDispose.empty()) {
1109 return views;
1110 }
1111
1112 std::unordered_set<int64_t> viewsToComposite(self.compositionOrder.begin(),
1113 self.compositionOrder.end());
1114 std::unordered_set<int64_t> viewsToDelayDispose;
1115 for (int64_t viewId : self.viewsToDispose) {
1116 if (viewsToComposite.count(viewId)) {
1117 viewsToDelayDispose.insert(viewId);
1118 continue;
1119 }
1120 UIView* rootView = self.platformViews[viewId].root_view;
1121 views.push_back(rootView);
1122 self.currentCompositionParams.erase(viewId);
1123 self.viewsToRecomposite.erase(viewId);
1124 self.platformViews.erase(viewId);
1125 }
1126 self.viewsToDispose = std::move(viewsToDelayDispose);
1127 return views;
1128}
1129
1130- (void)resetFrameState {
1131 self.slices.clear();
1132 self.compositionOrder.clear();
1133 self.visitedPlatformViews.clear();
1134}
1135
1136- (void)pushVisitedPlatformViewId:(int64_t)viewId {
1137 self.visitedPlatformViews.push_back(viewId);
1138}
1139
1140- (void)pushClipRectToVisitedPlatformViews:(const flutter::DlRect&)clipRect {
1141 for (int64_t id : self.visitedPlatformViews) {
1142 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1144 self.currentCompositionParams[id] = params;
1145 }
1146}
1147
1148- (void)pushClipRRectToVisitedPlatformViews:(const flutter::DlRoundRect&)clipRRect {
1149 for (int64_t id : self.visitedPlatformViews) {
1150 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1152 self.currentCompositionParams[id] = params;
1153 }
1154}
1155
1156- (void)pushClipRSuperellipseToVisitedPlatformViews:(const flutter::DlRoundSuperellipse&)clipRse {
1157 for (int64_t id : self.visitedPlatformViews) {
1158 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1160 self.currentCompositionParams[id] = params;
1161 }
1162}
1163
1164- (void)pushClipPathToVisitedPlatformViews:(const flutter::DlPath&)clipPath {
1165 for (int64_t id : self.visitedPlatformViews) {
1166 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1168 self.currentCompositionParams[id] = params;
1169 }
1170}
1171
1172- (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId {
1173 return self.currentCompositionParams.find(viewId)->second;
1174}
1175
1176#pragma mark - Properties
1177
1178- (flutter::OverlayLayerPool*)layerPool {
1179 return _layerPool.get();
1180}
1181
1182- (std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>&)slices {
1183 return _slices;
1184}
1185
1186- (std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>&)factories {
1187 return _factories;
1188}
1189
1190- (std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&)
1191 gestureRecognizersBlockingPoliciesByType {
1193}
1194
1195- (std::unordered_map<int64_t, PlatformViewData>&)platformViews {
1196 return _platformViews;
1197}
1198
1199- (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams {
1201}
1202
1203- (std::unordered_set<int64_t>&)viewsToDispose {
1204 return _viewsToDispose;
1205}
1206
1207- (std::vector<int64_t>&)compositionOrder {
1208 return _compositionOrder;
1209}
1210
1211- (std::vector<int64_t>&)visitedPlatformViews {
1212 return _visitedPlatformViews;
1213}
1214
1215- (std::unordered_set<int64_t>&)viewsToRecomposite {
1216 return _viewsToRecomposite;
1217}
1218
1219- (NSArray<NSNumber*>*)previousCompositionOrder {
1220 // TODO(cbracken): Migrate to Obj-C types. https://github.com/flutter/flutter/issues/185139
1221 NSMutableArray* array = [NSMutableArray arrayWithCapacity:_previousCompositionOrder.size()];
1222 for (int64_t viewId : _previousCompositionOrder) {
1223 [array addObject:@(viewId)];
1224 }
1225 return array;
1226}
1227
1228@end
void(^ FlutterResult)(id _Nullable result)
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
static bool HasNonRectClipForUnderlayCutout(const flutter::EmbeddedViewParams &params)
std::unordered_map< int64_t, LayerData > LayersMap
std::vector< int64_t > _compositionOrder
std::unordered_set< int64_t > _viewsToRecomposite
std::unordered_map< std::string, NSObject< FlutterPlatformViewFactory > * > _factories
static NSString *const kGestureBlockingPolicyFallbackToPluginDefault
static NSString *const kGestureBlockingPolicyWaitUntilTouchesEndedValue
std::unordered_map< std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy > _gestureRecognizersBlockingPoliciesByType
std::vector< int64_t > _previousCompositionOrder
static void ApplyNonRectClipToOverlayCanvas(flutter::DlCanvas *overlay_canvas, const flutter::EmbeddedViewParams &params)
FlutterFMLTaskRunner * _platformTaskRunner
static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity
std::unordered_map< int64_t, PlatformViewData > _platformViews
std::unordered_map< int64_t, std::unique_ptr< flutter::EmbedderViewSlice > > _slices
std::vector< int64_t > _visitedPlatformViews
std::unordered_map< int64_t, flutter::EmbeddedViewParams > _currentCompositionParams
static void ResetAnchor(CALayer *layer)
static NSString *const kGestureBlockingPolicyDoNotBlockGesture
static CATransform3D GetCATransform3DFromDlMatrix(const DlMatrix &matrix)
static NSString *const kGestureBlockingPolicyEagerValue
static CGRect GetCGRectFromDlRect(const DlRect &clipDlRect)
std::unordered_set< int64_t > _viewsToDispose
FlutterPlatformViewGestureRecognizersBlockingPolicy
@ FlutterPlatformViewGestureRecognizersBlockingPolicyEager
@ FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded
@ FlutterPlatformViewGestureRecognizersBlockingPolicyDoNotBlockGesture
static bool TransformedRectCoversBounds(const DlRect &local_rect, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local rect, when transformed by the matrix, completely covers the indicated culling bou...
static bool TransformedRoundSuperellipseCoversBounds(const DlRoundSuperellipse &local_rse, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local round superellipse, when transformed by the matrix, completely covers the indicat...
static bool TransformedRRectCoversBounds(const DlRoundRect &local_rrect, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local round rect, when transformed by the matrix, completely covers the indicated culli...
Developer-facing API for rendering anything within the engine.
Definition dl_canvas.h:32
virtual void ClipRoundSuperellipse(const DlRoundSuperellipse &rse, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)=0
virtual void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)=0
virtual void ClipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)=0
virtual int GetSaveCount() const =0
virtual void RestoreToCount(int restore_count)=0
virtual void ClipPath(const DlPath &path, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false)=0
void Clear(DlColor color)
Definition dl_canvas.h:104
virtual void Save()=0
static DlPath MakeRoundRect(const DlRoundRect &rrect)
Definition dl_path.cc:72
static DlPath MakeRoundSuperellipse(const DlRoundSuperellipse &rse)
Definition dl_path.cc:85
void PushImageFilter(const std::shared_ptr< DlImageFilter > &filter, const DlRect &filter_rect)
void PushPlatformViewClipRRect(const DlRoundRect &clip_rrect)
void PushPlatformViewClipPath(const DlPath &clip_path)
void PushPlatformViewClipRSuperellipse(const DlRoundSuperellipse &clip_rse)
void PushPlatformViewClipRect(const DlRect &clip_rect)
const DlRect & finalBoundingRect() const
Storage for Overlay layers across frames.
const EmbeddedViewParams * params
FlView * view
const char * message
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
HWND(* FlutterPlatformViewFactory)(const FlutterPlatformViewCreationParameters *)
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
impeller::Scalar DlScalar
impeller::RoundRect DlRoundRect
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::ISize32 DlISize
std::unordered_map< int64_t, DlRect > SliceViews(DlCanvas *background_canvas, const std::vector< int64_t > &composition_order, const std::unordered_map< int64_t, std::unique_ptr< EmbedderViewSlice > > &slices, const std::unordered_map< int64_t, DlRect > &view_rects, const std::unordered_set< int64_t > &views_with_underlay_preserved)
Compute the required overlay layers and clip the view slices according to the size and position of th...
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
impeller::RoundSuperellipse DlRoundSuperellipse
SkMatrix ToSkMatrix(const DlMatrix &matrix)
internal::CopyableLambda< T > MakeCopyable(T lambda)
flutter::DlPath DlPath
Definition ref_ptr.h:261
std::shared_ptr< flutter::OverlayLayer > layer
FlutterTouchInterceptingView * touch_interceptor
NSObject< FlutterPlatformView > * view
static constexpr DlColor kTransparent()
Definition dl_color.h:68
A 4x4 matrix using column-major storage.
Definition matrix.h:37
constexpr bool IsIdentity() const
Definition matrix.h:475
Scalar m[16]
Definition matrix.h:39
constexpr const RoundingRadii & GetRadii() const
Type width
Definition size.h:28
const uintptr_t id
#define TRACE_EVENT0(category_group, name)
int BOOL