From 0a3c0b5ecf4ebead950f190b99d7d0819e24c3c2 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 29 Nov 2016 14:39:29 -0800 Subject: [PATCH] Allow platform frameworks to snapshot FlutterView contents. (#3281) --- shell/platform/darwin/ios/BUILD.gn | 1 + .../ios/framework/Source/FlutterView.mm | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 182636adc..974f8c3dc 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -53,6 +53,7 @@ shared_library("flutter_framework_dylib") { deps = [ "//base:base", "//dart/runtime:libdart", + "//flutter/flow", "//flutter/glue", "//flutter/lib/ui", "//flutter/shell/common", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 0bab07bf1..d08199fcf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -3,6 +3,12 @@ // found in the LICENSE file. #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" +#include "flutter/common/threads.h" +#include "flutter/flow/layers/layer_tree.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" +#include "lib/ftl/synchronization/waitable_event.h" +#include "third_party/skia/include/utils/mac/SkCGUtils.h" @interface FlutterView () @@ -30,4 +36,91 @@ return YES; } +void SnapshotRasterizer(ftl::WeakPtr rasterizer, + CGContextRef context, + bool is_opaque) { + if (!rasterizer) { + return; + } + + // Access the layer tree and assess the description of the backing store to + // create for this snapshot. + flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree(); + if (layer_tree == nullptr) { + return; + } + auto size = layer_tree->frame_size(); + if (size.isEmpty()) { + return; + } + auto info = + SkImageInfo::MakeN32(size.width(), size.height(), + is_opaque ? SkAlphaType::kOpaque_SkAlphaType + : SkAlphaType::kPremul_SkAlphaType); + + // Create the backing store and prepare for use. + SkBitmap bitmap; + bitmap.setInfo(info); + if (!bitmap.tryAllocPixels()) { + return; + } + + // Create a canvas from the backing store and a single use compositor context + // to draw into the canvas. + SkAutoLockPixels pixel_lock(bitmap, true); + + SkCanvas canvas(bitmap); + + { + flow::CompositorContext compositor_context; + auto frame = compositor_context.AcquireFrame(nullptr, &canvas, + false /* instrumentation */); + layer_tree->Raster(frame, false /* ignore raster cache. */); + } + + canvas.flush(); + + // Draw the bitmap to the system provided snapshotting context. + SkCGDrawBitmap(context, bitmap, 0, 0); +} + +void SnapshotContents(CGContextRef context, bool is_opaque) { + // TODO(chinmaygarde): Currently, there is no way to get the rasterizer for + // a particular platform view from the shell. But, for now, we only have one + // platform view. So use that. Once we support multiple platform views, the + // shell will need to provide a way to get the rasterizer for a specific + // platform view. + std::vector> registered_rasterizers; + shell::Shell::Shared().GetRasterizers(®istered_rasterizers); + for (auto& rasterizer : registered_rasterizers) { + SnapshotRasterizer(rasterizer, context, is_opaque); + } +} + +void SnapshotContentsSync(CGContextRef context, UIView* view) { + auto gpu_thread = blink::Threads::Gpu(); + + if (!gpu_thread) { + return; + } + + ftl::AutoResetWaitableEvent latch; + gpu_thread->PostTask([&latch, context, view]() { + SnapshotContents(context, [view isOpaque]); + latch.Signal(); + }); + latch.Wait(); +} + +// Override the default CALayerDelegate method so that APIs that attempt to +// screenshot the view display contents correctly. We cannot depend on +// reading +// GPU pixels directly because: +// 1: We dont use retained backing on the CAEAGLLayer. +// 2: The call is made of the platform thread and not the GPU thread. +// 3: There may be a software rasterizer. +- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { + SnapshotContentsSync(context, self); +} + @end -- GitLab