Flutter Engine
image_encoding.cc
Go to the documentation of this file.
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/image_encoding.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "flutter/common/task_runners.h"
11 #include "flutter/fml/build_config.h"
12 #include "flutter/fml/make_copyable.h"
13 #include "flutter/fml/trace_event.h"
14 #include "flutter/lib/ui/painting/image.h"
15 #include "flutter/lib/ui/ui_dart_state.h"
16 #include "third_party/skia/include/core/SkCanvas.h"
17 #include "third_party/skia/include/core/SkEncodedImageFormat.h"
18 #include "third_party/skia/include/core/SkImage.h"
19 #include "third_party/skia/include/core/SkSurface.h"
23 
24 using tonic::DartInvoke;
26 using tonic::ToDart;
27 
28 namespace flutter {
29 namespace {
30 
31 // This must be kept in sync with the enum in painting.dart
33  kRawRGBA,
34  kRawUnmodified,
35  kPNG,
36 };
37 
38 void FinalizeSkData(void* isolate_callback_data,
39  Dart_WeakPersistentHandle handle,
40  void* peer) {
41  SkData* buffer = reinterpret_cast<SkData*>(peer);
42  buffer->unref();
43 }
44 
45 void InvokeDataCallback(std::unique_ptr<DartPersistentValue> callback,
46  sk_sp<SkData> buffer) {
47  std::shared_ptr<tonic::DartState> dart_state = callback->dart_state().lock();
48  if (!dart_state) {
49  return;
50  }
51  tonic::DartState::Scope scope(dart_state);
52  if (!buffer) {
53  DartInvoke(callback->value(), {Dart_Null()});
54  return;
55  }
56  // Skia will not modify the buffer, and it is backed by memory that is
57  // read/write, so Dart can be given direct access to the buffer through an
58  // external Uint8List.
59  void* bytes = const_cast<void*>(buffer->data());
60  const intptr_t length = buffer->size();
61  void* peer = reinterpret_cast<void*>(buffer.release());
62  Dart_Handle dart_data = Dart_NewExternalTypedDataWithFinalizer(
63  Dart_TypedData_kUint8, bytes, length, peer, length, FinalizeSkData);
64  DartInvoke(callback->value(), {dart_data});
65 }
66 
67 sk_sp<SkImage> ConvertToRasterUsingResourceContext(
68  sk_sp<SkImage> image,
69  GrDirectContext* resource_context) {
70  sk_sp<SkSurface> surface;
71  SkImageInfo surface_info = SkImageInfo::MakeN32Premul(image->dimensions());
72  if (resource_context) {
73  surface = SkSurface::MakeRenderTarget(resource_context, SkBudgeted::kNo,
74  surface_info);
75  } else {
76  surface = SkSurface::MakeRaster(surface_info);
77  }
78 
79  if (surface == nullptr || surface->getCanvas() == nullptr) {
80  FML_LOG(ERROR) << "Could not create a surface to copy the texture into.";
81  return nullptr;
82  }
83 
84  surface->getCanvas()->drawImage(image, 0, 0);
85  surface->getCanvas()->flush();
86 
87  auto snapshot = surface->makeImageSnapshot();
88 
89  if (snapshot == nullptr) {
90  FML_LOG(ERROR) << "Could not snapshot image to encode.";
91  return nullptr;
92  }
93 
94  return snapshot->makeRasterImage();
95 }
96 
97 void ConvertImageToRaster(sk_sp<SkImage> image,
98  std::function<void(sk_sp<SkImage>)> encode_task,
99  fml::RefPtr<fml::TaskRunner> raster_task_runner,
100  fml::RefPtr<fml::TaskRunner> io_task_runner,
101  GrDirectContext* resource_context,
102  fml::WeakPtr<SnapshotDelegate> snapshot_delegate) {
103  // Check validity of the image.
104  if (image == nullptr) {
105  FML_LOG(ERROR) << "Image was null.";
106  encode_task(nullptr);
107  return;
108  }
109 
110  auto dimensions = image->dimensions();
111 
112  if (dimensions.isEmpty()) {
113  FML_LOG(ERROR) << "Image dimensions were empty.";
114  encode_task(nullptr);
115  return;
116  }
117 
118  SkPixmap pixmap;
119  if (image->peekPixels(&pixmap)) {
120  // This is already a raster image.
121  encode_task(image);
122  return;
123  }
124 
125  if (sk_sp<SkImage> raster_image = image->makeRasterImage()) {
126  // The image can be converted to a raster image.
127  encode_task(raster_image);
128  return;
129  }
130 
131  // Cross-context images do not support makeRasterImage. Convert these images
132  // by drawing them into a surface. This must be done on the raster thread
133  // to prevent concurrent usage of the image on both the IO and raster threads.
134  raster_task_runner->PostTask([image, encode_task = std::move(encode_task),
135  resource_context, snapshot_delegate,
136  io_task_runner]() {
137  sk_sp<SkImage> raster_image =
138  snapshot_delegate->ConvertToRasterImage(image);
139 
140  io_task_runner->PostTask([image, encode_task = std::move(encode_task),
141  raster_image = std::move(raster_image),
142  resource_context]() mutable {
143  if (!raster_image) {
144  // The rasterizer was unable to render the cross-context image
145  // (presumably because it does not have a GrContext). In that case,
146  // convert the image on the IO thread using the resource context.
147  raster_image =
148  ConvertToRasterUsingResourceContext(image, resource_context);
149  }
150  encode_task(raster_image);
151  });
152  });
153 }
154 
155 sk_sp<SkData> CopyImageByteData(sk_sp<SkImage> raster_image,
156  SkColorType color_type) {
157  FML_DCHECK(raster_image);
158 
159  SkPixmap pixmap;
160 
161  if (!raster_image->peekPixels(&pixmap)) {
162  FML_LOG(ERROR) << "Could not copy pixels from the raster image.";
163  return nullptr;
164  }
165 
166  // The color types already match. No need to swizzle. Return early.
167  if (pixmap.colorType() == color_type) {
168  return SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize());
169  }
170 
171  // Perform swizzle if the type doesnt match the specification.
172  auto surface = SkSurface::MakeRaster(
173  SkImageInfo::Make(raster_image->width(), raster_image->height(),
174  color_type, kPremul_SkAlphaType, nullptr));
175 
176  if (!surface) {
177  FML_LOG(ERROR) << "Could not setup the surface for swizzle.";
178  return nullptr;
179  }
180 
181  surface->writePixels(pixmap, 0, 0);
182 
183  if (!surface->peekPixels(&pixmap)) {
184  FML_LOG(ERROR) << "Pixel address is not available.";
185  return nullptr;
186  }
187 
188  return SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize());
189 }
190 
191 sk_sp<SkData> EncodeImage(sk_sp<SkImage> raster_image, ImageByteFormat format) {
192  TRACE_EVENT0("flutter", __FUNCTION__);
193 
194  if (!raster_image) {
195  return nullptr;
196  }
197 
198  switch (format) {
199  case kPNG: {
200  auto png_image =
201  raster_image->encodeToData(SkEncodedImageFormat::kPNG, 0);
202 
203  if (png_image == nullptr) {
204  FML_LOG(ERROR) << "Could not convert raster image to PNG.";
205  return nullptr;
206  };
207  return png_image;
208  } break;
209  case kRawRGBA: {
210  return CopyImageByteData(raster_image, kRGBA_8888_SkColorType);
211  } break;
212  case kRawUnmodified: {
213  return CopyImageByteData(raster_image, raster_image->colorType());
214  } break;
215  }
216 
217  FML_LOG(ERROR) << "Unknown error encoding image.";
218  return nullptr;
219 }
220 
221 void EncodeImageAndInvokeDataCallback(
222  sk_sp<SkImage> image,
223  std::unique_ptr<DartPersistentValue> callback,
224  ImageByteFormat format,
225  fml::RefPtr<fml::TaskRunner> ui_task_runner,
226  fml::RefPtr<fml::TaskRunner> raster_task_runner,
227  fml::RefPtr<fml::TaskRunner> io_task_runner,
228  GrDirectContext* resource_context,
229  fml::WeakPtr<SnapshotDelegate> snapshot_delegate) {
230  auto callback_task = fml::MakeCopyable(
231  [callback = std::move(callback)](sk_sp<SkData> encoded) mutable {
232  InvokeDataCallback(std::move(callback), std::move(encoded));
233  });
234 
235  auto encode_task = [callback_task = std::move(callback_task), format,
236  ui_task_runner](sk_sp<SkImage> raster_image) {
237  sk_sp<SkData> encoded = EncodeImage(std::move(raster_image), format);
238  ui_task_runner->PostTask([callback_task = std::move(callback_task),
239  encoded = std::move(encoded)]() mutable {
240  callback_task(std::move(encoded));
241  });
242  };
243 
244  ConvertImageToRaster(std::move(image), encode_task, raster_task_runner,
245  io_task_runner, resource_context, snapshot_delegate);
246 }
247 
248 } // namespace
249 
250 Dart_Handle EncodeImage(CanvasImage* canvas_image,
251  int format,
252  Dart_Handle callback_handle) {
253  if (!canvas_image) {
254  return ToDart("encode called with non-genuine Image.");
255  }
256 
257  if (!Dart_IsClosure(callback_handle)) {
258  return ToDart("Callback must be a function.");
259  }
260 
261  ImageByteFormat image_format = static_cast<ImageByteFormat>(format);
262 
263  auto callback = std::make_unique<DartPersistentValue>(
264  tonic::DartState::Current(), callback_handle);
265 
266  const auto& task_runners = UIDartState::Current()->GetTaskRunners();
267 
269  [callback = std::move(callback), image = canvas_image->image(),
270  image_format, ui_task_runner = task_runners.GetUITaskRunner(),
271  raster_task_runner = task_runners.GetRasterTaskRunner(),
272  io_task_runner = task_runners.GetIOTaskRunner(),
273  io_manager = UIDartState::Current()->GetIOManager(),
274  snapshot_delegate =
275  UIDartState::Current()->GetSnapshotDelegate()]() mutable {
276  EncodeImageAndInvokeDataCallback(
277  std::move(image), std::move(callback), image_format,
278  std::move(ui_task_runner), std::move(raster_task_runner),
279  std::move(io_task_runner), io_manager->GetResourceContext().get(),
280  std::move(snapshot_delegate));
281  }));
282 
283  return Dart_Null();
284 }
285 
286 } // namespace flutter
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
#define FML_DCHECK(condition)
Definition: logging.h:86
uint32_t color_type
fml::WeakPtr< SnapshotDelegate > GetSnapshotDelegate() const
Dart_NativeFunction function
Definition: fuchsia.cc:51
static DartState * Current()
Definition: dart_state.cc:55
virtual void PostTask(const fml::closure &task)
Definition: task_runner.cc:24
#define FML_LOG(severity)
Definition: logging.h:65
Dart_Handle DartInvoke(Dart_Handle closure, std::initializer_list< Dart_Handle > args)
Definition: dart_invoke.cc:20
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
size_t length
virtual sk_sp< SkImage > ConvertToRasterImage(sk_sp< SkImage > image)=0
Dart_Handle EncodeImage(CanvasImage *canvas_image, int format, Dart_Handle callback_handle)
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
Definition: task_runners.cc:38
fml::WeakPtr< IOManager > GetIOManager() const
const TaskRunners & GetTaskRunners() const
Dart_Handle ToDart(const T &object)
sk_sp< SkImage > image() const
Definition: image.h:37
static UIDartState * Current()