Skip to content

Commit ea8fdc1

Browse files
Nayuta403Ricardo Amador
authored andcommitted
Reduce the size of Overlay FlutterImageView in HC mode (flutter#38393)
* intersect * add * test fix * fix test * modify * typo * modify * test
1 parent 20fc5ca commit ea8fdc1

File tree

2 files changed

+119
-10
lines changed

2 files changed

+119
-10
lines changed

shell/platform/android/external_view_embedder/external_view_embedder.cc

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void AndroidExternalViewEmbedder::SubmitFrame(
126126

127127
slice->end_recording();
128128

129-
SkRect joined_rect = SkRect::MakeEmpty();
129+
SkRect full_joined_rect = SkRect::MakeEmpty();
130130

131131
// Determinate if Flutter UI intersects with any of the previous
132132
// platform views stacked by z position.
@@ -137,6 +137,8 @@ void AndroidExternalViewEmbedder::SubmitFrame(
137137
for (ssize_t j = i; j >= 0; j--) {
138138
int64_t current_view_id = composition_order_[j];
139139
SkRect current_view_rect = GetViewRect(current_view_id);
140+
// The rect above the `current_view_rect`
141+
SkRect partial_joined_rect = SkRect::MakeEmpty();
140142
// Each rect corresponds to a native view that renders Flutter UI.
141143
std::list<SkRect> intersection_rects =
142144
slice->searchNonOverlappingDrawnRects(current_view_rect);
@@ -146,21 +148,26 @@ void AndroidExternalViewEmbedder::SubmitFrame(
146148
// In this case, the rects are merged into a single one that is the union
147149
// of all the rects.
148150
for (const SkRect& rect : intersection_rects) {
149-
joined_rect.join(rect);
151+
partial_joined_rect.join(rect);
150152
}
153+
// Get the intersection rect with the `current_view_rect`,
154+
partial_joined_rect.intersect(current_view_rect);
155+
// Join the `partial_joined_rect` into `full_joined_rect` to get the rect
156+
// above the current `slice`
157+
full_joined_rect.join(partial_joined_rect);
151158
}
152-
if (!joined_rect.isEmpty()) {
159+
if (!full_joined_rect.isEmpty()) {
153160
// Subpixels in the platform may not align with the canvas subpixels.
154161
//
155162
// To workaround it, round the floating point bounds and make the rect
156163
// slightly larger.
157164
//
158165
// For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}.
159-
joined_rect.set(joined_rect.roundOut());
160-
overlay_layers.insert({view_id, joined_rect});
166+
full_joined_rect.set(full_joined_rect.roundOut());
167+
overlay_layers.insert({view_id, full_joined_rect});
161168
// Clip the background canvas, so it doesn't contain any of the pixels
162169
// drawn on the overlay layer.
163-
background_canvas->clipRect(joined_rect, SkClipOp::kDifference);
170+
background_canvas->clipRect(full_joined_rect, SkClipOp::kDifference);
164171
}
165172
if (background_builder) {
166173
slice->render_into(background_builder);

shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) {
425425
0, 150, 150, 300, 300, 300, 300, stack1));
426426
// The JNI call to display the overlay surface.
427427
EXPECT_CALL(*jni_mock,
428-
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
428+
FlutterViewDisplayOverlaySurface(0, 150, 150, 100, 100));
429429

430430
auto did_submit_frame = false;
431431
auto surface_frame = std::make_unique<SurfaceFrame>(
@@ -491,7 +491,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) {
491491
0, 150, 150, 300, 300, 300, 300, stack1));
492492
// The JNI call to display the overlay surface.
493493
EXPECT_CALL(*jni_mock,
494-
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
494+
FlutterViewDisplayOverlaySurface(0, 150, 150, 100, 100));
495495

496496
auto did_submit_frame = false;
497497
auto surface_frame = std::make_unique<SurfaceFrame>(
@@ -516,6 +516,108 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) {
516516
}
517517
}
518518

519+
TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) {
520+
// In this test we will simulate two Android views appearing on the screen
521+
// with a rect intersecting both of them
522+
523+
auto jni_mock = std::make_shared<JNIMock>();
524+
auto android_context =
525+
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
526+
527+
auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
528+
auto gr_context = GrDirectContext::MakeMock(nullptr);
529+
auto frame_size = SkISize::Make(1000, 1000);
530+
SurfaceFrame::FramebufferInfo framebuffer_info;
531+
auto surface_factory = std::make_shared<TestAndroidSurfaceFactory>(
532+
[&android_context, gr_context, window, frame_size, framebuffer_info]() {
533+
auto surface_frame_1 = std::make_unique<SurfaceFrame>(
534+
SkSurface::MakeNull(1000, 1000), framebuffer_info,
535+
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
536+
return true;
537+
},
538+
/*frame_size=*/SkISize::Make(800, 600));
539+
540+
auto surface_mock = std::make_unique<SurfaceMock>();
541+
EXPECT_CALL(*surface_mock, AcquireFrame(frame_size))
542+
.Times(1 /* frames */)
543+
.WillOnce(Return(ByMove(std::move(surface_frame_1))));
544+
545+
auto android_surface_mock =
546+
std::make_unique<AndroidSurfaceMock>(android_context);
547+
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));
548+
549+
EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()))
550+
.WillOnce(Return(ByMove(std::move(surface_mock))));
551+
552+
EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
553+
return android_surface_mock;
554+
});
555+
auto embedder = std::make_unique<AndroidExternalViewEmbedder>(
556+
*android_context, jni_mock, surface_factory, GetTaskRunnersForFixture());
557+
558+
auto raster_thread_merger = GetThreadMergerFromPlatformThread();
559+
560+
EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
561+
embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);
562+
563+
{
564+
// Add first Android view.
565+
SkMatrix matrix = SkMatrix::Translate(100, 100);
566+
MutatorsStack stack;
567+
embedder->PrerollCompositeEmbeddedView(
568+
0, std::make_unique<EmbeddedViewParams>(matrix, SkSize::Make(100, 100),
569+
stack));
570+
// The JNI call to display the Android view.
571+
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(
572+
0, 100, 100, 100, 100, 150, 150, stack));
573+
}
574+
575+
{
576+
// Add second Android view.
577+
SkMatrix matrix = SkMatrix::Translate(300, 100);
578+
MutatorsStack stack;
579+
embedder->PrerollCompositeEmbeddedView(
580+
1, std::make_unique<EmbeddedViewParams>(matrix, SkSize::Make(100, 100),
581+
stack));
582+
// The JNI call to display the Android view.
583+
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(
584+
1, 300, 100, 100, 100, 150, 150, stack));
585+
}
586+
auto rect_paint = SkPaint();
587+
rect_paint.setColor(SkColors::kCyan);
588+
rect_paint.setStyle(SkPaint::Style::kFill_Style);
589+
590+
// This simulates Flutter UI that intersects with the two Android views.
591+
// Since we will compute the intersection for each android view in turn, and
592+
// finally merge The final size of the overlay will be smaller than the
593+
// width and height of the rect.
594+
embedder->CompositeEmbeddedView(1).canvas->drawRect(
595+
SkRect::MakeXYWH(150, 50, 200, 200), rect_paint);
596+
597+
EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
598+
.WillRepeatedly([&]() {
599+
return std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
600+
1, window);
601+
});
602+
603+
// The JNI call to display the overlay surface.
604+
EXPECT_CALL(*jni_mock,
605+
FlutterViewDisplayOverlaySurface(1, 150, 100, 200, 100))
606+
.Times(1);
607+
608+
auto surface_frame = std::make_unique<SurfaceFrame>(
609+
SkSurface::MakeNull(1000, 1000), framebuffer_info,
610+
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) mutable {
611+
return true;
612+
},
613+
/*frame_size=*/SkISize::Make(800, 600));
614+
615+
embedder->SubmitFrame(gr_context.get(), std::move(surface_frame));
616+
617+
EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
618+
embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
619+
}
620+
519621
TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) {
520622
auto jni_mock = std::make_shared<JNIMock>();
521623
auto android_context =
@@ -776,7 +878,7 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) {
776878
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200,
777879
300, 300, stack1));
778880
EXPECT_CALL(*jni_mock,
779-
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
881+
FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150));
780882

781883
SurfaceFrame::FramebufferInfo framebuffer_info;
782884
auto surface_frame = std::make_unique<SurfaceFrame>(
@@ -865,7 +967,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) {
865967
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200,
866968
300, 300, stack1));
867969
EXPECT_CALL(*jni_mock,
868-
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
970+
FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150));
869971

870972
auto surface_frame = std::make_unique<SurfaceFrame>(
871973
SkSurface::MakeNull(1000, 1000), framebuffer_info,

0 commit comments

Comments
 (0)