Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 2 additions & 72 deletions lib/ui/compositing/scene.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@

#include "flutter/lib/ui/compositing/scene.h"

#include "flutter/fml/make_copyable.h"
#include "flutter/fml/trace_event.h"
#include "flutter/lib/ui/painting/image.h"
#include "flutter/lib/ui/painting/picture.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
#include "third_party/tonic/dart_persistent_value.h"
#include "third_party/tonic/logging/dart_invoke.h"

namespace blink {

Expand Down Expand Up @@ -57,85 +55,17 @@ 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);
auto unref_queue = dart_state->GetSkiaUnrefQueue();
auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner();
auto gpu_task_runner = dart_state->GetTaskRunners().GetGPUTaskRunner();
auto snapshot_delegate = dart_state->GetSnapshotDelegate();

// 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 picture_bounds = SkISize::Make(width, height);
auto picture = m_layerTree->Flatten(SkRect::MakeWH(width, height));

if (!picture) {
// Already in Dart scope.
return tonic::ToDart("Could not flatten scene into a layer tree.");
}

auto ui_task = fml::MakeCopyable([ui_task_runner,
image_callback = std::move(image_callback),
unref_queue](
sk_sp<SkImage> raster_image) mutable {
// Send the raster image back to the UI thread for submission to the
// framework.
ui_task_runner->PostTask(fml::MakeCopyable([raster_image,
image_callback =
std::move(image_callback),
unref_queue]() mutable {
auto dart_state = image_callback->dart_state().lock();
if (!dart_state) {
// The root isolate could have died in the meantime.
return;
}
tonic::DartState::Scope scope(dart_state);

if (!raster_image) {
tonic::DartInvoke(image_callback->Get(), {Dart_Null()});
return;
}

auto dart_image = CanvasImage::Create();
dart_image->set_image({std::move(raster_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});
}));
});

auto gpu_task = fml::MakeCopyable([gpu_task_runner, picture, picture_bounds,
snapshot_delegate, ui_task]() {
gpu_task_runner->PostTask([snapshot_delegate, picture, picture_bounds,
ui_task]() {
// Snapshot the picture on the GPU thread. This thread has access to the
// GPU contexts that may contain the sole references to texture backed
// images in the picture.
ui_task(snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds));
});
});

// Kick things off on the GPU.
gpu_task();

return Dart_Null();
return Picture::RasterizeToImage(picture, width, height, raw_image_callback);
}

std::unique_ptr<flow::LayerTree> Scene::takeLayerTree() {
Expand Down
10 changes: 9 additions & 1 deletion lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3593,7 +3593,15 @@ class Picture extends NativeFieldWrapperClass2 {
///
/// Although the image is returned synchronously, the picture is actually
/// rasterized the first time the image is drawn and then cached.
Image toImage(int width, int height) native 'Picture_toImage';
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 'Picture_toImage';

/// Release the resources used by this object. The object is no longer usable
/// after this method is called.
Expand Down
92 changes: 86 additions & 6 deletions lib/ui/painting/picture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

#include "flutter/lib/ui/painting/picture.h"

#include "flutter/fml/make_copyable.h"
#include "flutter/lib/ui/painting/canvas.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
#include "third_party/tonic/dart_persistent_value.h"
#include "third_party/tonic/logging/dart_invoke.h"

namespace blink {

Expand All @@ -32,12 +35,14 @@ Picture::Picture(flow::SkiaGPUObject<SkPicture> picture)

Picture::~Picture() = default;

fml::RefPtr<CanvasImage> Picture::toImage(int width, int height) {
fml::RefPtr<CanvasImage> image = CanvasImage::Create();
image->set_image(UIDartState::CreateGPUObject(SkImage::MakeFromPicture(
picture_.get(), SkISize::Make(width, height), nullptr, nullptr,
SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB())));
return image;
Dart_Handle Picture::toImage(uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback) {
if (!picture_.get()) {
return tonic::ToDart("Picture is null");
}

return RasterizeToImage(picture_.get(), width, height, raw_image_callback);
}

void Picture::dispose() {
Expand All @@ -52,4 +57,79 @@ size_t Picture::GetAllocationSize() {
}
}

Dart_Handle Picture::RasterizeToImage(sk_sp<SkPicture> picture,
uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback) {
if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) {
return tonic::ToDart("Image callback was invalid");
}

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);
auto unref_queue = dart_state->GetSkiaUnrefQueue();
auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner();
auto gpu_task_runner = dart_state->GetTaskRunners().GetGPUTaskRunner();
auto snapshot_delegate = dart_state->GetSnapshotDelegate();

// 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 picture_bounds = SkISize::Make(width, height);

auto ui_task = fml::MakeCopyable([ui_task_runner,
image_callback = std::move(image_callback),
unref_queue](
sk_sp<SkImage> raster_image) mutable {
// Send the raster image back to the UI thread for submission to the
// framework.
ui_task_runner->PostTask(fml::MakeCopyable([raster_image,
image_callback =
std::move(image_callback),
unref_queue]() mutable {
auto dart_state = image_callback->dart_state().lock();
if (!dart_state) {
// The root isolate could have died in the meantime.
return;
}
tonic::DartState::Scope scope(dart_state);

if (!raster_image) {
tonic::DartInvoke(image_callback->Get(), {Dart_Null()});
return;
}

auto dart_image = CanvasImage::Create();
dart_image->set_image({std::move(raster_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});
}));
});

auto gpu_task = fml::MakeCopyable([gpu_task_runner, picture, picture_bounds,
snapshot_delegate, ui_task]() {
gpu_task_runner->PostTask([snapshot_delegate, picture, picture_bounds,
ui_task]() {
// Snapshot the picture on the GPU thread. This thread has access to the
// GPU contexts that may contain the sole references to texture backed
// images in the picture.
ui_task(snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds));
});
});

// Kick things off on the GPU.
gpu_task();

return Dart_Null();
}

} // namespace blink
9 changes: 8 additions & 1 deletion lib/ui/painting/picture.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,21 @@ class Picture : public RefCountedDartWrappable<Picture> {

sk_sp<SkPicture> picture() const { return picture_.get(); }

fml::RefPtr<CanvasImage> toImage(int width, int height);
Dart_Handle toImage(uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback);

void dispose();

size_t GetAllocationSize() override;

static void RegisterNatives(tonic::DartLibraryNatives* natives);

static Dart_Handle RasterizeToImage(sk_sp<SkPicture> picture,
uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback);

private:
explicit Picture(flow::SkiaGPUObject<SkPicture> picture);

Expand Down