-
Notifications
You must be signed in to change notification settings - Fork 6k
Use a single mask view to clip platform view #20050
Changes from 4 commits
db33eae
31db69b
0158df3
9785f97
eab8257
c0627b6
31d44e8
087b769
7c2ae3a
3a703fd
1ad2449
cab73d9
9b59753
ae69e2b
fa983ab
a1cd71a
4c102ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,32 @@ | |
| #include "flutter/shell/platform/darwin/ios/ios_context.h" | ||
| #include "third_party/skia/include/core/SkPictureRecorder.h" | ||
|
|
||
| // A UIView acts as a clipping mask for the |ChildClippingView|. | ||
| // | ||
| // On |DrawRect:|, this view performs a series of clipping operations and sets the alpha channel | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this interface contain the virtual
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This View is an OBJC class, drawRect is inherited from the UIView. So I don't think any virtual keyword is necessary here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sg. do you where I can read about the doc format? I'm not sure what
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean the notation. It reads like a class to me, but I’m not sure what the colon symbol is used for.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah you are right, let me fix that |
||
| // to the final resulting area to be 1; it also sets the "clipped out" area's alpha channel to be 0. | ||
| // | ||
| // When a UIView sets a |FlutterClippingMaskView| as its `maskView`, the alpha channel of the UIView | ||
| // is replaced with the alpha channel of the |FlutterClippingMaskView|. | ||
| @interface FlutterClippingMaskView : UIView | ||
|
|
||
| // Adds a clip rect operation to the queue. | ||
| // | ||
| // The `clipSkRect` is transformed with the `matrix` before adding to the queue. | ||
| - (void)clipRect:(const SkRect&)clipSkRect matrix:(const CATransform3D&)matrix; | ||
|
|
||
| // Adds a clip rrect operation to the queue. | ||
| // | ||
| // The `clipSkRRect` is transformed with the `matrix` before adding to the queue. | ||
| - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const CATransform3D&)matrix; | ||
|
|
||
| // Adds a clip path operation to the queue. | ||
| // | ||
| // The `path` is transformed with the `matrix` before adding to the queue. | ||
| - (void)clipPath:(const SkPath&)path matrix:(const CATransform3D&)matrix; | ||
|
|
||
| @end | ||
|
|
||
| // A UIView that is used as the parent for embedded UIViews. | ||
| // | ||
| // This view has 2 roles: | ||
|
|
@@ -37,14 +63,6 @@ | |
| // The parent view handles clipping to its subviews. | ||
| @interface ChildClippingView : UIView | ||
|
|
||
| // Performs the clipping based on the type. | ||
| // | ||
| // The `type` must be one of the 3: clip_rect, clip_rrect, clip_path. | ||
| - (void)setClip:(flutter::MutatorType)type | ||
| rect:(const SkRect&)rect | ||
| rrect:(const SkRRect&)rrect | ||
| path:(const SkPath&)path; | ||
|
|
||
| @end | ||
|
|
||
| namespace flutter { | ||
|
|
@@ -253,20 +271,6 @@ class FlutterPlatformViewsController { | |
| // Traverse the `mutators_stack` and return the number of clip operations. | ||
| int CountClips(const MutatorsStack& mutators_stack); | ||
|
|
||
| // Make sure that platform_view has exactly clip_count ChildClippingView ancestors. | ||
| // | ||
| // Existing ChildClippingViews are re-used. If there are currently more ChildClippingView | ||
| // ancestors than needed, the extra views are detached. If there are less ChildClippingView | ||
| // ancestors than needed, new ChildClippingViews will be added. | ||
| // | ||
| // If head_clip_view was attached as a subview to FlutterView, the head of the newly constructed | ||
| // ChildClippingViews chain is attached to FlutterView in the same position. | ||
| // | ||
| // Returns the new head of the clip views chain. | ||
| UIView* ReconstructClipViewsChain(int number_of_clips, | ||
| UIView* platform_view, | ||
| UIView* head_clip_view); | ||
|
|
||
| // Applies the mutators in the mutators_stack to the UIView chain that was constructed by | ||
| // `ReconstructClipViewsChain` | ||
| // | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,32 +53,72 @@ void ResetAnchor(CALayer* layer) { | |
|
|
||
| @implementation ChildClippingView | ||
|
|
||
| + (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect { | ||
| return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft, | ||
| clipSkRect.fBottom - clipSkRect.fTop); | ||
| // The ChildClippingView's frame is the bounding rect of the platform view. we only want touches to | ||
| // be hit tested and consumed by this view if they are inside the embedded platform view which could | ||
| // be smaller the embedded platform view is rotated. | ||
| - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event { | ||
| for (UIView* view in self.subviews) { | ||
| if ([view pointInside:[self convertPoint:point toView:view] withEvent:event]) { | ||
| return YES; | ||
| } | ||
| } | ||
| return NO; | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| @interface FlutterClippingMaskView () | ||
|
|
||
| - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix; | ||
| - (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect; | ||
|
|
||
| @end | ||
|
|
||
| @implementation FlutterClippingMaskView { | ||
| std::vector<fml::CFRef<CGPathRef>> paths_; | ||
| } | ||
|
|
||
| - (instancetype)initWithFrame:(CGRect)frame { | ||
| if ([super initWithFrame:frame]) { | ||
| self.backgroundColor = UIColor.clearColor; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (void)clipRect:(const SkRect&)clipSkRect { | ||
| CGRect clipRect = [ChildClippingView getCGRectFromSkRect:clipSkRect]; | ||
| fml::CFRef<CGPathRef> pathRef(CGPathCreateWithRect(clipRect, nil)); | ||
| CAShapeLayer* clip = [[[CAShapeLayer alloc] init] autorelease]; | ||
| clip.path = pathRef; | ||
| self.layer.mask = clip; | ||
| - (void)drawRect:(CGRect)rect { | ||
| CGContextRef context = UIGraphicsGetCurrentContext(); | ||
| CGContextSaveGState(context); | ||
|
|
||
| // For mask view, only the alpha channel is used. | ||
| CGContextSetAlpha(context, 1); | ||
|
|
||
| for (size_t i = 0; i < paths_.size(); i++) { | ||
| CGContextAddPath(context, paths_.at(i)); | ||
| CGContextClip(context); | ||
| } | ||
| CGContextFillRect(context, rect); | ||
| CGContextRestoreGState(context); | ||
| } | ||
|
|
||
| - (void)clipRect:(const SkRect&)clipSkRect matrix:(const CATransform3D&)matrix { | ||
| CGRect clipRect = [self getCGRectFromSkRect:clipSkRect]; | ||
| CGPathRef path = CGPathCreateWithRect(clipRect, nil); | ||
| paths_.push_back([self getTransformedPath:path matrix:matrix]); | ||
| } | ||
|
|
||
| - (void)clipRRect:(const SkRRect&)clipSkRRect { | ||
| - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const CATransform3D&)matrix { | ||
| CGPathRef pathRef = nullptr; | ||
| switch (clipSkRRect.getType()) { | ||
| case SkRRect::kEmpty_Type: { | ||
| break; | ||
| } | ||
| case SkRRect::kRect_Type: { | ||
| [self clipRect:clipSkRRect.rect()]; | ||
| [self clipRect:clipSkRRect.rect() matrix:matrix]; | ||
| return; | ||
| } | ||
| case SkRRect::kOval_Type: | ||
| case SkRRect::kSimple_Type: { | ||
| CGRect clipRect = [ChildClippingView getCGRectFromSkRect:clipSkRRect.rect()]; | ||
| CGRect clipRect = [self getCGRectFromSkRect:clipSkRRect.rect()]; | ||
| pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(), | ||
| clipSkRRect.getSimpleRadii().y(), nil); | ||
| break; | ||
|
|
@@ -129,23 +169,17 @@ - (void)clipRRect:(const SkRRect&)clipSkRRect { | |
| // TODO(cyanglaz): iOS does not seem to support hard edge on CAShapeLayer. It clearly stated that | ||
| // the CAShaperLayer will be drawn antialiased. Need to figure out a way to do the hard edge | ||
| // clipping on iOS. | ||
| CAShapeLayer* clip = [[[CAShapeLayer alloc] init] autorelease]; | ||
| clip.path = pathRef; | ||
| self.layer.mask = clip; | ||
| CGPathRelease(pathRef); | ||
| paths_.push_back([self getTransformedPath:pathRef matrix:matrix]); | ||
| } | ||
|
|
||
| - (void)clipPath:(const SkPath&)path { | ||
| - (void)clipPath:(const SkPath&)path matrix:(const CATransform3D&)matrix { | ||
| if (!path.isValid()) { | ||
| return; | ||
| } | ||
| fml::CFRef<CGMutablePathRef> pathRef(CGPathCreateMutable()); | ||
| if (path.isEmpty()) { | ||
| CAShapeLayer* clip = [[[CAShapeLayer alloc] init] autorelease]; | ||
| clip.path = pathRef; | ||
| self.layer.mask = clip; | ||
| return; | ||
| } | ||
| CGMutablePathRef pathRef = CGPathCreateMutable(); | ||
|
|
||
| // Loop through all verbs and translate them into CGPath | ||
| SkPath::Iter iter(path, true); | ||
|
|
@@ -197,42 +231,20 @@ - (void)clipPath:(const SkPath&)path { | |
| } | ||
| verb = iter.next(pts); | ||
| } | ||
|
|
||
| CAShapeLayer* clip = [[[CAShapeLayer alloc] init] autorelease]; | ||
| clip.path = pathRef; | ||
| self.layer.mask = clip; | ||
| paths_.push_back([self getTransformedPath:pathRef matrix:matrix]); | ||
| } | ||
|
|
||
| - (void)setClip:(flutter::MutatorType)type | ||
| rect:(const SkRect&)rect | ||
| rrect:(const SkRRect&)rrect | ||
| path:(const SkPath&)path { | ||
| FML_CHECK(type == flutter::clip_rect || type == flutter::clip_rrect || | ||
| type == flutter::clip_path); | ||
| switch (type) { | ||
| case flutter::clip_rect: | ||
| [self clipRect:rect]; | ||
| break; | ||
| case flutter::clip_rrect: | ||
| [self clipRRect:rrect]; | ||
| break; | ||
| case flutter::clip_path: | ||
| [self clipPath:path]; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix { | ||
| CGAffineTransform affine = | ||
| CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41, matrix.m42); | ||
| CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine); | ||
| CGPathRelease(path); | ||
| return fml::CFRef<CGPathRef>(transformedPath); | ||
| } | ||
|
|
||
| // The ChildClippingView is as big as the FlutterView, we only want touches to be hit tested and | ||
| // consumed by this view if they are inside the smaller child view. | ||
| - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event { | ||
| for (UIView* view in self.subviews) { | ||
| if ([view pointInside:[self convertPoint:point toView:view] withEvent:event]) { | ||
| return YES; | ||
| } | ||
| } | ||
| return NO; | ||
| - (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect { | ||
| return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft, | ||
| clipSkRect.fBottom - clipSkRect.fTop); | ||
| } | ||
|
|
||
| @end | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gpus_ReturnNotPermittedKillClient crash on iOS 62403 Can you help us have a look,please?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue doesn't seem to related to this PR? |
||
Uh oh!
There was an error while loading. Please reload this page.