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 visualEffectView.layer.cornerCurve =
688 isRoundedSuperellipse ? kCACornerCurveContinuous : kCACornerCurveCircular;
689 visualEffectView.clipsToBounds = YES;
690
691 PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
692 blurRadius:blurRadius
693 cornerRadius:cornerRadius
694 isRoundedSuperellipse:isRoundedSuperellipse
695 visualEffectView:visualEffectView];
696 if (!filter) {
697 self.canApplyBlurBackdrop = NO;
698 } else {
699 [blurFilters addObject:filter];
700 }
701 break;
702 }
704 // The frame already handles cropping into the rect so this can
705 // no-op
706 break;
707 }
709 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
710 DlRoundRect rrect = (*iter)->GetBackdropClipRRect().rrect;
711
712 clip.rect = boundingRect;
713 impeller::RoundingRadii radii = rrect.GetRadii();
714 clip.topLeftRadius = radii.top_left.width;
715 clip.topRightRadius = radii.top_right.width;
718 [pendingClipRRects addObject:clip];
719 break;
720 }
722 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
723 flutter::DlRoundSuperellipse rse = (*iter)->GetBackdropClipRSuperellipse().rse;
724
725 clip.rect = boundingRect;
726 impeller::RoundingRadii radii = rse.GetRadii();
727 clip.topLeftRadius = radii.top_left.width;
728 clip.topRightRadius = radii.top_right.width;
731 clip.isRoundedSuperellipse = YES;
732 [pendingClipRRects addObject:clip];
733 break;
734 }
736 // TODO(https://github.com/flutter/flutter/issues/179127)
737 break;
738 }
739 }
740 ++iter;
741 }
742
743 if (self.canApplyBlurBackdrop) {
744 [clipView applyBlurBackdropFilters:blurFilters];
745 }
746
747 // The UIKit frame is set based on the logical resolution (points) instead of physical.
748 // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
749 // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
750 // 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
751 // down to the logical resoltion before applying it to the layer of PlatformView.
752 flutter::DlScalar pointScale = 1.0 / screenScale;
753 transformMatrix = DlMatrix::MakeScale({pointScale, pointScale, 1}) * transformMatrix;
754
755 // Reverse the offset of the clipView.
756 // The clipView's frame includes the final translate of the final transform matrix.
757 // Thus, this translate needs to be reversed so the platform view can layout at the correct
758 // offset.
759 //
760 // Note that the transforms are not applied to the clipping paths because clipping paths happen on
761 // the mask view, whose origin is always (0,0) to the _flutterView.
762 impeller::Vector3 origin = impeller::Vector3(clipView.frame.origin.x, clipView.frame.origin.y);
763 transformMatrix = DlMatrix::MakeTranslation(-origin) * transformMatrix;
764
765 embeddedView.layer.transform = GetCATransform3DFromDlMatrix(transformMatrix);
766}
767
768- (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params {
769 // TODO(https://github.com/flutter/flutter/issues/109700)
770 CGRect frame = CGRectMake(0, 0, params.sizePoints().width, params.sizePoints().height);
771 FlutterTouchInterceptingView* touchInterceptor = self.platformViews[viewId].touch_interceptor;
772 touchInterceptor.layer.transform = CATransform3DIdentity;
773 touchInterceptor.frame = frame;
774 touchInterceptor.alpha = 1;
775
776 const flutter::MutatorsStack& mutatorStack = params.mutatorsStack();
777 UIView* clippingView = self.platformViews[viewId].root_view;
778 // The frame of the clipping view should be the final bounding rect.
779 // Because the translate matrix in the Mutator Stack also includes the offset,
780 // when we apply the transforms matrix in |applyMutators:embeddedView:boundingRect|, we need
781 // to remember to do a reverse translate.
782 const DlRect& rect = params.finalBoundingRect();
783 CGFloat screenScale = [UIScreen mainScreen].scale;
784 clippingView.frame = CGRectMake(rect.GetX() / screenScale, rect.GetY() / screenScale,
785 rect.GetWidth() / screenScale, rect.GetHeight() / screenScale);
786 [self applyMutators:mutatorStack embeddedView:touchInterceptor boundingRect:rect];
787}
788
789- (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId {
790 FML_DCHECK(self.slices.find(viewId) != self.slices.end());
791 return self.slices[viewId]->canvas();
792}
793
794- (void)reset {
795 // Reset will only be called from the raster thread or a merged raster/platform thread.
796 // _platformViews must only be modified on the platform thread, and any operations that
797 // read or modify platform views should occur there.
798 std::vector<int64_t> compositionOrder = self.compositionOrder;
799 [self.taskRunner runNowOrPostTask:^{
800 for (int64_t viewId : compositionOrder) {
801 [self.platformViews[viewId].root_view removeFromSuperview];
802 }
803 self.platformViews.clear();
804 _previousCompositionOrder.clear();
805 }];
806
807 self.compositionOrder.clear();
808 self.slices.clear();
809 self.currentCompositionParams.clear();
810 self.viewsToRecomposite.clear();
811 self.layerPool->RecycleLayers();
812 self.visitedPlatformViews.clear();
813}
814
815- (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)background_frame
816 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
817 TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
818
819 // No platform views to render.
820 if (self.flutterView == nil || (self.compositionOrder.empty() && !self.hadPlatformViews)) {
821 // No platform views to render but the FlutterView may need to be resized.
822 __weak FlutterPlatformViewsController* weakSelf = self;
823 if (self.flutterView != nil) {
824 // Pass frameSize by value since self.frameSize is mutated both here (on the platform
825 // thread) and in beginFrameWithSize: (on the raster thread).
826 const flutter::DlISize frameSize = self.frameSize;
827 [self.taskRunner runNowOrPostTask:^{
828 FlutterPlatformViewsController* strongSelf = weakSelf;
829 if (!strongSelf) {
830 return;
831 }
832 [strongSelf performResize:frameSize];
833 }];
834 }
835
836 self.hadPlatformViews = NO;
837 return background_frame->Submit();
838 }
839 self.hadPlatformViews = !self.compositionOrder.empty();
840
841 bool didEncode = true;
842 LayersMap platformViewLayers;
843 std::vector<std::unique_ptr<flutter::SurfaceFrame>> surfaceFrames;
844 surfaceFrames.reserve(self.compositionOrder.size());
845 std::unordered_map<int64_t, DlRect> viewRects;
846 std::unordered_set<int64_t> viewsWithUnderlayPreserved;
847
848 for (int64_t viewId : self.compositionOrder) {
849 const flutter::EmbeddedViewParams& params = self.currentCompositionParams[viewId];
850 viewRects[viewId] = params.finalBoundingRect();
852 viewsWithUnderlayPreserved.insert(viewId);
853 }
854 }
855
856 std::unordered_map<int64_t, DlRect> overlayLayers =
857 SliceViews(background_frame->Canvas(), self.compositionOrder, self.slices, viewRects,
858 viewsWithUnderlayPreserved);
859
860 size_t requiredOverlayLayers = 0;
861 for (int64_t viewId : self.compositionOrder) {
862 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
863 if (overlay == overlayLayers.end()) {
864 continue;
865 }
866 requiredOverlayLayers++;
867 }
868
869 // If there are not sufficient overlay layers, we must construct them on the platform
870 // thread, at least until we've refactored iOS surface creation to use IOSurfaces
871 // instead of CALayers.
872 [self createMissingOverlays:requiredOverlayLayers withIosContext:iosContext];
873
874 int64_t overlayId = 0;
875 for (int64_t viewId : self.compositionOrder) {
876 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
877 if (overlay == overlayLayers.end()) {
878 continue;
879 }
880 std::shared_ptr<flutter::OverlayLayer> layer = self.nextLayerInPool;
881 if (!layer) {
882 continue;
883 }
884
885 std::unique_ptr<flutter::SurfaceFrame> frame = layer->surface->AcquireFrame(self.frameSize);
886 // If frame is null, AcquireFrame already printed out an error message.
887 if (!frame) {
888 continue;
889 }
890 flutter::DlCanvas* overlayCanvas = frame->Canvas();
891 int restoreCount = overlayCanvas->GetSaveCount();
892 overlayCanvas->Save();
893 overlayCanvas->ClipRect(overlay->second);
894 if (viewsWithUnderlayPreserved.find(viewId) != viewsWithUnderlayPreserved.end()) {
895 ApplyNonRectClipToOverlayCanvas(overlayCanvas, self.currentCompositionParams[viewId]);
896 }
897 overlayCanvas->Clear(flutter::DlColor::kTransparent());
898 self.slices[viewId]->render_into(overlayCanvas);
899 overlayCanvas->RestoreToCount(restoreCount);
900
901 // This flutter view is never the last in a frame, since we always submit the
902 // underlay view last.
903 frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
904 layer->did_submit_last_frame = frame->Encode();
905
906 didEncode &= layer->did_submit_last_frame;
907 platformViewLayers[viewId] = LayerData{
908 .rect = overlay->second, //
909 .view_id = viewId, //
910 .overlay_id = overlayId, //
911 .layer = layer //
912 };
913 surfaceFrames.push_back(std::move(frame));
914 overlayId++;
915 }
916
917 auto previousSubmitInfo = background_frame->submit_info();
918 background_frame->set_submit_info({
919 .frame_damage = previousSubmitInfo.frame_damage,
920 .buffer_damage = previousSubmitInfo.buffer_damage,
921 .present_with_transaction = true,
922 });
923 background_frame->Encode();
924 surfaceFrames.push_back(std::move(background_frame));
925
926 // Mark all layers as available, so they can be used in the next frame.
927 std::vector<std::shared_ptr<flutter::OverlayLayer>> unusedLayers =
928 self.layerPool->RemoveUnusedLayers();
929 self.layerPool->RecycleLayers();
930 auto task = fml::MakeCopyable([self, //
931 platformViewLayers = std::move(platformViewLayers), //
932 currentCompositionParams = self.currentCompositionParams, //
933 viewsToRecomposite = self.viewsToRecomposite, //
934 compositionOrder = self.compositionOrder, //
935 unusedLayers = std::move(unusedLayers), //
936 surfaceFrames = std::move(surfaceFrames)]() mutable {
937 [self performSubmit:platformViewLayers
938 currentCompositionParams:currentCompositionParams
939 viewsToRecomposite:viewsToRecomposite
940 compositionOrder:compositionOrder
941 unusedLayers:unusedLayers
942 surfaceFrames:surfaceFrames];
943 });
944
945 [self.taskRunner runNowOrPostTask:^{
946 task();
947 }];
948 return didEncode;
949}
950
951- (void)createMissingOverlays:(size_t)requiredOverlayLayers
952 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
953 TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
954
955 if (requiredOverlayLayers <= self.layerPool->size()) {
956 return;
957 }
958 auto missingLayerCount = requiredOverlayLayers - self.layerPool->size();
959
960 // If the raster thread isn't merged, create layers on the platform thread and block until
961 // complete. The self-capture here is fine since this is effectively synchronous (we block on the
962 // latch right below).
963 auto latch = std::make_shared<fml::CountDownLatch>(1u);
964 [self.taskRunner runNowOrPostTask:^{
965 for (auto i = 0u; i < missingLayerCount; i++) {
966 [self createLayerWithIosContext:iosContext
967 pixelFormat:((FlutterView*)self.flutterView).pixelFormat];
968 }
969 latch->CountDown();
970 }];
971 if (![[NSThread currentThread] isMainThread]) {
972 latch->Wait();
973 }
974}
975
976- (void)performResize:(const flutter::DlISize&)frameSize {
977 TRACE_EVENT0("flutter", "PlatformViewsController::PerformResize");
978 FML_DCHECK([[NSThread currentThread] isMainThread]);
979
980 if (self.flutterView != nil) {
981 [(FlutterView*)self.flutterView
982 setIntrinsicContentSize:CGSizeMake(frameSize.width, frameSize.height)];
983 }
984}
985
986- (void)performSubmit:(const LayersMap&)platformViewLayers
987 currentCompositionParams:
988 (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
989 viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
990 compositionOrder:(const std::vector<int64_t>&)compositionOrder
991 unusedLayers:
992 (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
993 surfaceFrames:
994 (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames {
995 TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
996 FML_DCHECK([[NSThread currentThread] isMainThread]);
997
998 [CATransaction begin];
999
1000 // Configure Flutter overlay views.
1001 for (const auto& [viewId, layerData] : platformViewLayers) {
1002 layerData.layer->UpdateViewState(self.flutterView, //
1003 layerData.rect, //
1004 layerData.view_id, //
1005 layerData.overlay_id //
1006 );
1007 }
1008
1009 // Dispose unused Flutter Views.
1010 for (auto& view : [self computeViewsToDispose]) {
1011 [view removeFromSuperview];
1012 }
1013
1014 // Composite Platform Views.
1015 for (int64_t viewId : viewsToRecomposite) {
1016 [self compositeView:viewId withParams:currentCompositionParams[viewId]];
1017 }
1018
1019 // Present callbacks.
1020 for (const auto& frame : surfaceFrames) {
1021 frame->Submit();
1022 }
1023
1024 // If a layer was allocated in the previous frame, but it's not used in the current frame,
1025 // then it can be removed from the scene.
1026 [self removeUnusedLayers:unusedLayers withCompositionOrder:compositionOrder];
1027
1028 // Organize the layers by their z indexes.
1029 [self bringLayersIntoView:platformViewLayers withCompositionOrder:compositionOrder];
1030
1031 [CATransaction commit];
1032}
1033
1034- (void)bringLayersIntoView:(const LayersMap&)layerMap
1035 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
1036 FML_DCHECK(self.flutterView);
1037 UIView* flutterView = self.flutterView;
1038
1040 NSMutableArray* desiredPlatformSubviews = [NSMutableArray array];
1041 for (int64_t platformViewId : compositionOrder) {
1042 _previousCompositionOrder.push_back(platformViewId);
1043 UIView* platformViewRoot = self.platformViews[platformViewId].root_view;
1044 if (platformViewRoot != nil) {
1045 [desiredPlatformSubviews addObject:platformViewRoot];
1046 }
1047
1048 auto maybeLayerData = layerMap.find(platformViewId);
1049 if (maybeLayerData != layerMap.end()) {
1050 auto view = maybeLayerData->second.layer->overlay_view_wrapper;
1051 if (view != nil) {
1052 [desiredPlatformSubviews addObject:view];
1053 }
1054 }
1055 }
1056
1057 NSSet* desiredPlatformSubviewsSet = [NSSet setWithArray:desiredPlatformSubviews];
1058 NSArray* existingPlatformSubviews = [flutterView.subviews
1059 filteredArrayUsingPredicate:[NSPredicate
1060 predicateWithBlock:^BOOL(id object, NSDictionary* bindings) {
1061 return [desiredPlatformSubviewsSet containsObject:object];
1062 }]];
1063
1064 // Manipulate view hierarchy only if needed, to address a performance issue where
1065 // this method is called even when view hierarchy stays the same.
1066 // See: https://github.com/flutter/flutter/issues/121833
1067 // TODO(hellohuanlin): investigate if it is possible to skip unnecessary bringLayersIntoView.
1068 if (![desiredPlatformSubviews isEqualToArray:existingPlatformSubviews]) {
1069 for (UIView* subview in desiredPlatformSubviews) {
1070 // `addSubview` will automatically reorder subview if it is already added.
1071 [flutterView addSubview:subview];
1072 }
1073 }
1074}
1075
1076- (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool {
1077 return self.layerPool->GetNextLayer();
1078}
1079
1080- (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
1081 pixelFormat:(MTLPixelFormat)pixelFormat {
1082 self.layerPool->CreateLayer(iosContext, pixelFormat);
1083}
1084
1085- (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
1086 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
1087 for (const std::shared_ptr<flutter::OverlayLayer>& layer : unusedLayers) {
1088 [layer->overlay_view_wrapper removeFromSuperview];
1089 }
1090
1091 std::unordered_set<int64_t> compositionOrderSet;
1092 for (int64_t viewId : compositionOrder) {
1093 compositionOrderSet.insert(viewId);
1094 }
1095 // Remove unused platform views.
1096 for (int64_t viewId : _previousCompositionOrder) {
1097 if (compositionOrderSet.find(viewId) == compositionOrderSet.end()) {
1098 UIView* platformViewRoot = self.platformViews[viewId].root_view;
1099 [platformViewRoot removeFromSuperview];
1100 }
1101 }
1102}
1103
1104- (std::vector<UIView*>)computeViewsToDispose {
1105 std::vector<UIView*> views;
1106 if (self.viewsToDispose.empty()) {
1107 return views;
1108 }
1109
1110 std::unordered_set<int64_t> viewsToComposite(self.compositionOrder.begin(),
1111 self.compositionOrder.end());
1112 std::unordered_set<int64_t> viewsToDelayDispose;
1113 for (int64_t viewId : self.viewsToDispose) {
1114 if (viewsToComposite.count(viewId)) {
1115 viewsToDelayDispose.insert(viewId);
1116 continue;
1117 }
1118 UIView* rootView = self.platformViews[viewId].root_view;
1119 views.push_back(rootView);
1120 self.currentCompositionParams.erase(viewId);
1121 self.viewsToRecomposite.erase(viewId);
1122 self.platformViews.erase(viewId);
1123 }
1124 self.viewsToDispose = std::move(viewsToDelayDispose);
1125 return views;
1126}
1127
1128- (void)resetFrameState {
1129 self.slices.clear();
1130 self.compositionOrder.clear();
1131 self.visitedPlatformViews.clear();
1132}
1133
1134- (void)pushVisitedPlatformViewId:(int64_t)viewId {
1135 self.visitedPlatformViews.push_back(viewId);
1136}
1137
1138- (void)pushClipRectToVisitedPlatformViews:(const flutter::DlRect&)clipRect {
1139 for (int64_t id : self.visitedPlatformViews) {
1140 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1142 self.currentCompositionParams[id] = params;
1143 }
1144}
1145
1146- (void)pushClipRRectToVisitedPlatformViews:(const flutter::DlRoundRect&)clipRRect {
1147 for (int64_t id : self.visitedPlatformViews) {
1148 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1150 self.currentCompositionParams[id] = params;
1151 }
1152}
1153
1154- (void)pushClipRSuperellipseToVisitedPlatformViews:(const flutter::DlRoundSuperellipse&)clipRse {
1155 for (int64_t id : self.visitedPlatformViews) {
1156 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1158 self.currentCompositionParams[id] = params;
1159 }
1160}
1161
1162- (void)pushClipPathToVisitedPlatformViews:(const flutter::DlPath&)clipPath {
1163 for (int64_t id : self.visitedPlatformViews) {
1164 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1166 self.currentCompositionParams[id] = params;
1167 }
1168}
1169
1170- (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId {
1171 return self.currentCompositionParams.find(viewId)->second;
1172}
1173
1174#pragma mark - Properties
1175
1176- (flutter::OverlayLayerPool*)layerPool {
1177 return _layerPool.get();
1178}
1179
1180- (std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>&)slices {
1181 return _slices;
1182}
1183
1184- (std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>&)factories {
1185 return _factories;
1186}
1187
1188- (std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&)
1189 gestureRecognizersBlockingPoliciesByType {
1191}
1192
1193- (std::unordered_map<int64_t, PlatformViewData>&)platformViews {
1194 return _platformViews;
1195}
1196
1197- (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams {
1199}
1200
1201- (std::unordered_set<int64_t>&)viewsToDispose {
1202 return _viewsToDispose;
1203}
1204
1205- (std::vector<int64_t>&)compositionOrder {
1206 return _compositionOrder;
1207}
1208
1209- (std::vector<int64_t>&)visitedPlatformViews {
1210 return _visitedPlatformViews;
1211}
1212
1213- (std::unordered_set<int64_t>&)viewsToRecomposite {
1214 return _viewsToRecomposite;
1215}
1216
1217- (NSArray<NSNumber*>*)previousCompositionOrder {
1218 // TODO(cbracken): Migrate to Obj-C types. https://github.com/flutter/flutter/issues/185139
1219 NSMutableArray* array = [NSMutableArray arrayWithCapacity:_previousCompositionOrder.size()];
1220 for (int64_t viewId : _previousCompositionOrder) {
1221 [array addObject:@(viewId)];
1222 }
1223 return array;
1224}
1225
1226@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