1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/lib/ui/painting/picture.h"
6
7#include <memory>
8
9#include "flutter/fml/make_copyable.h"
10#include "flutter/lib/ui/painting/canvas.h"
11#include "flutter/lib/ui/ui_dart_state.h"
12#include "third_party/skia/include/core/SkImage.h"
13#include "third_party/tonic/converter/dart_converter.h"
14#include "third_party/tonic/dart_args.h"
15#include "third_party/tonic/dart_binding_macros.h"
16#include "third_party/tonic/dart_library_natives.h"
17#include "third_party/tonic/dart_persistent_value.h"
18#include "third_party/tonic/logging/dart_invoke.h"
19
20namespace flutter {
21
22IMPLEMENT_WRAPPERTYPEINFO(ui, Picture);
23
24#define FOR_EACH_BINDING(V) \
25 V(Picture, toImage) \
26 V(Picture, dispose) \
27 V(Picture, GetAllocationSize)
28
29DART_BIND_ALL(Picture, FOR_EACH_BINDING)
30
31fml::RefPtr<Picture> Picture::Create(Dart_Handle dart_handle,
32 flutter::SkiaGPUObject<SkPicture> picture,
33 size_t external_allocation_size) {
34 auto canvas_picture = fml::MakeRefCounted<Picture>(std::move(picture),
35 external_allocation_size);
36
37 canvas_picture->AssociateWithDartWrapper(dart_handle);
38 return canvas_picture;
39}
40
41Picture::Picture(flutter::SkiaGPUObject<SkPicture> picture,
42 size_t external_allocation_size)
43 : picture_(std::move(picture)),
44 external_allocation_size_(external_allocation_size) {}
45
46Picture::~Picture() = default;
47
48Dart_Handle Picture::toImage(uint32_t width,
49 uint32_t height,
50 Dart_Handle raw_image_callback) {
51 if (!picture_.get()) {
52 return tonic::ToDart("Picture is null");
53 }
54
55 return RasterizeToImage(picture_.get(), width, height, raw_image_callback);
56}
57
58void Picture::dispose() {
59 picture_.reset();
60 ClearDartWrapper();
61}
62
63size_t Picture::GetAllocationSize() const {
64 if (auto picture = picture_.get()) {
65 return picture->approximateBytesUsed() + sizeof(Picture) +
66 external_allocation_size_;
67 } else {
68 return sizeof(Picture);
69 }
70}
71
72Dart_Handle Picture::RasterizeToImage(sk_sp<SkPicture> picture,
73 uint32_t width,
74 uint32_t height,
75 Dart_Handle raw_image_callback) {
76 if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) {
77 return tonic::ToDart("Image callback was invalid");
78 }
79
80 if (width == 0 || height == 0) {
81 return tonic::ToDart("Image dimensions for scene were invalid.");
82 }
83
84 auto* dart_state = UIDartState::Current();
85 auto image_callback = std::make_unique<tonic::DartPersistentValue>(
86 dart_state, raw_image_callback);
87 auto unref_queue = dart_state->GetSkiaUnrefQueue();
88 auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner();
89 auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner();
90 auto snapshot_delegate = dart_state->GetSnapshotDelegate();
91
92 // We can't create an image on this task runner because we don't have a
93 // graphics context. Even if we did, it would be slow anyway. Also, this
94 // thread owns the sole reference to the layer tree. So we flatten the layer
95 // tree into a picture and use that as the thread transport mechanism.
96
97 auto picture_bounds = SkISize::Make(width, height);
98
99 auto ui_task = fml::MakeCopyable([image_callback = std::move(image_callback),
100 unref_queue](
101 sk_sp<SkImage> raster_image) mutable {
102 auto dart_state = image_callback->dart_state().lock();
103 if (!dart_state) {
104 // The root isolate could have died in the meantime.
105 return;
106 }
107 tonic::DartState::Scope scope(dart_state);
108
109 if (!raster_image) {
110 tonic::DartInvoke(image_callback->Get(), {Dart_Null()});
111 return;
112 }
113
114 auto dart_image = CanvasImage::Create();
115 dart_image->set_image({std::move(raster_image), std::move(unref_queue)});
116 auto* raw_dart_image = tonic::ToDart(std::move(dart_image));
117
118 // All done!
119 tonic::DartInvoke(image_callback->Get(), {raw_dart_image});
120
121 // image_callback is associated with the Dart isolate and must be deleted
122 // on the UI thread.
123 image_callback.reset();
124 });
125
126 // Kick things off on the raster rask runner.
127 fml::TaskRunner::RunNowOrPostTask(
128 raster_task_runner,
129 [ui_task_runner, snapshot_delegate, picture, picture_bounds, ui_task] {
130 sk_sp<SkImage> raster_image =
131 snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds);
132
133 fml::TaskRunner::RunNowOrPostTask(
134 ui_task_runner,
135 [ui_task, raster_image]() { ui_task(raster_image); });
136 });
137
138 return Dart_Null();
139}
140
141} // namespace flutter
142