Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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#if !SLIMPELLER
6
8
9#include <algorithm>
10
11#include "flutter/fml/logging.h"
14#include "third_party/skia/include/core/SkBitmap.h"
15#include "third_party/skia/include/core/SkImage.h"
16#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.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
30static sk_sp<SkImage> ResizeRasterImage(const sk_sp<SkImage>& image,
31 const SkISize& resized_dimensions,
32 const fml::tracing::TraceFlow& flow) {
33 FML_DCHECK(!image->isTextureBacked());
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(nullptr);
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(),
59 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
60 SkImage::kDisallow_CachingHint)) {
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
78static sk_sp<SkImage> ImageFromDecompressedData(
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__);
85 auto image = SkImages::RasterFromData(
87 descriptor->data(), descriptor->row_bytes());
88
89 if (!image) {
90 FML_LOG(ERROR) << "Could not create image from decompressed bytes.";
91 return nullptr;
92 }
93
94 if (!target_width && !target_height) {
95 // No resizing requested. Just rasterize the image.
96 return image->makeRasterImage(nullptr);
97 }
98
99 return ResizeRasterImage(image, SkISize::Make(target_width, target_height),
100 flow);
101}
102
104 ImageDescriptor* descriptor,
105 uint32_t target_width,
106 uint32_t target_height,
107 const fml::tracing::TraceFlow& flow) {
108 TRACE_EVENT0("flutter", __FUNCTION__);
109 flow.Step(__FUNCTION__);
110
111 if (!descriptor->should_resize(target_width, target_height)) {
112 // No resizing requested. Just decode & rasterize the image.
113 sk_sp<SkImage> image = descriptor->image();
114 return image ? image->makeRasterImage(nullptr) : nullptr;
115 }
116
117 const SkImageInfo image_info =
119 const SkISize source_dimensions = image_info.dimensions();
120 const SkISize resized_dimensions = {static_cast<int32_t>(target_width),
121 static_cast<int32_t>(target_height)};
122
123 auto decode_dimensions = descriptor->get_scaled_dimensions(
124 std::max(static_cast<float>(resized_dimensions.width()) /
125 source_dimensions.width(),
126 static_cast<float>(resized_dimensions.height()) /
127 source_dimensions.height()));
128
129 // If the codec supports efficient sub-pixel decoding, decoded at a resolution
130 // close to the target resolution before resizing.
131 if (decode_dimensions != source_dimensions) {
132 auto scaled_image_info = image_info.makeDimensions(decode_dimensions);
133
134 SkBitmap scaled_bitmap;
135 if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
136 FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
137 << scaled_image_info.computeMinByteSize() << "B";
138 return nullptr;
139 }
140
141 const auto& pixmap = scaled_bitmap.pixmap();
142 if (descriptor->get_pixels(pixmap)) {
143 // Marking this as immutable makes the MakeFromBitmap call share
144 // the pixels instead of copying.
145 scaled_bitmap.setImmutable();
146
147 auto decoded_image = SkImages::RasterFromBitmap(scaled_bitmap);
148 FML_DCHECK(decoded_image);
149 if (!decoded_image) {
150 FML_LOG(ERROR)
151 << "Could not create a scaled image from a scaled bitmap.";
152 return nullptr;
153 }
154 return ResizeRasterImage(decoded_image, resized_dimensions, flow);
155 }
156 }
157
158 auto image = descriptor->image();
159 if (!image) {
160 return nullptr;
161 }
162
163 return ResizeRasterImage(image, resized_dimensions, flow);
164}
165
167 sk_sp<SkImage> image,
168 const fml::WeakPtr<IOManager>& io_manager,
169 const fml::tracing::TraceFlow& flow) {
170 TRACE_EVENT0("flutter", __FUNCTION__);
171 flow.Step(__FUNCTION__);
172
173 // Should not already be a texture image because that is the entire point of
174 // the this method.
175 FML_DCHECK(!image->isTextureBacked());
176
177 if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) {
178 FML_LOG(ERROR)
179 << "Could not acquire context of release queue for texture upload.";
180 return {};
181 }
182
183 SkPixmap pixmap;
184 if (!image->peekPixels(&pixmap)) {
185 FML_LOG(ERROR) << "Could not peek pixels of image for texture upload.";
186 return {};
187 }
188
190 io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
192 .SetIfTrue([&result, &pixmap, &image] {
193 SkSafeRef(image.get());
194 sk_sp<SkImage> texture_image = SkImages::RasterFromPixmap(
195 pixmap,
196 [](const void* pixels, SkImages::ReleaseContext context) {
197 SkSafeUnref(static_cast<SkImage*>(context));
198 },
199 image.get());
200 result = {std::move(texture_image), nullptr};
201 })
202 .SetIfFalse([&result, context = io_manager->GetResourceContext(),
203 &pixmap, queue = io_manager->GetSkiaUnrefQueue()] {
204 TRACE_EVENT0("flutter", "MakeCrossContextImageFromPixmap");
205 sk_sp<SkImage> texture_image =
206 SkImages::CrossContextTextureFromPixmap(
207 context.get(), // context
208 pixmap, // pixmap
209 true, // buildMips,
210 true // limitToMaxTextureSize
211 );
212 if (!texture_image) {
213 FML_LOG(ERROR) << "Could not make x-context image.";
214 result = {};
215 } else {
216 result = {std::move(texture_image), queue};
217 }
218 }));
219
220 return result;
221}
222
223// |ImageDecoder|
225 const ImageDecoder::Options& options,
226 const ImageResult& callback) {
227 TRACE_EVENT0("flutter", __FUNCTION__);
228 fml::tracing::TraceFlow flow(__FUNCTION__);
229
230 // ImageDescriptors have Dart peers that must be collected on the UI thread.
231 // However, closures in MakeCopyable below capture the descriptor. The
232 // captures of copyable closures may be collected on any of the thread
233 // participating in task execution.
234 //
235 // To avoid this issue, we resort to manually reference counting the
236 // descriptor. Since all task flows invoke the `result` callback, the raw
237 // descriptor is retained in the beginning and released in the `result`
238 // callback.
239 //
240 // `ImageDecoder::Decode` itself is invoked on the UI thread, so the
241 // collection of the smart pointer from which we obtained the raw descriptor
242 // is fine in this scope.
243 auto raw_descriptor = descriptor_ref_ptr.get();
244 raw_descriptor->AddRef();
245
248
249 // Always service the callback (and cleanup the descriptor) on the UI thread.
250 auto result =
251 [callback, raw_descriptor, ui_runner = runners_.GetUITaskRunner()](
253 ui_runner->PostTask(fml::MakeCopyable(
254 [callback, raw_descriptor, image = std::move(image),
255 flow = std::move(flow)]() mutable {
256 // We are going to terminate the trace flow here. Flows cannot
257 // terminate without a base trace. Add one explicitly.
258 TRACE_EVENT0("flutter", "ImageDecodeCallback");
259 flow.End();
260 callback(DlImageGPU::Make(std::move(image)), {});
261 raw_descriptor->Release();
262 }));
263 };
264
265 if (!raw_descriptor->data() || raw_descriptor->data()->size() == 0) {
266 result({}, std::move(flow));
267 return;
268 }
269
270 concurrent_task_runner_->PostTask(
271 fml::MakeCopyable([raw_descriptor, //
272 io_manager = io_manager_, //
273 io_runner = runners_.GetIOTaskRunner(), //
274 result, //
275 target_width = options.target_width, //
276 target_height = options.target_height, //
277 flow = std::move(flow) //
278 ]() mutable {
279 // Step 1: Decompress the image.
280 // On Worker.
281
282 auto decompressed = raw_descriptor->is_compressed()
283 ? ImageFromCompressedData(raw_descriptor, //
284 target_width, //
285 target_height, //
286 flow)
287 : ImageFromDecompressedData(raw_descriptor, //
288 target_width, //
289 target_height, //
290 flow);
291
292 if (!decompressed) {
293 FML_DLOG(ERROR) << "Could not decompress image.";
294 result({}, std::move(flow));
295 return;
296 }
297
298 // Step 2: Update the image to the GPU.
299 // On IO Thread.
300
301 io_runner->PostTask(fml::MakeCopyable([io_manager, decompressed, result,
302 flow =
303 std::move(flow)]() mutable {
304 if (!io_manager) {
305 FML_DLOG(ERROR) << "Could not acquire IO manager.";
306 result({}, std::move(flow));
307 return;
308 }
309
310 // If the IO manager does not have a resource context, the caller
311 // might not have set one or a software backend could be in use.
312 // Either way, just return the image as-is.
313 if (!io_manager->GetResourceContext()) {
314 result({std::move(decompressed), io_manager->GetSkiaUnrefQueue()},
315 std::move(flow));
316 return;
317 }
318
319 auto uploaded =
320 UploadRasterImage(std::move(decompressed), io_manager, flow);
321
322 if (!uploaded.skia_object()) {
323 FML_DLOG(ERROR) << "Could not upload image to the GPU.";
324 result({}, std::move(flow));
325 return;
326 }
327
328 // Finally, all done.
329 result(std::move(uploaded), std::move(flow));
330 }));
331 }));
332}
333
334} // namespace flutter
335
336#endif // !SLIMPELLER
static sk_sp< DlImageGPU > Make(SkiaGPUObject< SkImage > image)
std::function< void(sk_sp< DlImage >, std::string)> ImageResult
std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner_
fml::WeakPtr< IOManager > io_manager_
ImageDecoderSkia(const TaskRunners &runners, std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner, fml::WeakPtr< IOManager > io_manager)
static sk_sp< SkImage > ImageFromCompressedData(ImageDescriptor *descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow &flow)
void Decode(fml::RefPtr< ImageDescriptor > descriptor, const Options &options, const ImageResult &result) override
Creates an image descriptor for encoded or decoded image data, describing the width,...
sk_sp< SkImage > image() const
SkISize get_scaled_dimensions(float scale)
Gets the scaled dimensions of this image, if backed by an ImageGenerator that can perform efficient s...
static SkImageInfo ToSkImageInfo(const ImageInfo &image_info)
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.
const ImageInfo & image_info() const
The orientation corrected image info for this image.
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:117
virtual bool RunsTasksOnCurrentThread()
void End(const char *label=nullptr)
void Step(const char *label=nullptr) const
FlutterVulkanImage * image
VkQueue queue
Definition main.cc:71
if(end==-1)
FlutterDesktopBinaryReply callback
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_LOG(severity)
Definition logging.h:101
#define FML_DCHECK(condition)
Definition logging.h:122
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:261
Represents the 2 code paths available when calling |SyncSwitchExecute|.
Definition sync_switch.h:35
#define TRACE_EVENT0(category_group, name)