Flutter Engine
The Flutter Engine
image_decoder_skia.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#if !SLIMPELLER
6
7#include "flutter/lib/ui/painting/image_decoder_skia.h"
8
9#include <algorithm>
10
11#include "flutter/fml/logging.h"
12#include "flutter/fml/make_copyable.h"
13#include "flutter/lib/ui/painting/display_list_image_gpu.h"
17
18namespace flutter {
19
21 const TaskRunners& runners,
22 std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
23 fml::WeakPtr<IOManager> io_manager)
24 : ImageDecoder(runners,
25 std::move(concurrent_task_runner),
26 std::move(io_manager)) {}
27
29
31 const SkISize& resized_dimensions,
32 const fml::tracing::TraceFlow& flow) {
34
35 TRACE_EVENT0("flutter", __FUNCTION__);
36 flow.Step(__FUNCTION__);
37
38 if (resized_dimensions.isEmpty()) {
39 FML_LOG(ERROR) << "Could not resize to empty dimensions.";
40 return nullptr;
41 }
42
43 if (image->dimensions() == resized_dimensions) {
44 return image->makeRasterImage();
45 }
46
47 const auto scaled_image_info =
48 image->imageInfo().makeDimensions(resized_dimensions);
49
50 SkBitmap scaled_bitmap;
51 if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
52 FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
53 << scaled_image_info.computeMinByteSize() << "B";
54 return nullptr;
55 }
56
57 if (!image->scalePixels(
58 scaled_bitmap.pixmap(),
61 FML_LOG(ERROR) << "Could not scale pixels";
62 return nullptr;
63 }
64
65 // Marking this as immutable makes the MakeFromBitmap call share the pixels
66 // instead of copying.
67 scaled_bitmap.setImmutable();
68
69 auto scaled_image = SkImages::RasterFromBitmap(scaled_bitmap);
70 if (!scaled_image) {
71 FML_LOG(ERROR) << "Could not create a scaled image from a scaled bitmap.";
72 return nullptr;
73 }
74
75 return scaled_image;
76}
77
79 ImageDescriptor* descriptor,
80 uint32_t target_width,
81 uint32_t target_height,
82 const fml::tracing::TraceFlow& flow) {
83 TRACE_EVENT0("flutter", __FUNCTION__);
84 flow.Step(__FUNCTION__);
86 descriptor->image_info(), descriptor->data(), descriptor->row_bytes());
87
88 if (!image) {
89 FML_LOG(ERROR) << "Could not create image from decompressed bytes.";
90 return nullptr;
91 }
92
93 if (!target_width && !target_height) {
94 // No resizing requested. Just rasterize the image.
95 return image->makeRasterImage();
96 }
97
98 return ResizeRasterImage(image, SkISize::Make(target_width, target_height),
99 flow);
100}
101
103 ImageDescriptor* descriptor,
104 uint32_t target_width,
105 uint32_t target_height,
106 const fml::tracing::TraceFlow& flow) {
107 TRACE_EVENT0("flutter", __FUNCTION__);
108 flow.Step(__FUNCTION__);
109
110 if (!descriptor->should_resize(target_width, target_height)) {
111 // No resizing requested. Just decode & rasterize the image.
112 sk_sp<SkImage> image = descriptor->image();
113 return image ? image->makeRasterImage() : nullptr;
114 }
115
116 const SkISize source_dimensions = descriptor->image_info().dimensions();
117 const SkISize resized_dimensions = {static_cast<int32_t>(target_width),
118 static_cast<int32_t>(target_height)};
119
120 auto decode_dimensions = descriptor->get_scaled_dimensions(
121 std::max(static_cast<float>(resized_dimensions.width()) /
122 source_dimensions.width(),
123 static_cast<float>(resized_dimensions.height()) /
124 source_dimensions.height()));
125
126 // If the codec supports efficient sub-pixel decoding, decoded at a resolution
127 // close to the target resolution before resizing.
128 if (decode_dimensions != source_dimensions) {
129 auto scaled_image_info =
130 descriptor->image_info().makeDimensions(decode_dimensions);
131
132 SkBitmap scaled_bitmap;
133 if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
134 FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
135 << scaled_image_info.computeMinByteSize() << "B";
136 return nullptr;
137 }
138
139 const auto& pixmap = scaled_bitmap.pixmap();
140 if (descriptor->get_pixels(pixmap)) {
141 // Marking this as immutable makes the MakeFromBitmap call share
142 // the pixels instead of copying.
143 scaled_bitmap.setImmutable();
144
145 auto decoded_image = SkImages::RasterFromBitmap(scaled_bitmap);
146 FML_DCHECK(decoded_image);
147 if (!decoded_image) {
149 << "Could not create a scaled image from a scaled bitmap.";
150 return nullptr;
151 }
152 return ResizeRasterImage(decoded_image, resized_dimensions, flow);
153 }
154 }
155
156 auto image = descriptor->image();
157 if (!image) {
158 return nullptr;
159 }
160
161 return ResizeRasterImage(image, resized_dimensions, flow);
162}
163
166 const fml::WeakPtr<IOManager>& io_manager,
167 const fml::tracing::TraceFlow& flow) {
168 TRACE_EVENT0("flutter", __FUNCTION__);
169 flow.Step(__FUNCTION__);
170
171 // Should not already be a texture image because that is the entire point of
172 // the this method.
174
175 if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) {
177 << "Could not acquire context of release queue for texture upload.";
178 return {};
179 }
180
181 SkPixmap pixmap;
182 if (!image->peekPixels(&pixmap)) {
183 FML_LOG(ERROR) << "Could not peek pixels of image for texture upload.";
184 return {};
185 }
186
188 io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
190 .SetIfTrue([&result, &pixmap, &image] {
193 pixmap,
194 [](const void* pixels, SkImages::ReleaseContext context) {
195 SkSafeUnref(static_cast<SkImage*>(context));
196 },
197 image.get());
198 result = {std::move(texture_image), nullptr};
199 })
200 .SetIfFalse([&result, context = io_manager->GetResourceContext(),
201 &pixmap, queue = io_manager->GetSkiaUnrefQueue()] {
202 TRACE_EVENT0("flutter", "MakeCrossContextImageFromPixmap");
203 sk_sp<SkImage> texture_image =
205 context.get(), // context
206 pixmap, // pixmap
207 true, // buildMips,
208 true // limitToMaxTextureSize
209 );
210 if (!texture_image) {
211 FML_LOG(ERROR) << "Could not make x-context image.";
212 result = {};
213 } else {
214 result = {std::move(texture_image), queue};
215 }
216 }));
217
218 return result;
219}
220
221// |ImageDecoder|
223 uint32_t target_width,
224 uint32_t target_height,
225 const ImageResult& callback) {
226 TRACE_EVENT0("flutter", __FUNCTION__);
227 fml::tracing::TraceFlow flow(__FUNCTION__);
228
229 // ImageDescriptors have Dart peers that must be collected on the UI thread.
230 // However, closures in MakeCopyable below capture the descriptor. The
231 // captures of copyable closures may be collected on any of the thread
232 // participating in task execution.
233 //
234 // To avoid this issue, we resort to manually reference counting the
235 // descriptor. Since all task flows invoke the `result` callback, the raw
236 // descriptor is retained in the beginning and released in the `result`
237 // callback.
238 //
239 // `ImageDecoder::Decode` itself is invoked on the UI thread, so the
240 // collection of the smart pointer from which we obtained the raw descriptor
241 // is fine in this scope.
242 auto raw_descriptor = descriptor_ref_ptr.get();
243 raw_descriptor->AddRef();
244
247
248 // Always service the callback (and cleanup the descriptor) on the UI thread.
249 auto result =
250 [callback, raw_descriptor, ui_runner = runners_.GetUITaskRunner()](
252 ui_runner->PostTask(fml::MakeCopyable(
253 [callback, raw_descriptor, image = std::move(image),
254 flow = std::move(flow)]() mutable {
255 // We are going to terminate the trace flow here. Flows cannot
256 // terminate without a base trace. Add one explicitly.
257 TRACE_EVENT0("flutter", "ImageDecodeCallback");
258 flow.End();
259 callback(DlImageGPU::Make(std::move(image)), {});
260 raw_descriptor->Release();
261 }));
262 };
263
264 if (!raw_descriptor->data() || raw_descriptor->data()->size() == 0) {
265 result({}, std::move(flow));
266 return;
267 }
268
269 concurrent_task_runner_->PostTask(
270 fml::MakeCopyable([raw_descriptor, //
271 io_manager = io_manager_, //
272 io_runner = runners_.GetIOTaskRunner(), //
273 result, //
274 target_width = target_width, //
275 target_height = target_height, //
276 flow = std::move(flow) //
277 ]() mutable {
278 // Step 1: Decompress the image.
279 // On Worker.
280
281 auto decompressed = raw_descriptor->is_compressed()
282 ? ImageFromCompressedData(raw_descriptor, //
283 target_width, //
284 target_height, //
285 flow)
286 : ImageFromDecompressedData(raw_descriptor, //
287 target_width, //
288 target_height, //
289 flow);
290
291 if (!decompressed) {
292 FML_DLOG(ERROR) << "Could not decompress image.";
293 result({}, std::move(flow));
294 return;
295 }
296
297 // Step 2: Update the image to the GPU.
298 // On IO Thread.
299
300 io_runner->PostTask(fml::MakeCopyable([io_manager, decompressed, result,
301 flow =
302 std::move(flow)]() mutable {
303 if (!io_manager) {
304 FML_DLOG(ERROR) << "Could not acquire IO manager.";
305 result({}, std::move(flow));
306 return;
307 }
308
309 // If the IO manager does not have a resource context, the caller
310 // might not have set one or a software backend could be in use.
311 // Either way, just return the image as-is.
312 if (!io_manager->GetResourceContext()) {
313 result({std::move(decompressed), io_manager->GetSkiaUnrefQueue()},
314 std::move(flow));
315 return;
316 }
317
318 auto uploaded =
319 UploadRasterImage(std::move(decompressed), io_manager, flow);
320
321 if (!uploaded.skia_object()) {
322 FML_DLOG(ERROR) << "Could not upload image to the GPU.";
323 result({}, std::move(flow));
324 return;
325 }
326
327 // Finally, all done.
328 result(std::move(uploaded), std::move(flow));
329 }));
330 }));
331}
332
333} // namespace flutter
334
335#endif // !SLIMPELLER
static void SkSafeUnref(T *obj)
Definition: SkRefCnt.h:149
static T * SkSafeRef(T *obj)
Definition: SkRefCnt.h:140
void setImmutable()
Definition: SkBitmap.cpp:400
const SkPixmap & pixmap() const
Definition: SkBitmap.h:133
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:271
sk_sp< SkImage > makeRasterImage(GrDirectContext *, CachingHint cachingHint=kDisallow_CachingHint) const
Definition: SkImage.cpp:267
const SkImageInfo & imageInfo() const
Definition: SkImage.h:279
SkISize dimensions() const
Definition: SkImage.h:297
bool peekPixels(SkPixmap *pixmap) const
Definition: SkImage.cpp:34
virtual bool isTextureBacked() const =0
@ kDisallow_CachingHint
disallows internally caching decoded and copied pixels
Definition: SkImage.h:465
bool scalePixels(const SkPixmap &dst, const SkSamplingOptions &, CachingHint cachingHint=kAllow_CachingHint) const
Definition: SkImage.cpp:127
static sk_sp< DlImageGPU > Make(SkiaGPUObject< SkImage > image)
ImageDecoderSkia(const TaskRunners &runners, std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner, fml::WeakPtr< IOManager > io_manager)
void Decode(fml::RefPtr< ImageDescriptor > descriptor, uint32_t target_width, uint32_t target_height, const ImageResult &result) override
static sk_sp< SkImage > ImageFromCompressedData(ImageDescriptor *descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
std::function< void(sk_sp< DlImage >, std::string)> ImageResult
Definition: image_decoder.h:35
std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner_
Definition: image_decoder.h:51
fml::WeakPtr< IOManager > io_manager_
Definition: image_decoder.h:52
Creates an image descriptor for encoded or decoded image data, describing the width,...
sk_sp< SkImage > image() const
const SkImageInfo & image_info() const
The orientation corrected image info for this image.
SkISize get_scaled_dimensions(float scale)
Gets the scaled dimensions of this image, if backed by an ImageGenerator that can perform efficient s...
int row_bytes() const
The byte length of the first row of the image. Defaults to width() * 4.
bool should_resize(int target_width, int target_height) const
Whether the given target_width or target_height differ from width() and height() respectively.
bool get_pixels(const SkPixmap &pixmap) const
Gets pixels for this image transformed based on the EXIF orientation tag, if applicable.
sk_sp< SkData > data() const
The underlying buffer for this image.
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
Definition: task_runners.cc:38
T * get() const
Definition: ref_ptr.h:116
virtual bool RunsTasksOnCurrentThread()
Definition: task_runner.cc:43
void End(const char *label=nullptr)
Definition: trace_event.h:491
void Step(const char *label=nullptr) const
Definition: trace_event.h:484
T * get() const
Definition: SkRefCnt.h:303
VkQueue queue
Definition: main.cc:55
if(end==-1)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_DCHECK(condition)
Definition: logging.h:103
static float max(float r, float g, float b)
Definition: hsl.cpp:49
SK_API sk_sp< SkImage > RasterFromData(const SkImageInfo &info, sk_sp< SkData > pixels, size_t rowBytes)
SK_API sk_sp< SkImage > RasterFromPixmap(const SkPixmap &pixmap, RasterReleaseProc rasterReleaseProc, ReleaseContext releaseContext)
SK_API sk_sp< SkImage > CrossContextTextureFromPixmap(GrDirectContext *context, const SkPixmap &pixmap, bool buildMips, bool limitToMaxTextureSize=false)
SK_API sk_sp< SkImage > RasterFromBitmap(const SkBitmap &bitmap)
void * ReleaseContext
Definition: SkImage.h:50
sk_sp< const SkImage > image
Definition: SkRecords.h:269
static SkiaGPUObject< SkImage > UploadRasterImage(sk_sp< SkImage > image, const fml::WeakPtr< IOManager > &io_manager, const fml::tracing::TraceFlow &flow)
static sk_sp< SkImage > ResizeRasterImage(const sk_sp< SkImage > &image, const SkISize &resized_dimensions, const fml::tracing::TraceFlow &flow)
static sk_sp< SkImage > ImageFromDecompressedData(ImageDescriptor *descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
Definition: ref_ptr.h:256
Definition: SkSize.h:16
bool isEmpty() const
Definition: SkSize.h:31
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
SkImageInfo makeDimensions(SkISize newSize) const
Definition: SkImageInfo.h:454
SkISize dimensions() const
Definition: SkImageInfo.h:421
Represents the 2 code paths available when calling |SyncSwitch::Execute|.
Definition: sync_switch.h:35
#define ERROR(message)
Definition: elf_loader.cc:260
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131