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