diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fe19287614df0..33569a6ced33a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -503,6 +503,7 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/fixtures/shell_test.dart +FILE: ../../../flutter/shell/common/fixtures/shelltest_screenshot.png FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f93bca63478f0..9d57ee5ff5ecf 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -153,6 +153,8 @@ if (current_toolchain == host_toolchain) { test_fixtures("shell_unittests_fixtures") { dart_main = "fixtures/shell_test.dart" + + fixtures = [ "fixtures/shelltest_screenshot.png" ] } shell_host_executable("shell_unittests") { diff --git a/shell/common/fixtures/shelltest_screenshot.png b/shell/common/fixtures/shelltest_screenshot.png new file mode 100644 index 0000000000000..3ad7d7e01512a Binary files /dev/null and b/shell/common/fixtures/shelltest_screenshot.png differ diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d61e1ff86ddc3..f40be319848bb 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -399,9 +399,14 @@ static sk_sp ScreenshotLayerTreeAsImage( SkMatrix root_surface_transformation; root_surface_transformation.reset(); - auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr, - root_surface_transformation, - false, nullptr); + // We want to ensure we call the base method for + // CompositorContext::AcquireFrame instead of the platform-specific method. + // Specifically, Fuchsia's CompositorContext handles the rendering surface + // itself which means that we will still continue to render to the onscreen + // surface if we don't call the base method. + auto frame = compositor_context.flutter::CompositorContext::AcquireFrame( + surface_context, canvas, nullptr, root_surface_transformation, false, + nullptr); canvas->clear(SK_ColorTRANSPARENT); frame->Raster(*tree, true); canvas->flush(); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 0146619cce4cf..b69bf226b5c61 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -10,6 +10,7 @@ #include #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/layers/picture_layer.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/fml/command_line.h" #include "flutter/fml/make_copyable.h" @@ -952,5 +953,66 @@ TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) { DestroyShell(std::move(shell), std::move(task_runners)); } +TEST_F(ShellTest, Screenshot) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent firstFrameLatch; + settings.frame_rasterized_callback = + [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); }; + + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + + LayerTreeBuilder builder = [&](std::shared_ptr root) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = + recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80)); + recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80), + SkPaint(SkColor4f::FromColor(SK_ColorRED))); + auto sk_picture = recorder.finishRecordingAsPicture(); + fml::RefPtr queue = fml::MakeRefCounted( + this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(10, 10), + flutter::SkiaGPUObject({sk_picture, queue}), false, false); + root->Add(picture_layer); + }; + + PumpOneFrame(shell.get(), 100, 100, builder); + firstFrameLatch.Wait(); + + std::promise screenshot_promise; + auto screenshot_future = screenshot_promise.get_future(); + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetGPUTaskRunner(), + [&screenshot_promise, &shell]() { + auto rasterizer = shell->GetRasterizer(); + screenshot_promise.set_value(rasterizer->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::CompressedImage, false)); + }); + + auto fixtures_dir = + fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); + + auto reference_png = fml::FileMapping::CreateReadOnly( + fixtures_dir, "shelltest_screenshot.png"); + + // Use MakeWithoutCopy instead of MakeWithCString because we don't want to + // encode the null sentinel + sk_sp reference_data = SkData::MakeWithoutCopy( + reference_png->GetMapping(), reference_png->GetSize()); + + ASSERT_TRUE(reference_data->equals(screenshot_future.get().data.get())); + + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter