Skip to content

Commit 1c80ab6

Browse files
authored
[CP-stable] fix iOS platform view clipping path intersection (flutter#55007)
flutter#54820 on stable branch. That PR was using some of the symbol changes in flutter#54335, so I had so switch some back: flutter@e0a630d Cherry-pick request: flutter/flutter#154712 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent abf3ef7 commit 1c80ab6

File tree

21 files changed

+1476
-61
lines changed

21 files changed

+1476
-61
lines changed

shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,100 @@ - (void)testClipRect {
17871787
}
17881788
}
17891789

1790+
- (void)testClipRect_multipleClips {
1791+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1792+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1793+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1794+
/*platform=*/thread_task_runner,
1795+
/*raster=*/thread_task_runner,
1796+
/*ui=*/thread_task_runner,
1797+
/*io=*/thread_task_runner);
1798+
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1799+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1800+
/*delegate=*/mock_delegate,
1801+
/*rendering_api=*/mock_delegate.settings_.enable_impeller
1802+
? flutter::IOSRenderingAPI::kMetal
1803+
: flutter::IOSRenderingAPI::kSoftware,
1804+
/*platform_views_controller=*/flutterPlatformViewsController,
1805+
/*task_runners=*/runners,
1806+
/*worker_task_runner=*/nil,
1807+
/*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1808+
1809+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1810+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1811+
flutterPlatformViewsController->RegisterViewFactory(
1812+
factory, @"MockFlutterPlatformView",
1813+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
1814+
FlutterResult result = ^(id result) {
1815+
};
1816+
flutterPlatformViewsController->OnMethodCall(
1817+
[FlutterMethodCall
1818+
methodCallWithMethodName:@"create"
1819+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1820+
result);
1821+
1822+
XCTAssertNotNil(gMockPlatformView);
1823+
1824+
UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1825+
flutterPlatformViewsController->SetFlutterView(flutterView);
1826+
// Create embedded view params
1827+
flutter::MutatorsStack stack;
1828+
// Layer tree always pushes a screen scale factor to the stack
1829+
SkMatrix screenScaleMatrix =
1830+
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1831+
stack.PushTransform(screenScaleMatrix);
1832+
// Push a clip rect
1833+
SkRect rect1 = SkRect::MakeXYWH(2, 2, 3, 3);
1834+
stack.PushClipRect(rect1);
1835+
// Push another clip rect
1836+
SkRect rect2 = SkRect::MakeXYWH(3, 3, 3, 3);
1837+
stack.PushClipRect(rect2);
1838+
1839+
auto embeddedViewParams =
1840+
std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1841+
1842+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1843+
flutterPlatformViewsController->CompositeEmbeddedView(2);
1844+
1845+
gMockPlatformView.backgroundColor = UIColor.redColor;
1846+
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1847+
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1848+
[flutterView addSubview:childClippingView];
1849+
1850+
[flutterView setNeedsLayout];
1851+
[flutterView layoutIfNeeded];
1852+
1853+
/*
1854+
clip 1 clip 2
1855+
2 3 4 5 6 2 3 4 5 6
1856+
2 + - - + 2
1857+
3 | | 3 + - - +
1858+
4 | | 4 | |
1859+
5 + - - + 5 | |
1860+
6 6 + - - +
1861+
1862+
Result should be the intersection of 2 clips
1863+
2 3 4 5 6
1864+
2
1865+
3 + - +
1866+
4 | |
1867+
5 + - +
1868+
6
1869+
*/
1870+
CGRect insideClipping = CGRectMake(3, 3, 2, 2);
1871+
for (int i = 0; i < 10; i++) {
1872+
for (int j = 0; j < 10; j++) {
1873+
CGPoint point = CGPointMake(i, j);
1874+
int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
1875+
if (CGRectContainsPoint(insideClipping, point)) {
1876+
XCTAssertEqual(alpha, 255);
1877+
} else {
1878+
XCTAssertEqual(alpha, 0);
1879+
}
1880+
}
1881+
}
1882+
}
1883+
17901884
- (void)testClipRRect {
17911885
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
17921886
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
@@ -1887,6 +1981,124 @@ - (void)testClipRRect {
18871981
}
18881982
}
18891983

1984+
- (void)testClipRRect_multipleClips {
1985+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1986+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1987+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1988+
/*platform=*/thread_task_runner,
1989+
/*raster=*/thread_task_runner,
1990+
/*ui=*/thread_task_runner,
1991+
/*io=*/thread_task_runner);
1992+
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1993+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1994+
/*delegate=*/mock_delegate,
1995+
/*rendering_api=*/mock_delegate.settings_.enable_impeller
1996+
? flutter::IOSRenderingAPI::kMetal
1997+
: flutter::IOSRenderingAPI::kSoftware,
1998+
/*platform_views_controller=*/flutterPlatformViewsController,
1999+
/*task_runners=*/runners,
2000+
/*worker_task_runner=*/nil,
2001+
/*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2002+
2003+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2004+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2005+
flutterPlatformViewsController->RegisterViewFactory(
2006+
factory, @"MockFlutterPlatformView",
2007+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2008+
FlutterResult result = ^(id result) {
2009+
};
2010+
flutterPlatformViewsController->OnMethodCall(
2011+
[FlutterMethodCall
2012+
methodCallWithMethodName:@"create"
2013+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2014+
result);
2015+
2016+
XCTAssertNotNil(gMockPlatformView);
2017+
2018+
UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2019+
flutterPlatformViewsController->SetFlutterView(flutterView);
2020+
// Create embedded view params
2021+
flutter::MutatorsStack stack;
2022+
// Layer tree always pushes a screen scale factor to the stack
2023+
SkMatrix screenScaleMatrix =
2024+
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2025+
stack.PushTransform(screenScaleMatrix);
2026+
// Push a clip rrect
2027+
SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2028+
stack.PushClipRRect(rrect);
2029+
// Push a clip rect
2030+
SkRect rect = SkRect::MakeXYWH(4, 2, 6, 6);
2031+
stack.PushClipRect(rect);
2032+
2033+
auto embeddedViewParams =
2034+
std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
2035+
2036+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2037+
flutterPlatformViewsController->CompositeEmbeddedView(2);
2038+
2039+
gMockPlatformView.backgroundColor = UIColor.redColor;
2040+
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2041+
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2042+
[flutterView addSubview:childClippingView];
2043+
2044+
[flutterView setNeedsLayout];
2045+
[flutterView layoutIfNeeded];
2046+
2047+
/*
2048+
clip 1 clip 2
2049+
2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9
2050+
2 / - - - - \ 2 + - - - - +
2051+
3 | | 3 | |
2052+
4 | | 4 | |
2053+
5 | | 5 | |
2054+
6 | | 6 | |
2055+
7 \ - - - - / 7 + - - - - +
2056+
2057+
Result should be the intersection of 2 clips
2058+
2 3 4 5 6 7 8 9
2059+
2 + - - \
2060+
3 | |
2061+
4 | |
2062+
5 | |
2063+
6 | |
2064+
7 + - - /
2065+
*/
2066+
CGRect clipping = CGRectMake(4, 2, 4, 6);
2067+
for (int i = 0; i < 10; i++) {
2068+
for (int j = 0; j < 10; j++) {
2069+
CGPoint point = CGPointMake(i, j);
2070+
int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2071+
if (i == 7 && (j == 2 || j == 7)) {
2072+
// Upper and lower right corners should be partially transparent.
2073+
XCTAssert(0 < alpha && alpha < 255);
2074+
} else if (
2075+
// left
2076+
(i == 4 && j >= 2 && j <= 7) ||
2077+
// right
2078+
(i == 7 && j >= 2 && j <= 7) ||
2079+
// top
2080+
(j == 2 && i >= 4 && i <= 7) ||
2081+
// bottom
2082+
(j == 7 && i >= 4 && i <= 7)) {
2083+
// Since we are falling back to software rendering for this case
2084+
// The edge pixels can be anti-aliased, so it may not be fully opaque.
2085+
XCTAssert(alpha > 127);
2086+
} else if ((i == 3 && j >= 1 && j <= 8) || (i == 8 && j >= 1 && j <= 8) ||
2087+
(j == 1 && i >= 3 && i <= 8) || (j == 8 && i >= 3 && i <= 8)) {
2088+
// Since we are falling back to software rendering for this case
2089+
// The edge pixels can be anti-aliased, so it may not be fully transparent.
2090+
XCTAssert(alpha < 127);
2091+
} else if (CGRectContainsPoint(clipping, point)) {
2092+
// Other pixels inside clipping should be fully opaque.
2093+
XCTAssertEqual(alpha, 255);
2094+
} else {
2095+
// Pixels outside clipping should be fully transparent.
2096+
XCTAssertEqual(alpha, 0);
2097+
}
2098+
}
2099+
}
2100+
}
2101+
18902102
- (void)testClipPath {
18912103
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
18922104
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
@@ -1988,6 +2200,125 @@ - (void)testClipPath {
19882200
}
19892201
}
19902202

2203+
- (void)testClipPath_multipleClips {
2204+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2205+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2206+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2207+
/*platform=*/thread_task_runner,
2208+
/*raster=*/thread_task_runner,
2209+
/*ui=*/thread_task_runner,
2210+
/*io=*/thread_task_runner);
2211+
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2212+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2213+
/*delegate=*/mock_delegate,
2214+
/*rendering_api=*/mock_delegate.settings_.enable_impeller
2215+
? flutter::IOSRenderingAPI::kMetal
2216+
: flutter::IOSRenderingAPI::kSoftware,
2217+
/*platform_views_controller=*/flutterPlatformViewsController,
2218+
/*task_runners=*/runners,
2219+
/*worker_task_runner=*/nil,
2220+
/*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2221+
2222+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2223+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2224+
flutterPlatformViewsController->RegisterViewFactory(
2225+
factory, @"MockFlutterPlatformView",
2226+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2227+
FlutterResult result = ^(id result) {
2228+
};
2229+
flutterPlatformViewsController->OnMethodCall(
2230+
[FlutterMethodCall
2231+
methodCallWithMethodName:@"create"
2232+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2233+
result);
2234+
2235+
XCTAssertNotNil(gMockPlatformView);
2236+
2237+
UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2238+
flutterPlatformViewsController->SetFlutterView(flutterView);
2239+
// Create embedded view params
2240+
flutter::MutatorsStack stack;
2241+
// Layer tree always pushes a screen scale factor to the stack
2242+
SkMatrix screenScaleMatrix =
2243+
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2244+
stack.PushTransform(screenScaleMatrix);
2245+
// Push a clip path
2246+
SkPath path;
2247+
path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2248+
stack.PushClipPath(path);
2249+
// Push a clip rect
2250+
SkRect rect = SkRect::MakeXYWH(4, 2, 6, 6);
2251+
stack.PushClipRect(rect);
2252+
2253+
auto embeddedViewParams =
2254+
std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
2255+
2256+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2257+
flutterPlatformViewsController->CompositeEmbeddedView(2);
2258+
2259+
gMockPlatformView.backgroundColor = UIColor.redColor;
2260+
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2261+
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2262+
[flutterView addSubview:childClippingView];
2263+
2264+
[flutterView setNeedsLayout];
2265+
[flutterView layoutIfNeeded];
2266+
2267+
/*
2268+
clip 1 clip 2
2269+
2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9
2270+
2 / - - - - \ 2 + - - - - +
2271+
3 | | 3 | |
2272+
4 | | 4 | |
2273+
5 | | 5 | |
2274+
6 | | 6 | |
2275+
7 \ - - - - / 7 + - - - - +
2276+
2277+
Result should be the intersection of 2 clips
2278+
2 3 4 5 6 7 8 9
2279+
2 + - - \
2280+
3 | |
2281+
4 | |
2282+
5 | |
2283+
6 | |
2284+
7 + - - /
2285+
*/
2286+
CGRect clipping = CGRectMake(4, 2, 4, 6);
2287+
for (int i = 0; i < 10; i++) {
2288+
for (int j = 0; j < 10; j++) {
2289+
CGPoint point = CGPointMake(i, j);
2290+
int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2291+
if (i == 7 && (j == 2 || j == 7)) {
2292+
// Upper and lower right corners should be partially transparent.
2293+
XCTAssert(0 < alpha && alpha < 255);
2294+
} else if (
2295+
// left
2296+
(i == 4 && j >= 2 && j <= 7) ||
2297+
// right
2298+
(i == 7 && j >= 2 && j <= 7) ||
2299+
// top
2300+
(j == 2 && i >= 4 && i <= 7) ||
2301+
// bottom
2302+
(j == 7 && i >= 4 && i <= 7)) {
2303+
// Since we are falling back to software rendering for this case
2304+
// The edge pixels can be anti-aliased, so it may not be fully opaque.
2305+
XCTAssert(alpha > 127);
2306+
} else if ((i == 3 && j >= 1 && j <= 8) || (i == 8 && j >= 1 && j <= 8) ||
2307+
(j == 1 && i >= 3 && i <= 8) || (j == 8 && i >= 3 && i <= 8)) {
2308+
// Since we are falling back to software rendering for this case
2309+
// The edge pixels can be anti-aliased, so it may not be fully transparent.
2310+
XCTAssert(alpha < 127);
2311+
} else if (CGRectContainsPoint(clipping, point)) {
2312+
// Other pixels inside clipping should be fully opaque.
2313+
XCTAssertEqual(alpha, 255);
2314+
} else {
2315+
// Pixels outside clipping should be fully transparent.
2316+
XCTAssertEqual(alpha, 0);
2317+
}
2318+
}
2319+
}
2320+
}
2321+
19912322
- (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
19922323
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
19932324
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");

0 commit comments

Comments
 (0)