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- (void)pushFilterToVisitedPlatformViews:(const std::shared_ptr<flutter::DlImageFilter>&)filter
510 withRect:(const flutter::DlRect&)filterRect {
511 for (int64_t id : self.visitedPlatformViews) {
512 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
513 params.PushImageFilter(filter, filterRect);
514 self.currentCompositionParams[id] = params;
515 }
516}
517
518- (void)prerollCompositeEmbeddedView:(int64_t)viewId
519 withParams:(std::unique_ptr<flutter::EmbeddedViewParams>)params {
520 DlRect viewBounds = DlRect::MakeSize(self.frameSize);
521 std::unique_ptr<flutter::EmbedderViewSlice> view;
522 view = std::make_unique<flutter::DisplayListEmbedderViewSlice>(viewBounds);
523 self.slices.insert_or_assign(viewId, std::move(view));
524
525 self.compositionOrder.push_back(viewId);
526
527 if (self.currentCompositionParams.count(viewId) == 1 &&
528 self.currentCompositionParams[viewId] == *params.get()) {
529 // Do nothing if the params didn't change.
530 return;
531 }
532 self.currentCompositionParams[viewId] = flutter::EmbeddedViewParams(*params.get());
533 self.viewsToRecomposite.insert(viewId);
534}
535
536- (size_t)embeddedViewCount {
537 return self.compositionOrder.size();
538}
539
540- (UIView*)platformViewForId:(int64_t)viewId {
541 return [self flutterTouchInterceptingViewForId:viewId].embeddedView;
542}
543
544- (FlutterTouchInterceptingView*)flutterTouchInterceptingViewForId:(int64_t)viewId {
545 if (self.platformViews.empty()) {
546 return nil;
547 }
548 return self.platformViews[viewId].touch_interceptor;
549}
550
551- (long)firstResponderPlatformViewId {
552 for (auto const& [id, platformViewData] : self.platformViews) {
553 UIView* rootView = platformViewData.root_view;
554 if (rootView.flt_hasFirstResponderInViewHierarchySubtree) {
555 return id;
556 }
557 }
558 return -1;
559}
560
561- (void)clipViewSetMaskView:(UIView*)clipView {
562 FML_DCHECK([[NSThread currentThread] isMainThread]);
563 if (clipView.maskView) {
564 return;
565 }
566 CGRect frame =
567 CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y,
568 CGRectGetWidth(self.flutterView.bounds), CGRectGetHeight(self.flutterView.bounds));
569 clipView.maskView = [self.maskViewPool getMaskViewWithFrame:frame];
570}
571
572- (void)applyMutators:(const flutter::MutatorsStack&)mutatorsStack
573 embeddedView:(UIView*)embeddedView
574 boundingRect:(const DlRect&)boundingRect {
575 if (self.flutterView == nil) {
576 return;
577 }
578
579 ResetAnchor(embeddedView.layer);
580 ChildClippingView* clipView = (ChildClippingView*)embeddedView.superview;
581
582 DlMatrix transformMatrix;
583 NSMutableArray* blurFilters = [[NSMutableArray alloc] init];
584 NSMutableArray<PendingRRectClip*>* pendingClipRRects = [[NSMutableArray alloc] init];
585
586 FML_DCHECK(!clipView.maskView ||
587 [clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]);
588 if (clipView.maskView) {
589 [self.maskViewPool insertViewToPoolIfNeeded:(FlutterClippingMaskView*)(clipView.maskView)];
590 clipView.maskView = nil;
591 }
592 CGFloat screenScale = [UIScreen mainScreen].scale;
593 auto iter = mutatorsStack.Begin();
594 while (iter != mutatorsStack.End()) {
595 switch ((*iter)->GetType()) {
597 transformMatrix = transformMatrix * (*iter)->GetMatrix();
598 break;
599 }
602 (*iter)->GetRect(), transformMatrix, boundingRect)) {
603 break;
604 }
605 [self clipViewSetMaskView:clipView];
606 [(FlutterClippingMaskView*)clipView.maskView clipRect:(*iter)->GetRect()
607 matrix:transformMatrix];
608 break;
609 }
612 (*iter)->GetRRect(), transformMatrix, boundingRect)) {
613 break;
614 }
615 [self clipViewSetMaskView:clipView];
616 [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRRect()
617 matrix:transformMatrix];
618 break;
619 }
622 (*iter)->GetRSE(), transformMatrix, boundingRect)) {
623 break;
624 }
625 [self clipViewSetMaskView:clipView];
626 [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRSEApproximation()
627 matrix:transformMatrix];
628 break;
629 }
631 // TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning
632 // rect. See `ClipRRectContainsPlatformViewBoundingRect`.
633 // https://github.com/flutter/flutter/issues/118650
634 [self clipViewSetMaskView:clipView];
635 [(FlutterClippingMaskView*)clipView.maskView clipPath:(*iter)->GetPath()
636 matrix:transformMatrix];
637 break;
638 }
640 embeddedView.alpha = (*iter)->GetAlphaFloat() * embeddedView.alpha;
641 break;
643 // Only support DlBlurImageFilter for BackdropFilter.
644 if (!self.canApplyBlurBackdrop || !(*iter)->GetFilterMutation().GetFilter().asBlur()) {
645 break;
646 }
647 CGRect filterRect = GetCGRectFromDlRect((*iter)->GetFilterMutation().GetFilterRect());
648 // `filterRect` is in global coordinates. We need to convert to local space.
649 filterRect = CGRectApplyAffineTransform(
650 filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale));
651 // `filterRect` reprents the rect that should be filtered inside the `_flutterView`.
652 // The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be
653 // filtered.
654 if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
655 break;
656 }
657 CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
658 CGRect frameInClipView = [self.flutterView convertRect:intersection toView:clipView];
659 // sigma_x is arbitrarily chosen as the radius value because Quartz sets
660 // sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
661 // is not supported in Quartz's gaussianBlur CAFilter, so it is not used
662 // to blur the PlatformView.
663 CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
664 UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
665 initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
666
667 // TODO(https://github.com/flutter/flutter/issues/179126)
668 CGFloat cornerRadius = 0.0;
669 BOOL isRoundedSuperellipse = NO;
670 // If there's multiple clips, this uses the innermost to decide if its
671 // rse or not. The assumption being the innermost will be the tightest
672 if ([pendingClipRRects count] > 0) {
673 cornerRadius = pendingClipRRects.lastObject.topLeftRadius;
674 isRoundedSuperellipse = pendingClipRRects.lastObject.isRoundedSuperellipse;
675 [pendingClipRRects removeAllObjects];
676 }
677 visualEffectView.layer.cornerRadius = cornerRadius;
678 visualEffectView.layer.cornerCurve =
679 isRoundedSuperellipse ? kCACornerCurveContinuous : kCACornerCurveCircular;
680 visualEffectView.clipsToBounds = YES;
681
682 PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
683 blurRadius:blurRadius
684 cornerRadius:cornerRadius
685 isRoundedSuperellipse:isRoundedSuperellipse
686 visualEffectView:visualEffectView];
687 if (!filter) {
688 self.canApplyBlurBackdrop = NO;
689 } else {
690 [blurFilters addObject:filter];
691 }
692 break;
693 }
695 // The frame already handles cropping into the rect so this can
696 // no-op
697 break;
698 }
700 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
701 DlRoundRect rrect = (*iter)->GetBackdropClipRRect().rrect;
702
703 clip.rect = boundingRect;
704 impeller::RoundingRadii radii = rrect.GetRadii();
705 clip.topLeftRadius = radii.top_left.width;
706 clip.topRightRadius = radii.top_right.width;
709 [pendingClipRRects addObject:clip];
710 break;
711 }
713 PendingRRectClip* clip = [[PendingRRectClip alloc] init];
714 flutter::DlRoundSuperellipse rse = (*iter)->GetBackdropClipRSuperellipse().rse;
715
716 clip.rect = boundingRect;
717 impeller::RoundingRadii radii = rse.GetRadii();
718 clip.topLeftRadius = radii.top_left.width;
719 clip.topRightRadius = radii.top_right.width;
722 clip.isRoundedSuperellipse = YES;
723 [pendingClipRRects addObject:clip];
724 break;
725 }
727 // TODO(https://github.com/flutter/flutter/issues/179127)
728 break;
729 }
730 }
731 ++iter;
732 }
733
734 if (self.canApplyBlurBackdrop) {
735 [clipView applyBlurBackdropFilters:blurFilters];
736 }
737
738 // The UIKit frame is set based on the logical resolution (points) instead of physical.
739 // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
740 // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
741 // 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
742 // down to the logical resoltion before applying it to the layer of PlatformView.
743 flutter::DlScalar pointScale = 1.0 / screenScale;
744 transformMatrix = DlMatrix::MakeScale({pointScale, pointScale, 1}) * transformMatrix;
745
746 // Reverse the offset of the clipView.
747 // The clipView's frame includes the final translate of the final transform matrix.
748 // Thus, this translate needs to be reversed so the platform view can layout at the correct
749 // offset.
750 //
751 // Note that the transforms are not applied to the clipping paths because clipping paths happen on
752 // the mask view, whose origin is always (0,0) to the _flutterView.
753 impeller::Vector3 origin = impeller::Vector3(clipView.frame.origin.x, clipView.frame.origin.y);
754 transformMatrix = DlMatrix::MakeTranslation(-origin) * transformMatrix;
755
756 embeddedView.layer.transform = GetCATransform3DFromDlMatrix(transformMatrix);
757}
758
759- (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params {
760 // TODO(https://github.com/flutter/flutter/issues/109700)
761 CGRect frame = CGRectMake(0, 0, params.sizePoints().width, params.sizePoints().height);
762 FlutterTouchInterceptingView* touchInterceptor = self.platformViews[viewId].touch_interceptor;
763 touchInterceptor.layer.transform = CATransform3DIdentity;
764 touchInterceptor.frame = frame;
765 touchInterceptor.alpha = 1;
766
767 const flutter::MutatorsStack& mutatorStack = params.mutatorsStack();
768 UIView* clippingView = self.platformViews[viewId].root_view;
769 // The frame of the clipping view should be the final bounding rect.
770 // Because the translate matrix in the Mutator Stack also includes the offset,
771 // when we apply the transforms matrix in |applyMutators:embeddedView:boundingRect|, we need
772 // to remember to do a reverse translate.
773 const DlRect& rect = params.finalBoundingRect();
774 CGFloat screenScale = [UIScreen mainScreen].scale;
775 clippingView.frame = CGRectMake(rect.GetX() / screenScale, rect.GetY() / screenScale,
776 rect.GetWidth() / screenScale, rect.GetHeight() / screenScale);
777 [self applyMutators:mutatorStack embeddedView:touchInterceptor boundingRect:rect];
778}
779
780- (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId {
781 FML_DCHECK(self.slices.find(viewId) != self.slices.end());
782 return self.slices[viewId]->canvas();
783}
784
785- (void)reset {
786 // Reset will only be called from the raster thread or a merged raster/platform thread.
787 // _platformViews must only be modified on the platform thread, and any operations that
788 // read or modify platform views should occur there.
789 std::vector<int64_t> compositionOrder = self.compositionOrder;
790 [self.taskRunner runNowOrPostTask:^{
791 for (int64_t viewId : compositionOrder) {
792 [self.platformViews[viewId].root_view removeFromSuperview];
793 }
794 self.platformViews.clear();
795 _previousCompositionOrder.clear();
796 }];
797
798 self.compositionOrder.clear();
799 self.slices.clear();
800 self.currentCompositionParams.clear();
801 self.viewsToRecomposite.clear();
802 self.layerPool->RecycleLayers();
803 self.visitedPlatformViews.clear();
804}
805
806- (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)background_frame
807 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
808 TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
809
810 // No platform views to render.
811 if (self.flutterView == nil || (self.compositionOrder.empty() && !self.hadPlatformViews)) {
812 // No platform views to render but the FlutterView may need to be resized.
813 __weak FlutterPlatformViewsController* weakSelf = self;
814 if (self.flutterView != nil) {
815 // Pass frameSize by value since self.frameSize is mutated both here (on the platform
816 // thread) and in beginFrameWithSize: (on the raster thread).
817 const flutter::DlISize frameSize = self.frameSize;
818 [self.taskRunner runNowOrPostTask:^{
819 FlutterPlatformViewsController* strongSelf = weakSelf;
820 if (!strongSelf) {
821 return;
822 }
823 [strongSelf performResize:frameSize];
824 }];
825 }
826
827 self.hadPlatformViews = NO;
828 return background_frame->Submit();
829 }
830 self.hadPlatformViews = !self.compositionOrder.empty();
831
832 bool didEncode = true;
833 LayersMap platformViewLayers;
834 std::vector<std::unique_ptr<flutter::SurfaceFrame>> surfaceFrames;
835 surfaceFrames.reserve(self.compositionOrder.size());
836 std::unordered_map<int64_t, DlRect> viewRects;
837 std::unordered_set<int64_t> viewsWithUnderlayPreserved;
838
839 for (int64_t viewId : self.compositionOrder) {
840 const flutter::EmbeddedViewParams& params = self.currentCompositionParams[viewId];
841 viewRects[viewId] = params.finalBoundingRect();
843 viewsWithUnderlayPreserved.insert(viewId);
844 }
845 }
846
847 std::unordered_map<int64_t, DlRect> overlayLayers =
848 SliceViews(background_frame->Canvas(), self.compositionOrder, self.slices, viewRects,
849 viewsWithUnderlayPreserved);
850
851 size_t requiredOverlayLayers = 0;
852 for (int64_t viewId : self.compositionOrder) {
853 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
854 if (overlay == overlayLayers.end()) {
855 continue;
856 }
857 requiredOverlayLayers++;
858 }
859
860 // If there are not sufficient overlay layers, we must construct them on the platform
861 // thread, at least until we've refactored iOS surface creation to use IOSurfaces
862 // instead of CALayers.
863 [self createMissingOverlays:requiredOverlayLayers withIosContext:iosContext];
864
865 int64_t overlayId = 0;
866 for (int64_t viewId : self.compositionOrder) {
867 std::unordered_map<int64_t, DlRect>::const_iterator overlay = overlayLayers.find(viewId);
868 if (overlay == overlayLayers.end()) {
869 continue;
870 }
871 std::shared_ptr<flutter::OverlayLayer> layer = self.nextLayerInPool;
872 if (!layer) {
873 continue;
874 }
875
876 std::unique_ptr<flutter::SurfaceFrame> frame = layer->surface->AcquireFrame(self.frameSize);
877 // If frame is null, AcquireFrame already printed out an error message.
878 if (!frame) {
879 continue;
880 }
881 flutter::DlCanvas* overlayCanvas = frame->Canvas();
882 int restoreCount = overlayCanvas->GetSaveCount();
883 overlayCanvas->Save();
884 overlayCanvas->ClipRect(overlay->second);
885 if (viewsWithUnderlayPreserved.find(viewId) != viewsWithUnderlayPreserved.end()) {
886 ApplyNonRectClipToOverlayCanvas(overlayCanvas, self.currentCompositionParams[viewId]);
887 }
888 overlayCanvas->Clear(flutter::DlColor::kTransparent());
889 self.slices[viewId]->render_into(overlayCanvas);
890 overlayCanvas->RestoreToCount(restoreCount);
891
892 // This flutter view is never the last in a frame, since we always submit the
893 // underlay view last.
894 frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
895 layer->did_submit_last_frame = frame->Encode();
896
897 didEncode &= layer->did_submit_last_frame;
898 platformViewLayers[viewId] = LayerData{
899 .rect = overlay->second, //
900 .view_id = viewId, //
901 .overlay_id = overlayId, //
902 .layer = layer //
903 };
904 surfaceFrames.push_back(std::move(frame));
905 overlayId++;
906 }
907
908 auto previousSubmitInfo = background_frame->submit_info();
909 background_frame->set_submit_info({
910 .frame_damage = previousSubmitInfo.frame_damage,
911 .buffer_damage = previousSubmitInfo.buffer_damage,
912 .present_with_transaction = true,
913 });
914 background_frame->Encode();
915 surfaceFrames.push_back(std::move(background_frame));
916
917 // Mark all layers as available, so they can be used in the next frame.
918 std::vector<std::shared_ptr<flutter::OverlayLayer>> unusedLayers =
919 self.layerPool->RemoveUnusedLayers();
920 self.layerPool->RecycleLayers();
921 auto task = fml::MakeCopyable([self, //
922 platformViewLayers = std::move(platformViewLayers), //
923 currentCompositionParams = self.currentCompositionParams, //
924 viewsToRecomposite = self.viewsToRecomposite, //
925 compositionOrder = self.compositionOrder, //
926 unusedLayers = std::move(unusedLayers), //
927 surfaceFrames = std::move(surfaceFrames)]() mutable {
928 [self performSubmit:platformViewLayers
929 currentCompositionParams:currentCompositionParams
930 viewsToRecomposite:viewsToRecomposite
931 compositionOrder:compositionOrder
932 unusedLayers:unusedLayers
933 surfaceFrames:surfaceFrames];
934 });
935
936 [self.taskRunner runNowOrPostTask:^{
937 task();
938 }];
939 return didEncode;
940}
941
942- (void)createMissingOverlays:(size_t)requiredOverlayLayers
943 withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
944 TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
945
946 if (requiredOverlayLayers <= self.layerPool->size()) {
947 return;
948 }
949 auto missingLayerCount = requiredOverlayLayers - self.layerPool->size();
950
951 // If the raster thread isn't merged, create layers on the platform thread and block until
952 // complete. The self-capture here is fine since this is effectively synchronous (we block on the
953 // latch right below).
954 auto latch = std::make_shared<fml::CountDownLatch>(1u);
955 [self.taskRunner runNowOrPostTask:^{
956 for (auto i = 0u; i < missingLayerCount; i++) {
957 [self createLayerWithIosContext:iosContext
958 pixelFormat:((FlutterView*)self.flutterView).pixelFormat];
959 }
960 latch->CountDown();
961 }];
962 if (![[NSThread currentThread] isMainThread]) {
963 latch->Wait();
964 }
965}
966
967- (void)performResize:(const flutter::DlISize&)frameSize {
968 TRACE_EVENT0("flutter", "PlatformViewsController::PerformResize");
969 FML_DCHECK([[NSThread currentThread] isMainThread]);
970
971 if (self.flutterView != nil) {
972 [(FlutterView*)self.flutterView
973 setIntrinsicContentSize:CGSizeMake(frameSize.width, frameSize.height)];
974 }
975}
976
977- (void)performSubmit:(const LayersMap&)platformViewLayers
978 currentCompositionParams:
979 (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
980 viewsToRecomposite:(const std::unordered_set<int64_t>&)viewsToRecomposite
981 compositionOrder:(const std::vector<int64_t>&)compositionOrder
982 unusedLayers:
983 (const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
984 surfaceFrames:
985 (const std::vector<std::unique_ptr<flutter::SurfaceFrame>>&)surfaceFrames {
986 TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
987 FML_DCHECK([[NSThread currentThread] isMainThread]);
988
989 [CATransaction begin];
990
991 // Configure Flutter overlay views.
992 for (const auto& [viewId, layerData] : platformViewLayers) {
993 layerData.layer->UpdateViewState(self.flutterView, //
994 layerData.rect, //
995 layerData.view_id, //
996 layerData.overlay_id //
997 );
998 }
999
1000 // Dispose unused Flutter Views.
1001 for (auto& view : [self computeViewsToDispose]) {
1002 [view removeFromSuperview];
1003 }
1004
1005 // Composite Platform Views.
1006 for (int64_t viewId : viewsToRecomposite) {
1007 [self compositeView:viewId withParams:currentCompositionParams[viewId]];
1008 }
1009
1010 // Present callbacks.
1011 for (const auto& frame : surfaceFrames) {
1012 frame->Submit();
1013 }
1014
1015 // If a layer was allocated in the previous frame, but it's not used in the current frame,
1016 // then it can be removed from the scene.
1017 [self removeUnusedLayers:unusedLayers withCompositionOrder:compositionOrder];
1018
1019 // Organize the layers by their z indexes.
1020 [self bringLayersIntoView:platformViewLayers withCompositionOrder:compositionOrder];
1021
1022 [CATransaction commit];
1023}
1024
1025- (void)bringLayersIntoView:(const LayersMap&)layerMap
1026 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
1027 FML_DCHECK(self.flutterView);
1028 UIView* flutterView = self.flutterView;
1029
1031 NSMutableArray* desiredPlatformSubviews = [NSMutableArray array];
1032 for (int64_t platformViewId : compositionOrder) {
1033 _previousCompositionOrder.push_back(platformViewId);
1034 UIView* platformViewRoot = self.platformViews[platformViewId].root_view;
1035 if (platformViewRoot != nil) {
1036 [desiredPlatformSubviews addObject:platformViewRoot];
1037 }
1038
1039 auto maybeLayerData = layerMap.find(platformViewId);
1040 if (maybeLayerData != layerMap.end()) {
1041 auto view = maybeLayerData->second.layer->overlay_view_wrapper;
1042 if (view != nil) {
1043 [desiredPlatformSubviews addObject:view];
1044 }
1045 }
1046 }
1047
1048 NSSet* desiredPlatformSubviewsSet = [NSSet setWithArray:desiredPlatformSubviews];
1049 NSArray* existingPlatformSubviews = [flutterView.subviews
1050 filteredArrayUsingPredicate:[NSPredicate
1051 predicateWithBlock:^BOOL(id object, NSDictionary* bindings) {
1052 return [desiredPlatformSubviewsSet containsObject:object];
1053 }]];
1054
1055 // Manipulate view hierarchy only if needed, to address a performance issue where
1056 // this method is called even when view hierarchy stays the same.
1057 // See: https://github.com/flutter/flutter/issues/121833
1058 // TODO(hellohuanlin): investigate if it is possible to skip unnecessary bringLayersIntoView.
1059 if (![desiredPlatformSubviews isEqualToArray:existingPlatformSubviews]) {
1060 for (UIView* subview in desiredPlatformSubviews) {
1061 // `addSubview` will automatically reorder subview if it is already added.
1062 [flutterView addSubview:subview];
1063 }
1064 }
1065}
1066
1067- (std::shared_ptr<flutter::OverlayLayer>)nextLayerInPool {
1068 return self.layerPool->GetNextLayer();
1069}
1070
1071- (void)createLayerWithIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
1072 pixelFormat:(MTLPixelFormat)pixelFormat {
1073 self.layerPool->CreateLayer(iosContext, pixelFormat);
1074}
1075
1076- (void)removeUnusedLayers:(const std::vector<std::shared_ptr<flutter::OverlayLayer>>&)unusedLayers
1077 withCompositionOrder:(const std::vector<int64_t>&)compositionOrder {
1078 for (const std::shared_ptr<flutter::OverlayLayer>& layer : unusedLayers) {
1079 [layer->overlay_view_wrapper removeFromSuperview];
1080 }
1081
1082 std::unordered_set<int64_t> compositionOrderSet;
1083 for (int64_t viewId : compositionOrder) {
1084 compositionOrderSet.insert(viewId);
1085 }
1086 // Remove unused platform views.
1087 for (int64_t viewId : _previousCompositionOrder) {
1088 if (compositionOrderSet.find(viewId) == compositionOrderSet.end()) {
1089 UIView* platformViewRoot = self.platformViews[viewId].root_view;
1090 [platformViewRoot removeFromSuperview];
1091 }
1092 }
1093}
1094
1095- (std::vector<UIView*>)computeViewsToDispose {
1096 std::vector<UIView*> views;
1097 if (self.viewsToDispose.empty()) {
1098 return views;
1099 }
1100
1101 std::unordered_set<int64_t> viewsToComposite(self.compositionOrder.begin(),
1102 self.compositionOrder.end());
1103 std::unordered_set<int64_t> viewsToDelayDispose;
1104 for (int64_t viewId : self.viewsToDispose) {
1105 if (viewsToComposite.count(viewId)) {
1106 viewsToDelayDispose.insert(viewId);
1107 continue;
1108 }
1109 UIView* rootView = self.platformViews[viewId].root_view;
1110 views.push_back(rootView);
1111 self.currentCompositionParams.erase(viewId);
1112 self.viewsToRecomposite.erase(viewId);
1113 self.platformViews.erase(viewId);
1114 }
1115 self.viewsToDispose = std::move(viewsToDelayDispose);
1116 return views;
1117}
1118
1119- (void)resetFrameState {
1120 self.slices.clear();
1121 self.compositionOrder.clear();
1122 self.visitedPlatformViews.clear();
1123}
1124
1125- (void)pushVisitedPlatformViewId:(int64_t)viewId {
1126 self.visitedPlatformViews.push_back(viewId);
1127}
1128
1129- (void)pushClipRectToVisitedPlatformViews:(const flutter::DlRect&)clipRect {
1130 for (int64_t id : self.visitedPlatformViews) {
1131 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1133 self.currentCompositionParams[id] = params;
1134 }
1135}
1136
1137- (void)pushClipRRectToVisitedPlatformViews:(const flutter::DlRoundRect&)clipRRect {
1138 for (int64_t id : self.visitedPlatformViews) {
1139 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1141 self.currentCompositionParams[id] = params;
1142 }
1143}
1144
1145- (void)pushClipRSuperellipseToVisitedPlatformViews:(const flutter::DlRoundSuperellipse&)clipRse {
1146 for (int64_t id : self.visitedPlatformViews) {
1147 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1149 self.currentCompositionParams[id] = params;
1150 }
1151}
1152
1153- (void)pushClipPathToVisitedPlatformViews:(const flutter::DlPath&)clipPath {
1154 for (int64_t id : self.visitedPlatformViews) {
1155 flutter::EmbeddedViewParams params = self.currentCompositionParams[id];
1157 self.currentCompositionParams[id] = params;
1158 }
1159}
1160
1161- (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId {
1162 return self.currentCompositionParams.find(viewId)->second;
1163}
1164
1165#pragma mark - Properties
1166
1167- (flutter::OverlayLayerPool*)layerPool {
1168 return _layerPool.get();
1169}
1170
1171- (std::unordered_map<int64_t, std::unique_ptr<flutter::EmbedderViewSlice>>&)slices {
1172 return _slices;
1173}
1174
1175- (std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*>&)factories {
1176 return _factories;
1177}
1178
1179- (std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>&)
1180 gestureRecognizersBlockingPoliciesByType {
1182}
1183
1184- (std::unordered_map<int64_t, PlatformViewData>&)platformViews {
1185 return _platformViews;
1186}
1187
1188- (std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams {
1190}
1191
1192- (std::unordered_set<int64_t>&)viewsToDispose {
1193 return _viewsToDispose;
1194}
1195
1196- (std::vector<int64_t>&)compositionOrder {
1197 return _compositionOrder;
1198}
1199
1200- (std::vector<int64_t>&)visitedPlatformViews {
1201 return _visitedPlatformViews;
1202}
1203
1204- (std::unordered_set<int64_t>&)viewsToRecomposite {
1205 return _viewsToRecomposite;
1206}
1207
1208- (NSArray<NSNumber*>*)previousCompositionOrder {
1209 // TODO(cbracken): Migrate to Obj-C types. https://github.com/flutter/flutter/issues/185139
1210 NSMutableArray* array = [NSMutableArray arrayWithCapacity:_previousCompositionOrder.size()];
1211 for (int64_t viewId : _previousCompositionOrder) {
1212 [array addObject:@(viewId)];
1213 }
1214 return array;
1215}
1216
1217@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