未验证 提交 8973c733 编写于 作者: C Chinmay Garde 提交者: GitHub

Implement Scene::toImage for creating a raster image representation of a scene. (#5021)

上级 572c5844
......@@ -6,6 +6,7 @@
#include "flutter/flow/layers/layer.h"
#include "flutter/glue/trace_event.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
namespace flow {
......@@ -60,6 +61,7 @@ void LayerTree::UpdateScene(SceneUpdateContext& context,
#endif
void LayerTree::Paint(CompositorContext::ScopedFrame& frame) const {
TRACE_EVENT0("flutter", "LayerTree::Paint");
Layer::PaintContext context = {
*frame.canvas(), //
frame.context().frame_time(), //
......@@ -67,10 +69,50 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame) const {
frame.context().texture_registry(), //
checkerboard_offscreen_layers_ //
};
TRACE_EVENT0("flutter", "LayerTree::Paint");
if (root_layer_->needs_painting())
root_layer_->Paint(context);
}
sk_sp<SkPicture> LayerTree::Flatten(const SkRect& bounds) {
TRACE_EVENT0("flutter", "LayerTree::Flatten");
SkPictureRecorder recorder;
auto canvas = recorder.beginRecording(bounds);
if (!canvas) {
return nullptr;
}
Layer::PrerollContext preroll_context{
nullptr, // raster_cache (don't consult the cache)
nullptr, // gr_context (used for the raster cache)
nullptr, // SkColorSpace* dst_color_space
SkRect::MakeEmpty(), // SkRect child_paint_bounds
};
const Stopwatch unused_stopwatch;
TextureRegistry unused_texture_registry;
Layer::PaintContext paint_context = {
*canvas, // canvas
unused_stopwatch, // frame time (dont care)
unused_stopwatch, // engine time (dont care)
unused_texture_registry, // texture registry (not supported)
false // checkerboard offscreen layers
};
// Even if we don't have a root layer, we still need to create an empty
// picture.
if (root_layer_) {
root_layer_->Preroll(&preroll_context, SkMatrix::I());
// The needs painting flag may be set after the preroll. So check it after.
if (root_layer_->needs_painting()) {
root_layer_->Paint(paint_context);
}
}
return recorder.finishRecordingAsPicture();
}
} // namespace flow
......@@ -13,6 +13,7 @@
#include "flutter/flow/layers/layer.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/time/time_delta.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkSize.h"
namespace flow {
......@@ -33,6 +34,8 @@ class LayerTree {
void Paint(CompositorContext::ScopedFrame& frame) const;
sk_sp<SkPicture> Flatten(const SkRect& bounds);
Layer* root_layer() const { return root_layer_.get(); }
void set_root_layer(std::unique_ptr<Layer> root_layer) {
......
......@@ -17,6 +17,19 @@ class Scene extends NativeFieldWrapperClass2 {
/// To create a Scene object, use a [SceneBuilder].
Scene._();
/// Creates a raster image representation of the current state of the scene.
/// This is a slow operation that is performed on a background thread.
Future<Image> toImage(int width, int height) {
if (width <= 0 || height <= 0)
throw new Exception('Invalid image dimensions.');
return _futurize(
(_Callback<Image> callback) => _toImage(width, height, callback)
);
}
String _toImage(int width, int height, _Callback<Image> callback) native 'Scene_toImage';
/// Releases the resources used by this scene.
///
/// After calling this function, the scene is cannot be used further.
......
......@@ -4,18 +4,26 @@
#include "flutter/lib/ui/compositing/scene.h"
#include "flutter/fml/trace_event.h"
#include "flutter/lib/ui/painting/image.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/tonic/converter/dart_converter.h"
#include "lib/tonic/dart_args.h"
#include "lib/tonic/dart_binding_macros.h"
#include "lib/tonic/dart_library_natives.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "lib/tonic/dart_persistent_value.h"
#include "lib/tonic/dart_wrappable.h"
#include "lib/tonic/logging/dart_invoke.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace blink {
IMPLEMENT_WRAPPERTYPEINFO(ui, Scene);
#define FOR_EACH_BINDING(V) V(Scene, dispose)
#define FOR_EACH_BINDING(V) \
V(Scene, toImage) \
V(Scene, dispose)
DART_BIND_ALL(Scene, FOR_EACH_BINDING)
......@@ -46,6 +54,128 @@ void Scene::dispose() {
ClearDartWrapper();
}
static sk_sp<SkImage> CreateSceneSnapshot(GrContext* context,
sk_sp<SkPicture> picture,
const SkSize& size) {
TRACE_EVENT0("flutter", "CreateSceneSnapshot");
auto image_info =
SkImageInfo::MakeN32Premul(SkISize::Make(size.width(), size.height()));
sk_sp<SkSurface> surface;
if (context) {
surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info);
}
if (!surface) {
surface = SkSurface::MakeRaster(image_info);
}
if (!surface) {
return nullptr;
}
auto canvas = surface->getCanvas();
if (!canvas) {
return nullptr;
}
if (picture) {
canvas->drawPicture(picture.get());
}
auto snapshot = surface->makeImageSnapshot();
if (!snapshot) {
return nullptr;
}
return snapshot->makeRasterImage();
}
Dart_Handle Scene::toImage(uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback) {
TRACE_EVENT0("flutter", "Scene::toImage");
if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) {
return tonic::ToDart("Image callback was invalid");
}
if (!m_layerTree) {
return tonic::ToDart("Scene did not contain a layer tree.");
}
if (width == 0 || height == 0) {
return tonic::ToDart("Image dimensions for scene were invalid.");
}
auto dart_state = UIDartState::Current();
auto image_callback = std::make_unique<tonic::DartPersistentValue>(
dart_state, raw_image_callback);
// We can't create an image on this task runner because we don't have a
// graphics context. Even if we did, it would be slow anyway. Also, this
// thread owns the sole reference to the layer tree. So we flatten the layer
// tree into a picture and use that as the thread transport mechanism.
auto bounds_size = SkSize::Make(width, height);
auto picture = m_layerTree->Flatten(SkRect::MakeSize(bounds_size));
if (!picture) {
// Already in Dart scope.
return tonic::ToDart("Could not flatten scene into a layer tree.");
}
auto resource_context = dart_state->GetResourceContext();
auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner();
auto unref_queue = dart_state->GetSkiaUnrefQueue();
// The picture has been prepared on the UI thread.
dart_state->GetTaskRunners().GetIOTaskRunner()->PostTask(
fxl::MakeCopyable([picture = std::move(picture), //
bounds_size, //
resource_context = std::move(resource_context), //
ui_task_runner = std::move(ui_task_runner), //
image_callback = std::move(image_callback), //
unref_queue = std::move(unref_queue) //
]() mutable {
// Snapshot the picture on the IO thread that contains an optional
// GrContext.
auto image = CreateSceneSnapshot(resource_context.get(),
std::move(picture), bounds_size);
// Send the image back to the UI thread for submission back to the
// framework.
ui_task_runner->PostTask(
fxl::MakeCopyable([image = std::move(image), //
image_callback = std::move(image_callback), //
unref_queue = std::move(unref_queue) //
]() mutable {
auto dart_state = image_callback->dart_state().get();
if (!dart_state) {
// The root isolate could have died in the meantime.
return;
}
tonic::DartState::Scope scope(dart_state);
if (!image) {
tonic::DartInvoke(image_callback->Get(), {Dart_Null()});
return;
}
auto dart_image = CanvasImage::Create();
dart_image->set_image({std::move(image), std::move(unref_queue)});
auto raw_dart_image = tonic::ToDart(std::move(dart_image));
// All done!
tonic::DartInvoke(image_callback->Get(), {raw_dart_image});
}));
}));
return Dart_Null();
}
std::unique_ptr<flow::LayerTree> Scene::takeLayerTree() {
return std::move(m_layerTree);
}
......
......@@ -32,6 +32,10 @@ class Scene : public fxl::RefCountedThreadSafe<Scene>,
std::unique_ptr<flow::LayerTree> takeLayerTree();
Dart_Handle toImage(uint32_t width,
uint32_t height,
Dart_Handle image_callback);
void dispose();
static void RegisterNatives(tonic::DartLibraryNatives* natives);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册