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