Flutter Engine
image_decoder.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_decoder.h"
6 
7 #include <algorithm>
8 
9 #include "flutter/fml/make_copyable.h"
10 #include "third_party/skia/include/codec/SkCodec.h"
11 
12 namespace flutter {
13 
15  TaskRunners runners,
16  std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
17  fml::WeakPtr<IOManager> io_manager)
18  : runners_(std::move(runners)),
19  concurrent_task_runner_(std::move(concurrent_task_runner)),
20  io_manager_(std::move(io_manager)),
21  weak_factory_(this) {
22  FML_DCHECK(runners_.IsValid());
24  << "The image decoder must be created & collected on the UI thread.";
25 }
26 
27 ImageDecoder::~ImageDecoder() = default;
28 
29 static sk_sp<SkImage> ResizeRasterImage(sk_sp<SkImage> image,
30  const SkISize& resized_dimensions,
31  const fml::tracing::TraceFlow& flow) {
32  FML_DCHECK(!image->isTextureBacked());
33 
34  TRACE_EVENT0("flutter", __FUNCTION__);
35  flow.Step(__FUNCTION__);
36 
37  if (resized_dimensions.isEmpty()) {
38  FML_LOG(ERROR) << "Could not resize to empty dimensions.";
39  return nullptr;
40  }
41 
42  if (image->dimensions() == resized_dimensions) {
43  return image->makeRasterImage();
44  }
45 
46  const auto scaled_image_info =
47  image->imageInfo().makeDimensions(resized_dimensions);
48 
49  SkBitmap scaled_bitmap;
50  if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
51  FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
52  << scaled_image_info.computeMinByteSize() << "B";
53  return nullptr;
54  }
55 
56  if (!image->scalePixels(scaled_bitmap.pixmap(), kLow_SkFilterQuality,
57  SkImage::kDisallow_CachingHint)) {
58  FML_LOG(ERROR) << "Could not scale pixels";
59  return nullptr;
60  }
61 
62  // Marking this as immutable makes the MakeFromBitmap call share the pixels
63  // instead of copying.
64  scaled_bitmap.setImmutable();
65 
66  auto scaled_image = SkImage::MakeFromBitmap(scaled_bitmap);
67  if (!scaled_image) {
68  FML_LOG(ERROR) << "Could not create a scaled image from a scaled bitmap.";
69  return nullptr;
70  }
71 
72  return scaled_image;
73 }
74 
75 static sk_sp<SkImage> ImageFromDecompressedData(
77  uint32_t target_width,
78  uint32_t target_height,
79  const fml::tracing::TraceFlow& flow) {
80  TRACE_EVENT0("flutter", __FUNCTION__);
81  flow.Step(__FUNCTION__);
82  auto image = SkImage::MakeRasterData(
83  descriptor->image_info(), descriptor->data(), descriptor->row_bytes());
84 
85  if (!image) {
86  FML_LOG(ERROR) << "Could not create image from decompressed bytes.";
87  return nullptr;
88  }
89 
90  if (!target_width && !target_height) {
91  // No resizing requested. Just rasterize the image.
92  return image->makeRasterImage();
93  }
94 
95  return ResizeRasterImage(std::move(image),
96  SkISize::Make(target_width, target_height), flow);
97 }
98 
100  uint32_t target_width,
101  uint32_t target_height,
102  const fml::tracing::TraceFlow& flow) {
103  TRACE_EVENT0("flutter", __FUNCTION__);
104  flow.Step(__FUNCTION__);
105 
106  if (!descriptor->should_resize(target_width, target_height)) {
107  // No resizing requested. Just decode & rasterize the image.
108  sk_sp<SkImage> image = descriptor->image();
109  return image ? image->makeRasterImage() : nullptr;
110  }
111 
112  const SkISize source_dimensions = descriptor->image_info().dimensions();
113  const SkISize resized_dimensions = {static_cast<int32_t>(target_width),
114  static_cast<int32_t>(target_height)};
115 
116  auto decode_dimensions = descriptor->get_scaled_dimensions(
117  std::max(static_cast<double>(resized_dimensions.width()) /
118  source_dimensions.width(),
119  static_cast<double>(resized_dimensions.height()) /
120  source_dimensions.height()));
121 
122  // If the codec supports efficient sub-pixel decoding, decoded at a resolution
123  // close to the target resolution before resizing.
124  if (decode_dimensions != source_dimensions) {
125  auto scaled_image_info =
126  descriptor->image_info().makeDimensions(decode_dimensions);
127 
128  SkBitmap scaled_bitmap;
129  if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
130  FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
131  << scaled_image_info.computeMinByteSize() << "B";
132  return nullptr;
133  }
134 
135  const auto& pixmap = scaled_bitmap.pixmap();
136  if (descriptor->get_pixels(pixmap)) {
137  // Marking this as immutable makes the MakeFromBitmap call share
138  // the pixels instead of copying.
139  scaled_bitmap.setImmutable();
140 
141  auto decoded_image = SkImage::MakeFromBitmap(scaled_bitmap);
142  FML_DCHECK(decoded_image);
143  if (!decoded_image) {
144  FML_LOG(ERROR)
145  << "Could not create a scaled image from a scaled bitmap.";
146  return nullptr;
147  }
148  return ResizeRasterImage(std::move(decoded_image), resized_dimensions,
149  flow);
150  }
151  }
152 
153  auto image = descriptor->image();
154  if (!image) {
155  return nullptr;
156  }
157 
158  return ResizeRasterImage(std::move(image), resized_dimensions, flow);
159 }
160 
162  sk_sp<SkImage> image,
163  fml::WeakPtr<IOManager> io_manager,
164  const fml::tracing::TraceFlow& flow) {
165  TRACE_EVENT0("flutter", __FUNCTION__);
166  flow.Step(__FUNCTION__);
167 
168  // Should not already be a texture image because that is the entire point of
169  // the this method.
170  FML_DCHECK(!image->isTextureBacked());
171 
172  if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) {
173  FML_LOG(ERROR)
174  << "Could not acquire context of release queue for texture upload.";
175  return {};
176  }
177 
178  SkPixmap pixmap;
179  if (!image->peekPixels(&pixmap)) {
180  FML_LOG(ERROR) << "Could not peek pixels of image for texture upload.";
181  return {};
182  }
183 
184  SkiaGPUObject<SkImage> result;
185  io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
187  .SetIfTrue([&result, &pixmap, &image] {
188  SkSafeRef(image.get());
189  sk_sp<SkImage> texture_image = SkImage::MakeFromRaster(
190  pixmap,
191  [](const void* pixels, SkImage::ReleaseContext context) {
192  SkSafeUnref(static_cast<SkImage*>(context));
193  },
194  image.get());
195  result = {std::move(texture_image), nullptr};
196  })
197  .SetIfFalse([&result, context = io_manager->GetResourceContext(),
198  &pixmap, queue = io_manager->GetSkiaUnrefQueue()] {
199  TRACE_EVENT0("flutter", "MakeCrossContextImageFromPixmap");
200  sk_sp<SkImage> texture_image = SkImage::MakeCrossContextFromPixmap(
201  context.get(), // context
202  pixmap, // pixmap
203  true, // buildMips,
204  true // limitToMaxTextureSize
205  );
206  if (!texture_image) {
207  FML_LOG(ERROR) << "Could not make x-context image.";
208  result = {};
209  } else {
210  result = {std::move(texture_image), queue};
211  }
212  }));
213 
214  return result;
215 }
216 
218  uint32_t target_width,
219  uint32_t target_height,
220  const ImageResult& callback) {
221  TRACE_EVENT0("flutter", __FUNCTION__);
222  fml::tracing::TraceFlow flow(__FUNCTION__);
223 
224  FML_DCHECK(callback);
226 
227  // Always service the callback on the UI thread.
228  auto result = [callback, ui_runner = runners_.GetUITaskRunner()](
231  ui_runner->PostTask(fml::MakeCopyable(
232  [callback, image = std::move(image), flow = std::move(flow)]() mutable {
233  // We are going to terminate the trace flow here. Flows cannot
234  // terminate without a base trace. Add one explicitly.
235  TRACE_EVENT0("flutter", "ImageDecodeCallback");
236  flow.End();
237  callback(std::move(image));
238  }));
239  };
240 
241  if (!descriptor->data() || descriptor->data()->size() == 0) {
242  result({}, std::move(flow));
243  return;
244  }
245 
246  concurrent_task_runner_->PostTask(
247  fml::MakeCopyable([descriptor, //
248  io_manager = io_manager_, //
249  io_runner = runners_.GetIOTaskRunner(), //
250  result, //
251  target_width = target_width, //
252  target_height = target_height, //
253  flow = std::move(flow) //
254  ]() mutable {
255  // Step 1: Decompress the image.
256  // On Worker.
257 
258  auto decompressed =
259  descriptor->is_compressed()
260  ? ImageFromCompressedData(std::move(descriptor), //
261  target_width, //
262  target_height, //
263  flow)
264  : ImageFromDecompressedData(std::move(descriptor), //
265  target_width, //
266  target_height, //
267  flow);
268 
269  if (!decompressed) {
270  FML_LOG(ERROR) << "Could not decompress image.";
271  result({}, std::move(flow));
272  return;
273  }
274 
275  // Step 2: Update the image to the GPU.
276  // On IO Thread.
277 
278  io_runner->PostTask(fml::MakeCopyable([io_manager, decompressed, result,
279  flow =
280  std::move(flow)]() mutable {
281  if (!io_manager) {
282  FML_LOG(ERROR) << "Could not acquire IO manager.";
283  return result({}, std::move(flow));
284  }
285 
286  // If the IO manager does not have a resource context, the caller
287  // might not have set one or a software backend could be in use.
288  // Either way, just return the image as-is.
289  if (!io_manager->GetResourceContext()) {
290  result({std::move(decompressed), io_manager->GetSkiaUnrefQueue()},
291  std::move(flow));
292  return;
293  }
294 
295  auto uploaded =
296  UploadRasterImage(std::move(decompressed), io_manager, flow);
297 
298  if (!uploaded.get()) {
299  FML_LOG(ERROR) << "Could not upload image to the GPU.";
300  result({}, std::move(flow));
301  return;
302  }
303 
304  // Finally, all done.
305  result(std::move(uploaded), std::move(flow));
306  }));
307  }));
308 }
309 
311  return weak_factory_.GetWeakPtr();
312 }
313 
314 } // namespace flutter
static SkiaGPUObject< SkImage > UploadRasterImage(sk_sp< SkImage > image, fml::WeakPtr< IOManager > io_manager, const fml::tracing::TraceFlow &flow)
bool IsValid() const
Definition: task_runners.cc:46
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
#define FML_DCHECK(condition)
Definition: logging.h:86
Definition: ref_ptr.h:252
void Step(const char *label) const
Definition: trace_event.h:348
#define FML_LOG(severity)
Definition: logging.h:65
void Decode(fml::RefPtr< ImageDescriptor > descriptor, uint32_t target_width, uint32_t target_height, const ImageResult &result)
std::function< void(SkiaGPUObject< SkImage >)> ImageResult
Definition: image_decoder.h:41
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
void End(const char *label=nullptr)
Definition: trace_event.h:352
static sk_sp< SkImage > ImageFromDecompressedData(fml::RefPtr< ImageDescriptor > descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
static sk_sp< SkImage > ResizeRasterImage(sk_sp< SkImage > image, const SkISize &resized_dimensions, const fml::tracing::TraceFlow &flow)
ImageDecoder(TaskRunners runners, std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner, fml::WeakPtr< IOManager > io_manager)
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
Definition: task_runners.cc:38
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
fml::WeakPtr< IOManager > io_manager_
virtual bool RunsTasksOnCurrentThread()
Definition: task_runner.cc:43
fml::WeakPtr< ImageDecoder > GetWeakPtr() const
sk_sp< SkImage > ImageFromCompressedData(fml::RefPtr< ImageDescriptor > descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
Represents the 2 code paths available when calling |SyncSwitch::Execute|.
Definition: sync_switch.h:24