-
Notifications
You must be signed in to change notification settings - Fork 6k
PlatformView partial blur #36015
PlatformView partial blur #36015
Conversation
c1e3f4e to
4445f86
Compare
b060125 to
a0fc0b9
Compare
LongCatIsLooong
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So behavior-wise, this only changes the Rect of the blur?
| // We only support DlBlurImageFilter for BackdropFilter. | ||
| if ((*iter)->GetFilter().asBlur() && canApplyBlurBackdrop) { | ||
| // Only support DlBlurImageFilter for BackdropFilter. | ||
| if ((*iter)->GetFilterMutation().GetFilter().asBlur() && canApplyBlurBackdrop) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to stop pushing the mutation if it's not supported?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be an optimization in the future. Pushing is done in the shared code across all platforms, to stop pushing we would have to create a way for the iOS PlatformView communicate back to the layer tree.
| CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x(); | ||
| CGRect filterRect = | ||
| flutter::GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect()); | ||
| // `filterRect` reprents the rect that shouuld be filtered inside the `flutter_view_`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: typo, should
| // The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be | ||
| // filtered. | ||
| CGRect intersection = CGRectIntersection(filterRect, clipView.frame); | ||
| if (CGRectContainsRect(clipView.frame, intersection)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: CGRectIsNull(_:) instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like CGRectContainsRect is more readable. Also, we would have to check both [CGRectIsNull(_:)](https://developer.apple.com/documentation/coregraphics/1455471-cgrectisnull) and CGRectIsEmpty if going the other route.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://developer.apple.com/documentation/coregraphics/1455346-cgrectintersection says use CGRectIsNull to check if the two rects don't overlap.
|
|
||
| - (instancetype)initWithFrame:(CGRect)frame | ||
| blurRadius:(CGFloat)blurRadius | ||
| visualEffectView:(UIVisualEffectView*)visualEffectView { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could copy an existing filter instead of instantiating a new UIVisualEffectView and finding the filter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we need a new UIVisualEffectView instance to blur anyway. The problem comes down to these 2 approaches:
1, We store a gaussianFilter Object after first time, and copy it later when needed. (I think this is what you meant)
2. We get the gaussianFilter for the new UIVisualEffectView instance.
Option 1 takes an addition space to store the gaussianFilter
Option 2 takes an addition loop ( for (NSObject* filter in view.layer.filters)) to find the filter, which is currently looping through 2 filters.
I feel like both are similar, maybe Option 2 is less future proof if more filters are added to the layer in some future SDK?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also you'd have to allocate space for a UIVisualEffectView and run its initializer every time. But probably not a big deal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the code to cache a gaussianFilter object and eliminated code to find the views, filters etc, PTAL.
| const SkRect& GetFilterRect() const { return filter_rect_; } | ||
|
|
||
| bool operator==(const ImageFilterMutation& other) const { | ||
| return *filter_ == *other.filter_ && filter_rect_ == other.filter_rect_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comparing pointer equality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is comparing the data. I added a test for it in my latest commit
| // The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be | ||
| // filtered. | ||
| CGRect intersection = CGRectIntersection(filterRect, clipView.frame); | ||
| if (CGRectContainsRect(clipView.frame, intersection)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://developer.apple.com/documentation/coregraphics/1455346-cgrectintersection says use CGRectIsNull to check if the two rects don't overlap.
|
|
||
| - (instancetype)initWithFrame:(CGRect)frame | ||
| blurRadius:(CGFloat)blurRadius | ||
| visualEffectView:(UIVisualEffectView*)visualEffectView { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also you'd have to allocate space for a UIVisualEffectView and run its initializer every time. But probably not a big deal.
| if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"] && | ||
| [[filter valueForKey:@"inputRadius"] isKindOfClass:[NSNumber class]]) { | ||
| GaussianBlurFilter = filter; | ||
| IsUIVisualEffectViewImplementationValid = YES; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you still need this? You can check if the index < 0 I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you were somehow looking at an old version of the code.
| return; | ||
| } | ||
| NSUInteger index = 0; | ||
| for (UIView* view in visualEffectView.subviews) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if you need the index, use for(int i; i < length; i++) instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought fast enumeration (for x in y) is faster? Probably doesn't really matter in this case because the number of subviews won't be too big. I will take the suggestion to avoid future confusion since the performance difference will be very insignificant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The enumeration is considerably more efficient than, for example, using NSEnumerator directly.
It's faster than NSEnumerator. I think with NSArray you have random access so it probably won't be slower than for(x in y).
| } | ||
|
|
||
| + (void)prepareIfNecessary:(UIVisualEffectView*)visualEffectView { | ||
| if (GaussianBlurFilter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is GaussianBlurFilter for? It's seems it's not referenced anywhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you were somehow looking at an old version of the code.
This reverts commit abc3aab.
…114178) * c33eae133 Macos re-enable TestSetMenu (flutter/engine#37058) * abc3aabad PlatformView partial blur (flutter/engine#36015) * 8fcf413f8 Revert "PlatformView partial blur" (flutter/engine#37085) * 8bb0441e5 Revert "Fixes Android text field to use hint text for accessibility (#36846)" (flutter/engine#37083)
This PR makes the PlatformView able to be partially blurred based on the cullrect.
This PR modifies the UIVisualEffectView's filters so that it uses the correct inputRadius from the backdropfilter widget.
The UIVisualEffectView is then set to the correct frame based on
cullrect, added to the PlatformView as a subview.Fixes: flutter/flutter#50183, flutter/flutter#113508
Things to do in this PR:
1. Unit Tests2. Scenario Tests3. Manual submission tests since some private API is accessed.4. Design doc update.Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.