• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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