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"
12#include "third_party/skia/include/core/SkBitmap.h"
13#include "third_party/skia/include/core/SkImage.h"
14#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
15
16namespace flutter {
17
18ImageDecoderSkia::ImageDecoderSkia(
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
26ImageDecoderSkia::~ImageDecoderSkia() = default;
27
28static sk_sp<SkImage> ResizeRasterImage(const sk_sp<SkImage>& image,
29 const SkISize& resized_dimensions,
30 const fml::tracing::TraceFlow& flow) {
31 FML_DCHECK(!image->isTextureBacked());
32
33 TRACE_EVENT0("flutter", __FUNCTION__);
34 flow.Step(label: __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(newSize: resized_dimensions);
47
48 SkBitmap scaled_bitmap;
49 if (!scaled_bitmap.tryAllocPixels(info: 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 dst: scaled_bitmap.pixmap(),
57 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
58 cachingHint: SkImage::kDisallow_CachingHint)) {
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(bitmap: 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
76static sk_sp<SkImage> ImageFromDecompressedData(
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(label: __FUNCTION__);
83 auto image = SkImages::RasterFromData(
84 info: descriptor->image_info(), pixels: descriptor->data(), rowBytes: 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, resized_dimensions: SkISize::Make(w: target_width, h: target_height),
97 flow);
98}
99
100sk_sp<SkImage> ImageDecoderSkia::ImageFromCompressedData(
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(label: __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 = {.fWidth: static_cast<int32_t>(target_width),
116 .fHeight: static_cast<int32_t>(target_height)};
117
118 auto decode_dimensions = descriptor->get_scaled_dimensions(
119 scale: std::max(a: static_cast<float>(resized_dimensions.width()) /
120 source_dimensions.width(),
121 b: 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(newSize: decode_dimensions);
129
130 SkBitmap scaled_bitmap;
131 if (!scaled_bitmap.tryAllocPixels(info: 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(bitmap: scaled_bitmap);
144 FML_DCHECK(decoded_image);
145 if (!decoded_image) {
146 FML_LOG(ERROR)
147 << "Could not create a scaled image from a scaled bitmap.";
148 return nullptr;
149 }
150 return ResizeRasterImage(image: 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
162static SkiaGPUObject<SkImage> UploadRasterImage(
163 sk_sp<SkImage> image,
164 const fml::WeakPtr<IOManager>& io_manager,
165 const fml::tracing::TraceFlow& flow) {
166 TRACE_EVENT0("flutter", __FUNCTION__);
167 flow.Step(label: __FUNCTION__);
168
169 // Should not already be a texture image because that is the entire point of
170 // the this method.
171 FML_DCHECK(!image->isTextureBacked());
172
173 if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) {
174 FML_LOG(ERROR)
175 << "Could not acquire context of release queue for texture upload.";
176 return {};
177 }
178
179 SkPixmap pixmap;
180 if (!image->peekPixels(pixmap: &pixmap)) {
181 FML_LOG(ERROR) << "Could not peek pixels of image for texture upload.";
182 return {};
183 }
184
185 SkiaGPUObject<SkImage> result;
186 io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
187 handlers: fml::SyncSwitch::Handlers()
188 .SetIfTrue([&result, &pixmap, &image] {
189 SkSafeRef(obj: image.get());
190 sk_sp<SkImage> texture_image = SkImages::RasterFromPixmap(
191 pixmap,
192 rasterReleaseProc: [](const void* pixels, SkImages::ReleaseContext context) {
193 SkSafeUnref(obj: static_cast<SkImage*>(context));
194 },
195 releaseContext: 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 =
202 SkImages::CrossContextTextureFromPixmap(
203 context: context.get(), // context
204 pixmap, // pixmap
205 buildMips: true, // buildMips,
206 limitToMaxTextureSize: 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|
220void ImageDecoderSkia::Decode(fml::RefPtr<ImageDescriptor> descriptor_ref_ptr,
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
243 FML_DCHECK(callback);
244 FML_DCHECK(runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
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()](
249 SkiaGPUObject<SkImage> image, fml::tracing::TraceFlow flow) {
250 ui_runner->PostTask(task: fml::MakeCopyable(
251 lambda: [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(image: 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 task: fml::MakeCopyable(lambda: [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(descriptor: raw_descriptor, //
281 target_width, //
282 target_height, //
283 flow)
284 : ImageFromDecompressedData(descriptor: 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(task: fml::MakeCopyable(lambda: [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(image: 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
332

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

source code of flutter_engine/flutter/lib/ui/painting/image_decoder_skia.cc