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_impeller.h"
6
7#include <memory>
8
9#include "flutter/fml/closure.h"
10#include "flutter/fml/make_copyable.h"
11#include "flutter/fml/trace_event.h"
12#include "flutter/impeller/core/allocator.h"
13#include "flutter/impeller/core/texture.h"
14#include "flutter/impeller/display_list/dl_image_impeller.h"
15#include "flutter/impeller/renderer/command_buffer.h"
16#include "flutter/impeller/renderer/context.h"
17#include "flutter/lib/ui/painting/image_decoder_skia.h"
18#include "impeller/base/strings.h"
19#include "impeller/display_list/skia_conversions.h"
20#include "impeller/geometry/size.h"
21#include "third_party/skia/include/core/SkAlphaType.h"
22#include "third_party/skia/include/core/SkBitmap.h"
23#include "third_party/skia/include/core/SkColorSpace.h"
24#include "third_party/skia/include/core/SkColorType.h"
25#include "third_party/skia/include/core/SkImageInfo.h"
26#include "third_party/skia/include/core/SkMallocPixelRef.h"
27#include "third_party/skia/include/core/SkPixelRef.h"
28#include "third_party/skia/include/core/SkPixmap.h"
29#include "third_party/skia/include/core/SkPoint.h"
30#include "third_party/skia/include/core/SkSamplingOptions.h"
31#include "third_party/skia/include/core/SkSize.h"
32
33namespace flutter {
34
35class MallocDeviceBuffer : public impeller::DeviceBuffer {
36 public:
37 explicit MallocDeviceBuffer(impeller::DeviceBufferDescriptor desc)
38 : impeller::DeviceBuffer(desc) {
39 data_ = static_cast<uint8_t*>(malloc(size: desc.size));
40 }
41
42 ~MallocDeviceBuffer() override { free(ptr: data_); }
43
44 bool SetLabel(const std::string& label) override { return true; }
45
46 bool SetLabel(const std::string& label, impeller::Range range) override {
47 return true;
48 }
49
50 uint8_t* OnGetContents() const override { return data_; }
51
52 bool OnCopyHostBuffer(const uint8_t* source,
53 impeller::Range source_range,
54 size_t offset) override {
55 memcpy(dest: data_ + offset, src: source + source_range.offset, n: source_range.length);
56 return true;
57 }
58
59 private:
60 uint8_t* data_;
61
62 FML_DISALLOW_COPY_AND_ASSIGN(MallocDeviceBuffer);
63};
64
65#ifdef FML_OS_ANDROID
66static constexpr bool kShouldUseMallocDeviceBuffer = true;
67#else
68static constexpr bool kShouldUseMallocDeviceBuffer = false;
69#endif // FML_OS_ANDROID
70
71namespace {
72/**
73 * Loads the gamut as a set of three points (triangle).
74 */
75void LoadGamut(SkPoint abc[3], const skcms_Matrix3x3& xyz) {
76 // rx = rX / (rX + rY + rZ)
77 // ry = rY / (rX + rY + rZ)
78 // gx, gy, bx, and gy are calculated similarly.
79 for (int index = 0; index < 3; index++) {
80 float sum = xyz.vals[index][0] + xyz.vals[index][1] + xyz.vals[index][2];
81 abc[index].fX = xyz.vals[index][0] / sum;
82 abc[index].fY = xyz.vals[index][1] / sum;
83 }
84}
85
86/**
87 * Calculates the area of the triangular gamut.
88 */
89float CalculateArea(SkPoint abc[3]) {
90 const SkPoint& a = abc[0];
91 const SkPoint& b = abc[1];
92 const SkPoint& c = abc[2];
93 return 0.5f * fabsf(x: a.fX * b.fY + b.fX * c.fY - a.fX * c.fY - c.fX * b.fY -
94 b.fX * a.fY);
95}
96
97// Note: This was calculated from SkColorSpace::MakeSRGB().
98static constexpr float kSrgbGamutArea = 0.0982f;
99
100// Source:
101// https://source.chromium.org/chromium/_/skia/skia.git/+/393fb1ec80f41d8ad7d104921b6920e69749fda1:src/codec/SkAndroidCodec.cpp;l=67;drc=46572b4d445f41943059d0e377afc6d6748cd5ca;bpv=1;bpt=0
102bool IsWideGamut(const SkColorSpace* color_space) {
103 if (!color_space) {
104 return false;
105 }
106 skcms_Matrix3x3 xyzd50;
107 color_space->toXYZD50(toXYZD50: &xyzd50);
108 SkPoint rgb[3];
109 LoadGamut(abc: rgb, xyz: xyzd50);
110 float area = CalculateArea(abc: rgb);
111 return area > kSrgbGamutArea;
112}
113} // namespace
114
115ImageDecoderImpeller::ImageDecoderImpeller(
116 const TaskRunners& runners,
117 std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
118 const fml::WeakPtr<IOManager>& io_manager,
119 bool supports_wide_gamut,
120 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch)
121 : ImageDecoder(runners, std::move(concurrent_task_runner), io_manager),
122 supports_wide_gamut_(supports_wide_gamut),
123 gpu_disabled_switch_(gpu_disabled_switch) {
124 std::promise<std::shared_ptr<impeller::Context>> context_promise;
125 context_ = context_promise.get_future();
126 runners_.GetIOTaskRunner()->PostTask(task: fml::MakeCopyable(
127 lambda: [promise = std::move(context_promise), io_manager]() mutable {
128 promise.set_value(io_manager ? io_manager->GetImpellerContext()
129 : nullptr);
130 }));
131}
132
133ImageDecoderImpeller::~ImageDecoderImpeller() = default;
134
135static SkColorType ChooseCompatibleColorType(SkColorType type) {
136 switch (type) {
137 case kRGBA_F32_SkColorType:
138 return kRGBA_F16_SkColorType;
139 default:
140 return kRGBA_8888_SkColorType;
141 }
142}
143
144static SkAlphaType ChooseCompatibleAlphaType(SkAlphaType type) {
145 return type;
146}
147
148DecompressResult ImageDecoderImpeller::DecompressTexture(
149 ImageDescriptor* descriptor,
150 SkISize target_size,
151 impeller::ISize max_texture_size,
152 bool supports_wide_gamut,
153 const std::shared_ptr<impeller::Allocator>& allocator) {
154 TRACE_EVENT0("impeller", __FUNCTION__);
155 if (!descriptor) {
156 std::string decode_error("Invalid descriptor (should never happen)");
157 FML_DLOG(ERROR) << decode_error;
158 return DecompressResult{.decode_error = decode_error};
159 }
160
161 target_size.set(w: std::min(a: static_cast<int32_t>(max_texture_size.width),
162 b: target_size.width()),
163 h: std::min(a: static_cast<int32_t>(max_texture_size.height),
164 b: target_size.height()));
165
166 const SkISize source_size = descriptor->image_info().dimensions();
167 auto decode_size = source_size;
168 if (descriptor->is_compressed()) {
169 decode_size = descriptor->get_scaled_dimensions(scale: std::max(
170 a: static_cast<float>(target_size.width()) / source_size.width(),
171 b: static_cast<float>(target_size.height()) / source_size.height()));
172 }
173
174 //----------------------------------------------------------------------------
175 /// 1. Decode the image.
176 ///
177
178 const auto base_image_info = descriptor->image_info();
179 const bool is_wide_gamut =
180 supports_wide_gamut ? IsWideGamut(color_space: base_image_info.colorSpace()) : false;
181 SkAlphaType alpha_type =
182 ChooseCompatibleAlphaType(type: base_image_info.alphaType());
183 SkImageInfo image_info;
184 if (is_wide_gamut) {
185 SkColorType color_type = alpha_type == SkAlphaType::kOpaque_SkAlphaType
186 ? kBGR_101010x_XR_SkColorType
187 : kRGBA_F16_SkColorType;
188 image_info =
189 base_image_info.makeWH(newWidth: decode_size.width(), newHeight: decode_size.height())
190 .makeColorType(newColorType: color_type)
191 .makeAlphaType(newAlphaType: alpha_type)
192 .makeColorSpace(cs: SkColorSpace::MakeSRGB());
193 } else {
194 image_info =
195 base_image_info.makeWH(newWidth: decode_size.width(), newHeight: decode_size.height())
196 .makeColorType(
197 newColorType: ChooseCompatibleColorType(type: base_image_info.colorType()))
198 .makeAlphaType(newAlphaType: alpha_type);
199 }
200
201 const auto pixel_format =
202 impeller::skia_conversions::ToPixelFormat(type: image_info.colorType());
203 if (!pixel_format.has_value()) {
204 std::string decode_error(impeller::SPrintF(
205 format: "Codec pixel format is not supported (SkColorType=%d)",
206 image_info.colorType()));
207 FML_DLOG(ERROR) << decode_error;
208 return DecompressResult{.decode_error = decode_error};
209 }
210
211 auto bitmap = std::make_shared<SkBitmap>();
212 bitmap->setInfo(imageInfo: image_info);
213 auto bitmap_allocator = std::make_shared<ImpellerAllocator>(args: allocator);
214
215 if (descriptor->is_compressed()) {
216 if (!bitmap->tryAllocPixels(allocator: bitmap_allocator.get())) {
217 std::string decode_error(
218 "Could not allocate intermediate for image decompression.");
219 FML_DLOG(ERROR) << decode_error;
220 return DecompressResult{.decode_error = decode_error};
221 }
222 // Decode the image into the image generator's closest supported size.
223 if (!descriptor->get_pixels(pixmap: bitmap->pixmap())) {
224 std::string decode_error("Could not decompress image.");
225 FML_DLOG(ERROR) << decode_error;
226 return DecompressResult{.decode_error = decode_error};
227 }
228 } else {
229 auto temp_bitmap = std::make_shared<SkBitmap>();
230 temp_bitmap->setInfo(imageInfo: base_image_info);
231 auto pixel_ref = SkMallocPixelRef::MakeWithData(
232 base_image_info, rowBytes: descriptor->row_bytes(), data: descriptor->data());
233 temp_bitmap->setPixelRef(pixelRef: pixel_ref, dx: 0, dy: 0);
234
235 if (!bitmap->tryAllocPixels(allocator: bitmap_allocator.get())) {
236 std::string decode_error(
237 "Could not allocate intermediate for pixel conversion.");
238 FML_DLOG(ERROR) << decode_error;
239 return DecompressResult{.decode_error = decode_error};
240 }
241 temp_bitmap->readPixels(dst: bitmap->pixmap());
242 bitmap->setImmutable();
243 }
244
245 if (bitmap->dimensions() == target_size) {
246 auto buffer = bitmap_allocator->GetDeviceBuffer();
247 if (!buffer) {
248 return DecompressResult{.decode_error = "Unable to get device buffer"};
249 }
250 return DecompressResult{.device_buffer = buffer,
251 .sk_bitmap = bitmap,
252 .image_info = bitmap->info()};
253 }
254
255 //----------------------------------------------------------------------------
256 /// 2. If the decoded image isn't the requested target size, resize it.
257 ///
258
259 TRACE_EVENT0("impeller", "DecodeScale");
260 const auto scaled_image_info = image_info.makeDimensions(newSize: target_size);
261
262 auto scaled_bitmap = std::make_shared<SkBitmap>();
263 auto scaled_allocator = std::make_shared<ImpellerAllocator>(args: allocator);
264 scaled_bitmap->setInfo(imageInfo: scaled_image_info);
265 if (!scaled_bitmap->tryAllocPixels(allocator: scaled_allocator.get())) {
266 std::string decode_error(
267 "Could not allocate scaled bitmap for image decompression.");
268 FML_DLOG(ERROR) << decode_error;
269 return DecompressResult{.decode_error = decode_error};
270 }
271 if (!bitmap->pixmap().scalePixels(
272 dst: scaled_bitmap->pixmap(),
273 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone))) {
274 FML_LOG(ERROR) << "Could not scale decoded bitmap data.";
275 }
276 scaled_bitmap->setImmutable();
277
278 auto buffer = scaled_allocator->GetDeviceBuffer();
279 if (!buffer) {
280 return DecompressResult{.decode_error = "Unable to get device buffer"};
281 }
282 return DecompressResult{.device_buffer = buffer,
283 .sk_bitmap = scaled_bitmap,
284 .image_info = scaled_bitmap->info()};
285}
286
287/// Only call this method if the GPU is available.
288static std::pair<sk_sp<DlImage>, std::string> UnsafeUploadTextureToPrivate(
289 const std::shared_ptr<impeller::Context>& context,
290 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
291 const SkImageInfo& image_info) {
292 const auto pixel_format =
293 impeller::skia_conversions::ToPixelFormat(type: image_info.colorType());
294 if (!pixel_format) {
295 std::string decode_error(impeller::SPrintF(
296 format: "Unsupported pixel format (SkColorType=%d)", image_info.colorType()));
297 FML_DLOG(ERROR) << decode_error;
298 return std::make_pair(t1: nullptr, t2&: decode_error);
299 }
300
301 impeller::TextureDescriptor texture_descriptor;
302 texture_descriptor.storage_mode = impeller::StorageMode::kDevicePrivate;
303 texture_descriptor.format = pixel_format.value();
304 texture_descriptor.size = {image_info.width(), image_info.height()};
305 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
306 texture_descriptor.compression_type = impeller::CompressionType::kLossy;
307
308 auto dest_texture =
309 context->GetResourceAllocator()->CreateTexture(desc: texture_descriptor);
310 if (!dest_texture) {
311 std::string decode_error("Could not create Impeller texture.");
312 FML_DLOG(ERROR) << decode_error;
313 return std::make_pair(t1: nullptr, t2&: decode_error);
314 }
315
316 dest_texture->SetLabel(
317 impeller::SPrintF(format: "ui.Image(%p)", dest_texture.get()).c_str());
318
319 auto command_buffer = context->CreateCommandBuffer();
320 if (!command_buffer) {
321 std::string decode_error(
322 "Could not create command buffer for mipmap generation.");
323 FML_DLOG(ERROR) << decode_error;
324 return std::make_pair(t1: nullptr, t2&: decode_error);
325 }
326 command_buffer->SetLabel("Mipmap Command Buffer");
327
328 auto blit_pass = command_buffer->CreateBlitPass();
329 if (!blit_pass) {
330 std::string decode_error(
331 "Could not create blit pass for mipmap generation.");
332 FML_DLOG(ERROR) << decode_error;
333 return std::make_pair(t1: nullptr, t2&: decode_error);
334 }
335 blit_pass->SetLabel("Mipmap Blit Pass");
336 blit_pass->AddCopy(source: buffer->AsBufferView(), destination: dest_texture);
337 if (texture_descriptor.size.MipCount() > 1) {
338 blit_pass->GenerateMipmap(texture: dest_texture);
339 }
340
341 blit_pass->EncodeCommands(transients_allocator: context->GetResourceAllocator());
342 if (!command_buffer->SubmitCommands()) {
343 std::string decode_error("Failed to submit blit pass command buffer.");
344 FML_DLOG(ERROR) << decode_error;
345 return std::make_pair(t1: nullptr, t2&: decode_error);
346 }
347
348 return std::make_pair(
349 t1: impeller::DlImageImpeller::Make(texture: std::move(dest_texture)), t2: std::string());
350}
351
352std::pair<sk_sp<DlImage>, std::string>
353ImageDecoderImpeller::UploadTextureToPrivate(
354 const std::shared_ptr<impeller::Context>& context,
355 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
356 const SkImageInfo& image_info,
357 const std::shared_ptr<SkBitmap>& bitmap,
358 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch) {
359 TRACE_EVENT0("impeller", __FUNCTION__);
360 if (!context) {
361 return std::make_pair(t1: nullptr, t2: "No Impeller context is available");
362 }
363 if (!buffer) {
364 return std::make_pair(t1: nullptr, t2: "No Impeller device buffer is available");
365 }
366
367 std::pair<sk_sp<DlImage>, std::string> result;
368 gpu_disabled_switch->Execute(
369 handlers: fml::SyncSwitch::Handlers()
370 .SetIfFalse([&result, context, buffer, image_info] {
371 result = UnsafeUploadTextureToPrivate(context, buffer, image_info);
372 })
373 .SetIfTrue([&result, context, bitmap, gpu_disabled_switch] {
374 // create_mips is false because we already know the GPU is disabled.
375 result =
376 UploadTextureToStorage(context, bitmap, gpu_disabled_switch,
377 storage_mode: impeller::StorageMode::kHostVisible,
378 /*create_mips=*/false);
379 }));
380 return result;
381}
382
383std::pair<sk_sp<DlImage>, std::string>
384ImageDecoderImpeller::UploadTextureToStorage(
385 const std::shared_ptr<impeller::Context>& context,
386 std::shared_ptr<SkBitmap> bitmap,
387 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch,
388 impeller::StorageMode storage_mode,
389 bool create_mips) {
390 TRACE_EVENT0("impeller", __FUNCTION__);
391 if (!context) {
392 return std::make_pair(t1: nullptr, t2: "No Impeller context is available");
393 }
394 if (!bitmap) {
395 return std::make_pair(t1: nullptr, t2: "No texture bitmap is available");
396 }
397 const auto image_info = bitmap->info();
398 const auto pixel_format =
399 impeller::skia_conversions::ToPixelFormat(type: image_info.colorType());
400 if (!pixel_format) {
401 std::string decode_error(impeller::SPrintF(
402 format: "Unsupported pixel format (SkColorType=%d)", image_info.colorType()));
403 FML_DLOG(ERROR) << decode_error;
404 return std::make_pair(t1: nullptr, t2&: decode_error);
405 }
406
407 impeller::TextureDescriptor texture_descriptor;
408 texture_descriptor.storage_mode = storage_mode;
409 texture_descriptor.format = pixel_format.value();
410 texture_descriptor.size = {image_info.width(), image_info.height()};
411 texture_descriptor.mip_count =
412 create_mips ? texture_descriptor.size.MipCount() : 1;
413
414 auto texture =
415 context->GetResourceAllocator()->CreateTexture(desc: texture_descriptor);
416 if (!texture) {
417 std::string decode_error("Could not create Impeller texture.");
418 FML_DLOG(ERROR) << decode_error;
419 return std::make_pair(t1: nullptr, t2&: decode_error);
420 }
421
422 auto mapping = std::make_shared<fml::NonOwnedMapping>(
423 args: reinterpret_cast<const uint8_t*>(bitmap->getAddr(x: 0, y: 0)), // data
424 args: texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
425 args: [bitmap](auto, auto) mutable { bitmap.reset(); } // proc
426 );
427
428 if (!texture->SetContents(mapping)) {
429 std::string decode_error("Could not copy contents into Impeller texture.");
430 FML_DLOG(ERROR) << decode_error;
431 return std::make_pair(t1: nullptr, t2&: decode_error);
432 }
433
434 texture->SetLabel(impeller::SPrintF(format: "ui.Image(%p)", texture.get()).c_str());
435
436 if (texture_descriptor.mip_count > 1u && create_mips) {
437 std::optional<std::string> decode_error;
438
439 // The only platform that needs mipmapping unconditionally is GL.
440 // GL based platforms never disable GPU access.
441 // This is only really needed for iOS.
442 gpu_disabled_switch->Execute(handlers: fml::SyncSwitch::Handlers().SetIfFalse(
443 [context, &texture, &decode_error] {
444 auto command_buffer = context->CreateCommandBuffer();
445 if (!command_buffer) {
446 decode_error =
447 "Could not create command buffer for mipmap generation.";
448 return;
449 }
450 command_buffer->SetLabel("Mipmap Command Buffer");
451
452 auto blit_pass = command_buffer->CreateBlitPass();
453 if (!blit_pass) {
454 decode_error = "Could not create blit pass for mipmap generation.";
455 return;
456 }
457 blit_pass->SetLabel("Mipmap Blit Pass");
458 blit_pass->GenerateMipmap(texture);
459
460 blit_pass->EncodeCommands(transients_allocator: context->GetResourceAllocator());
461 if (!command_buffer->SubmitCommands()) {
462 decode_error = "Failed to submit blit pass command buffer.";
463 return;
464 }
465 command_buffer->WaitUntilScheduled();
466 }));
467 if (decode_error.has_value()) {
468 FML_DLOG(ERROR) << decode_error.value();
469 return std::make_pair(t1: nullptr, t2&: decode_error.value());
470 }
471 }
472
473 return std::make_pair(t1: impeller::DlImageImpeller::Make(texture: std::move(texture)),
474 t2: std::string());
475}
476
477// |ImageDecoder|
478void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
479 uint32_t target_width,
480 uint32_t target_height,
481 const ImageResult& p_result) {
482 FML_DCHECK(descriptor);
483 FML_DCHECK(p_result);
484
485 // Wrap the result callback so that it can be invoked from any thread.
486 auto raw_descriptor = descriptor.get();
487 raw_descriptor->AddRef();
488 ImageResult result = [p_result, //
489 raw_descriptor, //
490 ui_runner = runners_.GetUITaskRunner() //
491 ](auto image, auto decode_error) {
492 ui_runner->PostTask(task: [raw_descriptor, p_result, image, decode_error]() {
493 raw_descriptor->Release();
494 p_result(std::move(image), decode_error);
495 });
496 };
497
498 concurrent_task_runner_->PostTask(
499 task: [raw_descriptor, //
500 context = context_.get(), //
501 target_size = SkISize::Make(w: target_width, h: target_height), //
502 io_runner = runners_.GetIOTaskRunner(), //
503 result,
504 supports_wide_gamut = supports_wide_gamut_, //
505 gpu_disabled_switch = gpu_disabled_switch_]() {
506 if (!context) {
507 result(nullptr, "No Impeller context is available");
508 return;
509 }
510 auto max_size_supported =
511 context->GetResourceAllocator()->GetMaxTextureSizeSupported();
512
513 // Always decompress on the concurrent runner.
514 auto bitmap_result = DecompressTexture(
515 descriptor: raw_descriptor, target_size, max_texture_size: max_size_supported,
516 supports_wide_gamut, allocator: context->GetResourceAllocator());
517 if (!bitmap_result.device_buffer) {
518 result(nullptr, bitmap_result.decode_error);
519 return;
520 }
521 auto upload_texture_and_invoke_result = [result, context, bitmap_result,
522 gpu_disabled_switch]() {
523 sk_sp<DlImage> image;
524 std::string decode_error;
525 if (!kShouldUseMallocDeviceBuffer &&
526 context->GetCapabilities()->SupportsBufferToTextureBlits()) {
527 std::tie(t&: image, t&: decode_error) = UploadTextureToPrivate(
528 context, buffer: bitmap_result.device_buffer, image_info: bitmap_result.image_info,
529 bitmap: bitmap_result.sk_bitmap, gpu_disabled_switch);
530 result(image, decode_error);
531 } else {
532 std::tie(t&: image, t&: decode_error) = UploadTextureToStorage(
533 context, bitmap: bitmap_result.sk_bitmap, gpu_disabled_switch,
534 storage_mode: impeller::StorageMode::kDevicePrivate,
535 /*create_mips=*/true);
536 result(image, decode_error);
537 }
538 };
539 // TODO(jonahwilliams):
540 // https://github.com/flutter/flutter/issues/123058 Technically we
541 // don't need to post tasks to the io runner, but without this
542 // forced serialization we can end up overloading the GPU and/or
543 // competing with raster workloads.
544 io_runner->PostTask(task: upload_texture_and_invoke_result);
545 });
546}
547
548ImpellerAllocator::ImpellerAllocator(
549 std::shared_ptr<impeller::Allocator> allocator)
550 : allocator_(std::move(allocator)) {}
551
552std::shared_ptr<impeller::DeviceBuffer> ImpellerAllocator::GetDeviceBuffer()
553 const {
554 return buffer_;
555}
556
557bool ImpellerAllocator::allocPixelRef(SkBitmap* bitmap) {
558 const SkImageInfo& info = bitmap->info();
559 if (kUnknown_SkColorType == info.colorType() || info.width() < 0 ||
560 info.height() < 0 || !info.validRowBytes(rowBytes: bitmap->rowBytes())) {
561 return false;
562 }
563
564 impeller::DeviceBufferDescriptor descriptor;
565 descriptor.storage_mode = impeller::StorageMode::kHostVisible;
566 descriptor.size = ((bitmap->height() - 1) * bitmap->rowBytes()) +
567 (bitmap->width() * bitmap->bytesPerPixel());
568
569 std::shared_ptr<impeller::DeviceBuffer> device_buffer =
570 kShouldUseMallocDeviceBuffer
571 ? std::make_shared<MallocDeviceBuffer>(args&: descriptor)
572 : allocator_->CreateBuffer(desc: descriptor);
573
574 struct ImpellerPixelRef final : public SkPixelRef {
575 ImpellerPixelRef(int w, int h, void* s, size_t r)
576 : SkPixelRef(w, h, s, r) {}
577
578 ~ImpellerPixelRef() override {}
579 };
580
581 auto pixel_ref = sk_sp<SkPixelRef>(
582 new ImpellerPixelRef(info.width(), info.height(),
583 device_buffer->OnGetContents(), bitmap->rowBytes()));
584
585 bitmap->setPixelRef(pixelRef: std::move(pixel_ref), dx: 0, dy: 0);
586 buffer_ = std::move(device_buffer);
587 return true;
588}
589
590} // namespace flutter
591

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_impeller.cc