diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index c1054c78b9ee6..b8d20e3f1578c 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -126,7 +126,7 @@ void AndroidExternalViewEmbedder::SubmitFrame( slice->end_recording(); - SkRect joined_rect = SkRect::MakeEmpty(); + SkRect full_joined_rect = SkRect::MakeEmpty(); // Determinate if Flutter UI intersects with any of the previous // platform views stacked by z position. @@ -137,6 +137,8 @@ void AndroidExternalViewEmbedder::SubmitFrame( for (ssize_t j = i; j >= 0; j--) { int64_t current_view_id = composition_order_[j]; SkRect current_view_rect = GetViewRect(current_view_id); + // The rect above the `current_view_rect` + SkRect partial_joined_rect = SkRect::MakeEmpty(); // Each rect corresponds to a native view that renders Flutter UI. std::list intersection_rects = slice->searchNonOverlappingDrawnRects(current_view_rect); @@ -146,21 +148,26 @@ void AndroidExternalViewEmbedder::SubmitFrame( // In this case, the rects are merged into a single one that is the union // of all the rects. for (const SkRect& rect : intersection_rects) { - joined_rect.join(rect); + partial_joined_rect.join(rect); } + // Get the intersection rect with the `current_view_rect`, + partial_joined_rect.intersect(current_view_rect); + // Join the `partial_joined_rect` into `full_joined_rect` to get the rect + // above the current `slice` + full_joined_rect.join(partial_joined_rect); } - if (!joined_rect.isEmpty()) { + if (!full_joined_rect.isEmpty()) { // Subpixels in the platform may not align with the canvas subpixels. // // To workaround it, round the floating point bounds and make the rect // slightly larger. // // For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}. - joined_rect.set(joined_rect.roundOut()); - overlay_layers.insert({view_id, joined_rect}); + full_joined_rect.set(full_joined_rect.roundOut()); + overlay_layers.insert({view_id, full_joined_rect}); // Clip the background canvas, so it doesn't contain any of the pixels // drawn on the overlay layer. - background_canvas->clipRect(joined_rect, SkClipOp::kDifference); + background_canvas->clipRect(full_joined_rect, SkClipOp::kDifference); } if (background_builder) { slice->render_into(background_builder); diff --git a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc index b174cd92703f9..f05db8e6e8071 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc @@ -425,7 +425,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { 0, 150, 150, 300, 300, 300, 300, stack1)); // The JNI call to display the overlay surface. EXPECT_CALL(*jni_mock, - FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200)); + FlutterViewDisplayOverlaySurface(0, 150, 150, 100, 100)); auto did_submit_frame = false; auto surface_frame = std::make_unique( @@ -491,7 +491,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { 0, 150, 150, 300, 300, 300, 300, stack1)); // The JNI call to display the overlay surface. EXPECT_CALL(*jni_mock, - FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200)); + FlutterViewDisplayOverlaySurface(0, 150, 150, 100, 100)); auto did_submit_frame = false; auto surface_frame = std::make_unique( @@ -516,6 +516,108 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { } } +TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { + // In this test we will simulate two Android views appearing on the screen + // with a rect intersecting both of them + + auto jni_mock = std::make_shared(); + auto android_context = + std::make_shared(AndroidRenderingAPI::kSoftware); + + auto window = fml::MakeRefCounted(nullptr); + auto gr_context = GrDirectContext::MakeMock(nullptr); + auto frame_size = SkISize::Make(1000, 1000); + SurfaceFrame::FramebufferInfo framebuffer_info; + auto surface_factory = std::make_shared( + [&android_context, gr_context, window, frame_size, framebuffer_info]() { + auto surface_frame_1 = std::make_unique( + SkSurface::MakeNull(1000, 1000), framebuffer_info, + [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { + return true; + }, + /*frame_size=*/SkISize::Make(800, 600)); + + auto surface_mock = std::make_unique(); + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + .Times(1 /* frames */) + .WillOnce(Return(ByMove(std::move(surface_frame_1)))); + + auto android_surface_mock = + std::make_unique(android_context); + EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); + + EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get())) + .WillOnce(Return(ByMove(std::move(surface_mock)))); + + EXPECT_CALL(*android_surface_mock, SetNativeWindow(window)); + return android_surface_mock; + }); + auto embedder = std::make_unique( + *android_context, jni_mock, surface_factory, GetTaskRunnersForFixture()); + + auto raster_thread_merger = GetThreadMergerFromPlatformThread(); + + EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); + embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger); + + { + // Add first Android view. + SkMatrix matrix = SkMatrix::Translate(100, 100); + MutatorsStack stack; + embedder->PrerollCompositeEmbeddedView( + 0, std::make_unique(matrix, SkSize::Make(100, 100), + stack)); + // The JNI call to display the Android view. + EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView( + 0, 100, 100, 100, 100, 150, 150, stack)); + } + + { + // Add second Android view. + SkMatrix matrix = SkMatrix::Translate(300, 100); + MutatorsStack stack; + embedder->PrerollCompositeEmbeddedView( + 1, std::make_unique(matrix, SkSize::Make(100, 100), + stack)); + // The JNI call to display the Android view. + EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView( + 1, 300, 100, 100, 100, 150, 150, stack)); + } + auto rect_paint = SkPaint(); + rect_paint.setColor(SkColors::kCyan); + rect_paint.setStyle(SkPaint::Style::kFill_Style); + + // This simulates Flutter UI that intersects with the two Android views. + // Since we will compute the intersection for each android view in turn, and + // finally merge The final size of the overlay will be smaller than the + // width and height of the rect. + embedder->CompositeEmbeddedView(1).canvas->drawRect( + SkRect::MakeXYWH(150, 50, 200, 200), rect_paint); + + EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()) + .WillRepeatedly([&]() { + return std::make_unique( + 1, window); + }); + + // The JNI call to display the overlay surface. + EXPECT_CALL(*jni_mock, + FlutterViewDisplayOverlaySurface(1, 150, 100, 200, 100)) + .Times(1); + + auto surface_frame = std::make_unique( + SkSurface::MakeNull(1000, 1000), framebuffer_info, + [](const SurfaceFrame& surface_frame, SkCanvas* canvas) mutable { + return true; + }, + /*frame_size=*/SkISize::Make(800, 600)); + + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + + EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); + embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); +} + TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { auto jni_mock = std::make_shared(); auto android_context = @@ -776,7 +878,7 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200, 300, 300, stack1)); EXPECT_CALL(*jni_mock, - FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200)); + FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150)); SurfaceFrame::FramebufferInfo framebuffer_info; auto surface_frame = std::make_unique( @@ -865,7 +967,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200, 300, 300, stack1)); EXPECT_CALL(*jni_mock, - FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200)); + FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150)); auto surface_frame = std::make_unique( SkSurface::MakeNull(1000, 1000), framebuffer_info,