Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions display_list/geometry/dl_rtree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -201,18 +201,30 @@ void DlRTree::search(const Node& parent,
}
}

const DlRegion& DlRTree::region() const {
const DlRegion& DlRTree::region(bool useRoundIn) const {
if (!region_) {
std::vector<SkIRect> rects;
rects.resize(leaf_count_);
for (int i = 0; i < leaf_count_; i++) {
nodes_[i].bounds.roundOut(&rects[i]);
if (useRoundIn) {
nodes_[i].bounds.roundIn(&rects[i]);
} else {
nodes_[i].bounds.roundOut(&rects[i]);
}
}
region_.emplace(rects);
}
return *region_;
}

const DlRegion& DlRTree::region() const {
return region(/*useRoundIn=*/false);
}

const DlRegion& DlRTree::roundedInRegion() const {
return region(/*useRoundIn=*/true);
}

const SkRect& DlRTree::bounds() const {
if (!nodes_.empty()) {
return nodes_.back().bounds;
Expand Down
6 changes: 5 additions & 1 deletion display_list/geometry/dl_rtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,11 @@ class DlRTree : public SkRefCnt {
bool deband = true) const;

/// Returns DlRegion that represents the union of all rectangles in the
/// R-Tree.
/// R-Tree. Each rectangle is rounded out.
const DlRegion& region() const;
/// Returns DlRegion that represents the union of all rectangles in the
/// R-Tree. Each rectangle is rounded in.
const DlRegion& roundedInRegion() const;

/// Returns DlRegion that represents the union of all rectangles in the
/// R-Tree intersected with the query rect.
Expand All @@ -147,6 +150,7 @@ class DlRTree : public SkRefCnt {
int leaf_count_ = 0;
int invalid_id_;
mutable std::optional<DlRegion> region_;
const DlRegion& region(bool useRoundIn) const;
};

} // namespace flutter
Expand Down
4 changes: 4 additions & 0 deletions flow/embedded_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const DlRegion& DisplayListEmbedderViewSlice::getRegion() const {
return display_list_->rtree()->region();
}

const DlRegion& DisplayListEmbedderViewSlice::getRoundedInRegion() const {
return display_list_->rtree()->roundedInRegion();
}

void DisplayListEmbedderViewSlice::render_into(DlCanvas* canvas) {
canvas->DrawDisplayList(display_list_);
}
Expand Down
21 changes: 14 additions & 7 deletions flow/embedded_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,13 @@ class EmbedderViewSlice {
virtual ~EmbedderViewSlice() = default;
virtual DlCanvas* canvas() = 0;
virtual void end_recording() = 0;
// TODO(hellohuanlin): deprecate `getRegion` and migrate to
// `getRoundedInRegion`. Then we should rename `getRoundedInRegion` to just
// `getRegion`.
virtual const DlRegion& getRegion() const = 0;
// TODO(hellohuanlin): iOS only for now. Try on other platforms.
virtual const DlRegion& getRoundedInRegion() const = 0;

// TODO(hellohuanlin): We should deprecate this function if we migrate
// all platforms to use `roundedInRegion`. Then we should rename
// `roundedInRegion` to just `region`.
Expand All @@ -355,13 +361,13 @@ class EmbedderViewSlice {
// result in an intersection region of 1 px height, which is then used to
// create an overlay layer. For each overlay, we acquire a surface frame,
// paint the pixels and submit the frame. This resulted in performance
// issues since the surface frame acquisition is expensive. Since slice
// regions are already rounded out (see:
// https://github.com/flutter/engine/blob/5f40c9f49f88729bc3e71390356209dbe29ec788/display_list/geometry/dl_rtree.cc#L209),
// we can simply round in the queried rect to avoid the situation.
// After rounding in, it will ignore a single (or partial) pixel overlap,
// and give the ownership to the platform view.
return DlRegion::MakeIntersection(getRegion(), DlRegion(query.roundIn()));
// issues since the surface frame acquisition is expensive.
// We round in the layers and round out the platform view, rather than the
// opposite, so that the edge pixel overlay is guaranteed to be displayed
// on top of the platform view. After rounding in, layers will give the
// ownership of a single (or partial) pixel on the edge to platform views.
return DlRegion::MakeIntersection(getRoundedInRegion(),
DlRegion(query.roundOut()));
}

virtual void render_into(DlCanvas* canvas) = 0;
Expand All @@ -375,6 +381,7 @@ class DisplayListEmbedderViewSlice : public EmbedderViewSlice {
DlCanvas* canvas() override;
void end_recording() override;
const DlRegion& getRegion() const override;
const DlRegion& getRoundedInRegion() const override;

void render_into(DlCanvas* canvas) override;
void dispatch(DlOpReceiver& receiver);
Expand Down
5 changes: 4 additions & 1 deletion testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ - (BOOL)application:(UIApplication*)application
@"platform_view_one_overlay_two_intersecting_overlays",
@"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays",
@"--platform-view-max-overlays" : @"platform_view_max_overlays",
@"--platform-view-surrounding-layers" : @"platform_view_surrounding_layers",
@"--platform-view-surrounding-layers-fractional-coordinate" :
@"platform_view_surrounding_layers_fractional_coordinate",
@"--platform-view-partial-intersection-fractional-coordinate" :
@"platform_view_partial_intersection_fractional_coordinate",
@"--platform-view-multiple" : @"platform_view_multiple",
@"--platform-view-multiple-background-foreground" :
@"platform_view_multiple_background_foreground",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,9 @@ - (void)testPlatformViewsMaxOverlays {
// +---+----+---+
// | D |
// +----+
- (void)testPlatformViewsWithAdjacentSurroundingLayers {
- (void)testPlatformViewsWithAdjacentSurroundingLayersAndFractionalCoordinate {
XCUIApplication* app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"--platform-view-surrounding-layers" ];
app.launchArguments = @[ @"--platform-view-surrounding-layers-fractional-coordinate" ];
[app launch];

XCUIElement* platform_view = app.otherElements[@"platform_view[0]"];
Expand All @@ -331,4 +331,36 @@ - (void)testPlatformViewsWithAdjacentSurroundingLayers {
XCTAssertFalse(overlay.exists);
}

// Platform view partially intersect with a layer in fractional coordinate.
// +-------+
// | |
// | PV +--+--+
// | | |
// +----+ A |
// | |
// +-----+
- (void)testPlatformViewsWithPartialIntersectionAndFractionalCoordinate {
XCUIApplication* app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"--platform-view-partial-intersection-fractional-coordinate" ];
[app launch];

XCUIElement* platform_view = app.otherElements[@"platform_view[0]"];
XCTAssertTrue([platform_view waitForExistenceWithTimeout:1.0]);

CGFloat scale = [UIScreen mainScreen].scale;
XCTAssertEqual(platform_view.frame.origin.x * scale, 0.5);
XCTAssertEqual(platform_view.frame.origin.y * scale, 0.5);
XCTAssertEqual(platform_view.frame.size.width * scale, 100);
XCTAssertEqual(platform_view.frame.size.height * scale, 100);

XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"];
XCTAssert(overlay.exists);

// 51 since layer is rounded in.
XCTAssertEqual(CGRectGetMinX(overlay.frame) * scale, 51);
XCTAssertEqual(CGRectGetMinY(overlay.frame) * scale, 51);
// 101 since platform view is rounded out.
XCTAssertEqual(CGRectGetMaxX(overlay.frame) * scale, 101);
XCTAssertEqual(CGRectGetMaxY(overlay.frame) * scale, 101);
}
@end
53 changes: 51 additions & 2 deletions testing/scenario_app/lib/src/platform_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,10 @@ class PlatformViewMaxOverlaysScenario extends Scenario
}

/// A platform view with adjacent surrounding layers should not create overlays.
class PlatformViewSurroundingLayersScenario extends Scenario
class PlatformViewSurroundingLayersFractionalCoordinateScenario extends Scenario
with _BasePlatformViewScenarioMixin {
/// Creates the PlatformView scenario.
PlatformViewSurroundingLayersScenario(
PlatformViewSurroundingLayersFractionalCoordinateScenario(
super.view, {
required this.id,
});
Expand Down Expand Up @@ -438,6 +438,55 @@ class PlatformViewSurroundingLayersScenario extends Scenario
}
}

/// A platform view partially intersect with a layer, both with fractional coordinates.
class PlatformViewPartialIntersectionFractionalCoordinateScenario extends Scenario
with _BasePlatformViewScenarioMixin {
/// Creates the PlatformView scenario.
PlatformViewPartialIntersectionFractionalCoordinateScenario(
super.view, {
required this.id,
});

/// The platform view identifier.
final int id;

@override
void onBeginFrame(Duration duration) {
final SceneBuilder builder = SceneBuilder();

// Simulate partial pixel offsets as we would see while scrolling.
// All objects in the scene below are then on sub-pixel boundaries.
builder.pushOffset(0.5, 0.5);

// a platform view from (0, 0) to (100, 100)
addPlatformView(
id,
width: 100,
height: 100,
dispatcher: view.platformDispatcher,
sceneBuilder: builder,
);

final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);

canvas.drawRect(
Rect.fromLTWH(50, 50, 100, 100),
Paint()..color = const Color(0x22FF0000),
);

final Picture picture = recorder.endRecording();
builder.addPicture(Offset.zero, picture);

// Pop the (0.5, 0.5) offset.
builder.pop();

final Scene scene = builder.build();
view.render(scene);
scene.dispose();
}
}

/// Builds a scene with 2 platform views.
class MultiPlatformViewScenario extends Scenario
with _BasePlatformViewScenarioMixin {
Expand Down
3 changes: 2 additions & 1 deletion testing/scenario_app/lib/src/scenarios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ Map<String, _ScenarioFactory> _scenarios = <String, _ScenarioFactory>{
'platform_view_one_overlay_two_intersecting_overlays': (FlutterView view) => PlatformViewOneOverlayTwoIntersectingOverlaysScenario(view, id: _viewId++),
'platform_view_multiple_without_overlays': (FlutterView view) => MultiPlatformViewWithoutOverlaysScenario(view, firstId: _viewId++, secondId: _viewId++),
'platform_view_max_overlays': (FlutterView view) => PlatformViewMaxOverlaysScenario(view, id: _viewId++),
'platform_view_surrounding_layers': (FlutterView view) => PlatformViewSurroundingLayersScenario(view, id: _viewId++),
'platform_view_surrounding_layers_fractional_coordinate': (FlutterView view) => PlatformViewSurroundingLayersFractionalCoordinateScenario(view, id: _viewId++),
'platform_view_partial_intersection_fractional_coordinate': (FlutterView view) => PlatformViewPartialIntersectionFractionalCoordinateScenario(view, id: _viewId++),
'platform_view_cliprect': (FlutterView view) => PlatformViewClipRectScenario(view, id: _viewId++),
'platform_view_cliprect_with_transform': (FlutterView view) => PlatformViewClipRectWithTransformScenario(view, id: _viewId++),
'platform_view_cliprect_after_moved': (FlutterView view) => PlatformViewClipRectAfterMovedScenario(view, id: _viewId++),
Expand Down