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/codec.h"
6
7 #include <variant>
8
9 #include "flutter/common/task_runners.h"
10 #include "flutter/fml/logging.h"
11 #include "flutter/fml/make_copyable.h"
12 #include "flutter/fml/trace_event.h"
13 #include "flutter/lib/ui/painting/frame_info.h"
14 #include "flutter/lib/ui/painting/multi_frame_codec.h"
15 #include "flutter/lib/ui/painting/single_frame_codec.h"
16 #include "third_party/skia/include/codec/SkCodec.h"
17 #include "third_party/skia/include/core/SkPixelRef.h"
18
19 #if OS_ANDROID
20 #include <sys/mman.h>
21 #endif
22
23 using tonic::DartInvoke;
24 using tonic::DartPersistentValue;
25 using tonic::ToDart;
26
27 namespace flutter {
28
29 namespace {
30
31 // This needs to be kept in sync with _kDoNotResizeDimension in painting.dart
32 const int kDoNotResizeDimension = -1;
33
34 // This must be kept in sync with the enum in painting.dart
35 enum PixelFormat {
36 kRGBA8888,
37 kBGRA8888,
38 };
39
40 #if OS_ANDROID
41
42 // Compressed image buffers are allocated on the UI thread but are deleted on a
43 // decoder worker thread. Android's implementation of malloc appears to
44 // continue growing the native heap size when the allocating thread is
45 // different from the freeing thread. To work around this, create an SkData
46 // backed by an anonymous mapping.
MakeSkDataWithCopy(const void * data,size_t length)47 sk_sp<SkData> MakeSkDataWithCopy(const void* data, size_t length) {
48 if (length == 0) {
49 return SkData::MakeEmpty();
50 }
51
52 size_t mapping_length = length + sizeof(size_t);
53 void* mapping = ::mmap(nullptr, mapping_length, PROT_READ | PROT_WRITE,
54 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
55
56 if (mapping == MAP_FAILED) {
57 return SkData::MakeEmpty();
58 }
59
60 *reinterpret_cast<size_t*>(mapping) = mapping_length;
61 void* mapping_data = reinterpret_cast<char*>(mapping) + sizeof(size_t);
62 ::memcpy(mapping_data, data, length);
63
64 SkData::ReleaseProc proc = [](const void* ptr, void* context) {
65 size_t* size_ptr = reinterpret_cast<size_t*>(context);
66 FML_DCHECK(ptr == size_ptr + 1);
67 if (::munmap(const_cast<void*>(context), *size_ptr) == -1) {
68 FML_LOG(ERROR) << "munmap of codec SkData failed";
69 }
70 };
71
72 return SkData::MakeWithProc(mapping_data, length, proc, mapping);
73 }
74
75 #else
76
MakeSkDataWithCopy(const void * data,size_t length)77 sk_sp<SkData> MakeSkDataWithCopy(const void* data, size_t length) {
78 return SkData::MakeWithCopy(data, length);
79 }
80
81 #endif // OS_ANDROID
82
83 } // anonymous namespace
84
ConvertImageInfo(Dart_Handle image_info_handle,Dart_NativeArguments args)85 static std::variant<ImageDecoder::ImageInfo, std::string> ConvertImageInfo(
86 Dart_Handle image_info_handle,
87 Dart_NativeArguments args) {
88 Dart_Handle width_handle = Dart_GetField(image_info_handle, ToDart("width"));
89 if (!Dart_IsInteger(width_handle)) {
90 return "ImageInfo.width must be an integer";
91 }
92 Dart_Handle height_handle =
93 Dart_GetField(image_info_handle, ToDart("height"));
94 if (!Dart_IsInteger(height_handle)) {
95 return "ImageInfo.height must be an integer";
96 }
97 Dart_Handle format_handle =
98 Dart_GetField(image_info_handle, ToDart("format"));
99 if (!Dart_IsInteger(format_handle)) {
100 return "ImageInfo.format must be an integer";
101 }
102 Dart_Handle row_bytes_handle =
103 Dart_GetField(image_info_handle, ToDart("rowBytes"));
104 if (!Dart_IsInteger(row_bytes_handle)) {
105 return "ImageInfo.rowBytes must be an integer";
106 }
107
108 PixelFormat pixel_format = static_cast<PixelFormat>(
109 tonic::DartConverter<int>::FromDart(format_handle));
110 SkColorType color_type = kUnknown_SkColorType;
111 switch (pixel_format) {
112 case kRGBA8888:
113 color_type = kRGBA_8888_SkColorType;
114 break;
115 case kBGRA8888:
116 color_type = kBGRA_8888_SkColorType;
117 break;
118 }
119 if (color_type == kUnknown_SkColorType) {
120 return "Invalid pixel format";
121 }
122
123 int width = tonic::DartConverter<int>::FromDart(width_handle);
124 if (width <= 0) {
125 return "width must be greater than zero";
126 }
127 int height = tonic::DartConverter<int>::FromDart(height_handle);
128 if (height <= 0) {
129 return "height must be greater than zero";
130 }
131
132 ImageDecoder::ImageInfo image_info;
133 image_info.sk_info =
134 SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
135 image_info.row_bytes =
136 tonic::DartConverter<size_t>::FromDart(row_bytes_handle);
137
138 if (image_info.row_bytes < image_info.sk_info.minRowBytes()) {
139 return "rowBytes does not match the width of the image";
140 }
141
142 return image_info;
143 }
144
InstantiateImageCodec(Dart_NativeArguments args)145 static void InstantiateImageCodec(Dart_NativeArguments args) {
146 Dart_Handle callback_handle = Dart_GetNativeArgument(args, 1);
147 if (!Dart_IsClosure(callback_handle)) {
148 Dart_SetReturnValue(args, tonic::ToDart("Callback must be a function"));
149 return;
150 }
151
152 Dart_Handle image_info_handle = Dart_GetNativeArgument(args, 2);
153
154 std::optional<ImageDecoder::ImageInfo> image_info;
155
156 if (!Dart_IsNull(image_info_handle)) {
157 auto image_info_results = ConvertImageInfo(image_info_handle, args);
158 if (auto value =
159 std::get_if<ImageDecoder::ImageInfo>(&image_info_results)) {
160 image_info = *value;
161 } else if (auto error = std::get_if<std::string>(&image_info_results)) {
162 Dart_SetReturnValue(args, tonic::ToDart(*error));
163 return;
164 }
165 }
166
167 sk_sp<SkData> buffer;
168
169 {
170 Dart_Handle exception = nullptr;
171 tonic::Uint8List list =
172 tonic::DartConverter<tonic::Uint8List>::FromArguments(args, 0,
173 exception);
174 if (exception) {
175 Dart_SetReturnValue(args, exception);
176 return;
177 }
178 buffer = MakeSkDataWithCopy(list.data(), list.num_elements());
179 }
180
181 if (image_info) {
182 const auto expected_size =
183 image_info->row_bytes * image_info->sk_info.height();
184 if (buffer->size() < expected_size) {
185 Dart_SetReturnValue(
186 args, ToDart("Pixel buffer size does not match image size"));
187 return;
188 }
189 }
190
191 const int targetWidth =
192 tonic::DartConverter<int>::FromDart(Dart_GetNativeArgument(args, 3));
193 const int targetHeight =
194 tonic::DartConverter<int>::FromDart(Dart_GetNativeArgument(args, 4));
195
196 std::unique_ptr<SkCodec> codec;
197 bool single_frame;
198 if (image_info) {
199 single_frame = true;
200 } else {
201 codec = SkCodec::MakeFromData(buffer);
202 if (!codec) {
203 Dart_SetReturnValue(args, ToDart("Could not instantiate image codec."));
204 return;
205 }
206 single_frame = codec->getFrameCount() == 1;
207 }
208
209 fml::RefPtr<Codec> ui_codec;
210
211 if (single_frame) {
212 ImageDecoder::ImageDescriptor descriptor;
213 descriptor.decompressed_image_info = image_info;
214
215 if (targetWidth != kDoNotResizeDimension) {
216 descriptor.target_width = targetWidth;
217 }
218 if (targetHeight != kDoNotResizeDimension) {
219 descriptor.target_height = targetHeight;
220 }
221 descriptor.data = std::move(buffer);
222
223 ui_codec = fml::MakeRefCounted<SingleFrameCodec>(std::move(descriptor));
224 } else {
225 ui_codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(codec));
226 }
227
228 tonic::DartInvoke(callback_handle, {ToDart(ui_codec)});
229 }
230
231 IMPLEMENT_WRAPPERTYPEINFO(ui, Codec);
232
233 #define FOR_EACH_BINDING(V) \
234 V(Codec, getNextFrame) \
235 V(Codec, frameCount) \
236 V(Codec, repetitionCount) \
237 V(Codec, dispose)
238
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)239 FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
240
241 void Codec::dispose() {
242 ClearDartWrapper();
243 }
244
RegisterNatives(tonic::DartLibraryNatives * natives)245 void Codec::RegisterNatives(tonic::DartLibraryNatives* natives) {
246 natives->Register({
247 {"instantiateImageCodec", InstantiateImageCodec, 5, true},
248 });
249 natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
250 }
251
252 } // namespace flutter
253