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 | |
20 | namespace flutter { |
21 | |
22 | IMPLEMENT_WRAPPERTYPEINFO(ui, Picture); |
23 | |
24 | #define FOR_EACH_BINDING(V) \ |
25 | V(Picture, toImage) \ |
26 | V(Picture, dispose) \ |
27 | V(Picture, GetAllocationSize) |
28 | |
29 | DART_BIND_ALL(Picture, FOR_EACH_BINDING) |
30 | |
31 | fml::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 | |
41 | Picture::Picture(flutter::SkiaGPUObject<SkPicture> picture, |
42 | size_t external_allocation_size) |
43 | : picture_(std::move(picture)), |
44 | external_allocation_size_(external_allocation_size) {} |
45 | |
46 | Picture::~Picture() = default; |
47 | |
48 | Dart_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 | |
58 | void Picture::dispose() { |
59 | picture_.reset(); |
60 | ClearDartWrapper(); |
61 | } |
62 | |
63 | size_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 | |
72 | Dart_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 | |