Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
image_decoder_impeller.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
6
7#include <format>
8#include <memory>
9
10#include "flutter/fml/closure.h"
12#include "flutter/fml/mapping.h"
23#include "third_party/skia/include/core/SkAlphaType.h"
24#include "third_party/skia/include/core/SkBitmap.h"
25#include "third_party/skia/include/core/SkColorSpace.h"
26#include "third_party/skia/include/core/SkColorType.h"
27#include "third_party/skia/include/core/SkImageInfo.h"
28#include "third_party/skia/include/core/SkMallocPixelRef.h"
29#include "third_party/skia/include/core/SkPixelRef.h"
30#include "third_party/skia/include/core/SkPixmap.h"
31#include "third_party/skia/include/core/SkPoint.h"
32#include "third_party/skia/include/core/SkSize.h"
33
34namespace flutter {
35
36namespace {
37/**
38 * Loads the gamut as a set of three points (triangle).
39 */
40void LoadGamut(SkPoint abc[3], const skcms_Matrix3x3& xyz) {
41 // rx = rX / (rX + rY + rZ)
42 // ry = rY / (rX + rY + rZ)
43 // gx, gy, bx, and gy are calculated similarly.
44 for (int index = 0; index < 3; index++) {
45 float sum = xyz.vals[index][0] + xyz.vals[index][1] + xyz.vals[index][2];
46 abc[index].fX = xyz.vals[index][0] / sum;
47 abc[index].fY = xyz.vals[index][1] / sum;
48 }
49}
50
51/**
52 * Calculates the area of the triangular gamut.
53 */
54float CalculateArea(SkPoint abc[3]) {
55 const SkPoint& a = abc[0];
56 const SkPoint& b = abc[1];
57 const SkPoint& c = abc[2];
58 return 0.5f * fabsf(a.fX * b.fY + b.fX * c.fY - a.fX * c.fY - c.fX * b.fY -
59 b.fX * a.fY);
60}
61
62// Note: This was calculated from SkColorSpace::MakeSRGB().
63static constexpr float kSrgbGamutArea = 0.0982f;
64
65// Source:
66// https://source.chromium.org/chromium/_/skia/skia.git/+/393fb1ec80f41d8ad7d104921b6920e69749fda1:src/codec/SkAndroidCodec.cpp;l=67;drc=46572b4d445f41943059d0e377afc6d6748cd5ca;bpv=1;bpt=0
67bool IsWideGamut(const SkColorSpace* color_space) {
68 if (!color_space) {
69 return false;
70 }
71 skcms_Matrix3x3 xyzd50;
72 color_space->toXYZD50(&xyzd50);
73 SkPoint rgb[3];
74 LoadGamut(rgb, xyzd50);
75 float area = CalculateArea(rgb);
76 return area > kSrgbGamutArea;
77}
78
79absl::StatusOr<ImageDecoderImpeller::ImageInfo> ToImageInfo(
80 const SkImageInfo& sk_image_format) {
81 const std::optional<impeller::PixelFormat> pixel_format =
82 ImageDecoderImpeller::ToPixelFormat(sk_image_format.colorType());
83 if (!pixel_format.has_value()) {
84 std::string decode_error(
85 std::format("Codec pixel format is not supported (SkColorType={})",
86 static_cast<int>(sk_image_format.colorType())));
87 return absl::InvalidArgumentError(decode_error);
88 }
89 return {{
90 .size =
91 impeller::ISize(sk_image_format.width(), sk_image_format.height()),
92 .format = pixel_format.value(),
93 }};
94}
95
96SkColorType ChooseCompatibleColorType(SkColorType type) {
97 switch (type) {
98 case kRGBA_F32_SkColorType:
99 return kRGBA_F16_SkColorType;
100 default:
101 return kRGBA_8888_SkColorType;
102 }
103}
104
105SkAlphaType ChooseCompatibleAlphaType(SkAlphaType type) {
106 return type;
107}
108
109absl::StatusOr<SkColorType> ChooseCompatibleColorType(
111 switch (format) {
113 return kRGBA_F32_SkColorType;
114 default:
115 return absl::InvalidArgumentError("unsupported target pixel format");
116 }
117}
118
119absl::StatusOr<SkImageInfo> CreateImageInfo(
120 const SkImageInfo& base_image_info,
121 const SkISize& decode_size,
122 bool supports_wide_gamut,
123 ImageDecoder::TargetPixelFormat target_format) {
124 const bool is_wide_gamut =
125 supports_wide_gamut ? IsWideGamut(base_image_info.colorSpace()) : false;
126 SkAlphaType alpha_type =
127 ChooseCompatibleAlphaType(base_image_info.alphaType());
128 if (target_format != ImageDecoder::TargetPixelFormat::kDontCare) {
129 absl::StatusOr<SkColorType> target_skia_color_type =
130 ChooseCompatibleColorType(target_format);
131 if (!target_skia_color_type.ok()) {
132 return target_skia_color_type.status();
133 }
134 return base_image_info.makeWH(decode_size.width(), decode_size.height())
135 .makeColorType(target_skia_color_type.value())
136 .makeAlphaType(alpha_type)
137 .makeColorSpace(SkColorSpace::MakeSRGB());
138 } else if (is_wide_gamut) {
139 SkColorType color_type = alpha_type == SkAlphaType::kOpaque_SkAlphaType
140 ? kBGR_101010x_XR_SkColorType
141 : kRGBA_F16_SkColorType;
142 return base_image_info.makeWH(decode_size.width(), decode_size.height())
143 .makeColorType(color_type)
144 .makeAlphaType(alpha_type)
145 .makeColorSpace(SkColorSpace::MakeSRGB());
146 } else {
147 return base_image_info.makeWH(decode_size.width(), decode_size.height())
148 .makeColorType(ChooseCompatibleColorType(base_image_info.colorType()))
149 .makeAlphaType(alpha_type)
150 .makeColorSpace(SkColorSpace::MakeSRGB());
151 }
152}
153
154struct DecodedBitmap {
155 std::shared_ptr<SkBitmap> bitmap;
156 std::shared_ptr<ImpellerAllocator> allocator;
157};
158
159absl::StatusOr<DecodedBitmap> DecodeToBitmap(
160 ImageDescriptor* descriptor,
161 const SkImageInfo& image_info,
162 const SkImageInfo& base_image_info,
163 const std::shared_ptr<impeller::Allocator>& allocator) {
164 std::shared_ptr<SkBitmap> bitmap = std::make_shared<SkBitmap>();
165 bitmap->setInfo(image_info);
166 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
167 std::make_shared<ImpellerAllocator>(allocator);
168
169 if (descriptor->is_compressed()) {
170 if (!bitmap->tryAllocPixels(bitmap_allocator.get())) {
171 std::string error =
172 "Could not allocate intermediate for image decompression.";
173 FML_DLOG(ERROR) << error;
174 return absl::InvalidArgumentError(error);
175 }
176 if (!descriptor->get_pixels(bitmap->pixmap())) {
177 std::string error = "Could not decompress image.";
178 FML_DLOG(ERROR) << error;
179 return absl::InvalidArgumentError(error);
180 }
181 } else {
182 std::shared_ptr<SkBitmap> temp_bitmap = std::make_shared<SkBitmap>();
183 temp_bitmap->setInfo(base_image_info);
184 sk_sp<SkPixelRef> pixel_ref = SkMallocPixelRef::MakeWithData(
185 base_image_info, descriptor->row_bytes(), descriptor->data());
186 temp_bitmap->setPixelRef(pixel_ref, 0, 0);
187
188 if (!bitmap->tryAllocPixels(bitmap_allocator.get())) {
189 std::string error =
190 "Could not allocate intermediate for pixel conversion.";
191 FML_DLOG(ERROR) << error;
192 return absl::ResourceExhaustedError(error);
193 }
194 temp_bitmap->readPixels(bitmap->pixmap());
195 bitmap->setImmutable();
196 }
197 return DecodedBitmap{.bitmap = bitmap, .allocator = bitmap_allocator};
198}
199
200absl::StatusOr<DecodedBitmap> HandlePremultiplication(
201 const std::shared_ptr<SkBitmap>& bitmap,
202 std::shared_ptr<ImpellerAllocator> bitmap_allocator,
203 const std::shared_ptr<impeller::Allocator>& allocator) {
204 if (bitmap->alphaType() != SkAlphaType::kUnpremul_SkAlphaType) {
205 return DecodedBitmap{.bitmap = bitmap,
206 .allocator = std::move(bitmap_allocator)};
207 }
208
209 std::shared_ptr<ImpellerAllocator> premul_allocator =
210 std::make_shared<ImpellerAllocator>(allocator);
211 std::shared_ptr<SkBitmap> premul_bitmap = std::make_shared<SkBitmap>();
212 premul_bitmap->setInfo(bitmap->info().makeAlphaType(kPremul_SkAlphaType));
213 if (!premul_bitmap->tryAllocPixels(premul_allocator.get())) {
214 std::string error =
215 "Could not allocate intermediate for premultiplication conversion.";
216 FML_DLOG(ERROR) << error;
217 return absl::ResourceExhaustedError(error);
218 }
219 bitmap->readPixels(premul_bitmap->pixmap());
220 premul_bitmap->setImmutable();
221 return DecodedBitmap{.bitmap = premul_bitmap, .allocator = premul_allocator};
222}
223
224absl::StatusOr<ImageDecoderImpeller::DecompressResult> ResizeOnCpu(
225 const std::shared_ptr<SkBitmap>& bitmap,
226 const SkISize& target_size,
227 const std::shared_ptr<impeller::Allocator>& allocator) {
228 TRACE_EVENT0("impeller", "SlowCPUDecodeScale");
229 const SkImageInfo scaled_image_info =
230 bitmap->info().makeDimensions(target_size);
231
232 std::shared_ptr<SkBitmap> scaled_bitmap = std::make_shared<SkBitmap>();
233 std::shared_ptr<ImpellerAllocator> scaled_allocator =
234 std::make_shared<ImpellerAllocator>(allocator);
235 scaled_bitmap->setInfo(scaled_image_info);
236 if (!scaled_bitmap->tryAllocPixels(scaled_allocator.get())) {
237 std::string error =
238 "Could not allocate scaled bitmap for image decompression.";
239 FML_DLOG(ERROR) << error;
240 return absl::ResourceExhaustedError(error);
241 }
242 if (!bitmap->pixmap().scalePixels(
243 scaled_bitmap->pixmap(),
244 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone))) {
245 FML_LOG(ERROR) << "Could not scale decoded bitmap data.";
246 }
247 scaled_bitmap->setImmutable();
248
249 std::shared_ptr<impeller::DeviceBuffer> buffer =
250 scaled_allocator->GetDeviceBuffer();
251 if (!buffer) {
252 return absl::InternalError("Unable to get device buffer");
253 }
254 buffer->Flush();
255
256 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
257 ToImageInfo(scaled_bitmap->info());
258 if (!decoded_image_info.ok()) {
259 return decoded_image_info.status();
260 }
261 return ImageDecoderImpeller::DecompressResult{
262 .device_buffer = std::move(buffer),
263 .image_info = decoded_image_info.value()};
264}
265
266bool IsZeroOpConversion(ImageDescriptor::PixelFormat input,
268 switch (input) {
271 default:
272 return false;
273 }
274}
275
278 switch (format) {
281 default:
282 FML_DCHECK(false) << "Unsupported pixel format";
284 }
285}
286} // namespace
287
288std::optional<impeller::PixelFormat> ImageDecoderImpeller::ToPixelFormat(
289 SkColorType type) {
290 switch (type) {
291 case kRGBA_8888_SkColorType:
293 case kBGRA_8888_SkColorType:
295 case kRGBA_F16_SkColorType:
297 case kBGR_101010x_XR_SkColorType:
299 case kRGBA_F32_SkColorType:
301 default:
302 return std::nullopt;
303 }
304 return std::nullopt;
305}
306
308 const TaskRunners& runners,
309 std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
310 const fml::WeakPtr<IOManager>& io_manager,
311 bool wide_gamut_enabled,
312 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch)
313 : ImageDecoder(runners, std::move(concurrent_task_runner), io_manager),
314 wide_gamut_enabled_(wide_gamut_enabled),
315 gpu_disabled_switch_(gpu_disabled_switch) {
316 std::promise<std::shared_ptr<impeller::Context>> context_promise;
317 context_ = context_promise.get_future();
319 [promise = std::move(context_promise), io_manager]() mutable {
320 if (io_manager) {
321 promise.set_value(io_manager->GetImpellerContext());
322 } else {
323 promise.set_value(nullptr);
324 }
325 }));
326}
327
329
330absl::StatusOr<ImageDecoderImpeller::DecompressResult>
332 ImageDescriptor* descriptor,
333 const ImageDecoder::Options& options,
334 impeller::ISize max_texture_size,
335 bool supports_wide_gamut,
336 const std::shared_ptr<const impeller::Capabilities>& capabilities,
337 const std::shared_ptr<impeller::Allocator>& allocator) {
338 TRACE_EVENT0("impeller", __FUNCTION__);
339 if (!descriptor) {
340 std::string decode_error("Invalid descriptor (should never happen)");
341 FML_DLOG(ERROR) << decode_error;
342 return absl::InvalidArgumentError(decode_error);
343 }
344
345 const SkISize source_size = SkISize::Make(descriptor->image_info().width,
346 descriptor->image_info().height);
347 const SkISize target_size =
348 SkISize::Make(std::min(max_texture_size.width,
349 static_cast<int64_t>(options.target_width)),
350 std::min(max_texture_size.height,
351 static_cast<int64_t>(options.target_height)));
352
353 // Fast path for when the input requires no decompressing or conversion.
354 if (!descriptor->is_compressed() && source_size == target_size &&
355 IsZeroOpConversion(descriptor->image_info().format,
356 options.target_format)) {
359 desc.size = descriptor->data()->size();
360 std::shared_ptr<impeller::DeviceBuffer> buffer =
361 allocator->CreateBuffer(desc);
362 if (!buffer) {
363 return absl::ResourceExhaustedError("Could not create buffer for image.");
364 }
365 sk_sp<SkData> data = descriptor->data();
366 memcpy(buffer->OnGetContents(), data->bytes(), data->size());
367 buffer->Flush();
368
370 .device_buffer = std::move(buffer),
371 .image_info =
372 {
373 .size =
374 impeller::ISize(source_size.width(), source_size.height()),
375 .format = ToImpellerPixelFormat(options.target_format),
376 },
377 };
378 }
379
380 SkISize decode_size = source_size;
381 if (descriptor->is_compressed()) {
382 decode_size = descriptor->get_scaled_dimensions(std::max(
383 static_cast<float>(target_size.width()) / source_size.width(),
384 static_cast<float>(target_size.height()) / source_size.height()));
385 }
386
387 const SkImageInfo base_image_info =
389 const absl::StatusOr<SkImageInfo> image_info = CreateImageInfo(
390 base_image_info, decode_size, supports_wide_gamut, options.target_format);
391
392 if (!image_info.ok()) {
393 return image_info.status();
394 }
395
396 if (!ToPixelFormat(image_info.value().colorType()).has_value()) {
397 std::string decode_error =
398 std::format("Codec pixel format is not supported (SkColorType={})",
399 static_cast<int>(image_info.value().colorType()));
400 FML_DLOG(ERROR) << decode_error;
401 return absl::InvalidArgumentError(decode_error);
402 }
403
404 absl::StatusOr<DecodedBitmap> decoded = DecodeToBitmap(
405 descriptor, image_info.value(), base_image_info, allocator);
406 if (!decoded.ok()) {
407 return decoded.status();
408 }
409
410 absl::StatusOr<DecodedBitmap> premultiplied =
411 HandlePremultiplication(decoded->bitmap, decoded->allocator, allocator);
412 if (!premultiplied.ok()) {
413 return premultiplied.status();
414 }
415 std::shared_ptr<SkBitmap> bitmap = premultiplied->bitmap;
416 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
417 premultiplied->allocator;
418
419 if (source_size.width() > max_texture_size.width ||
420 source_size.height() > max_texture_size.height ||
421 !capabilities->SupportsTextureToTextureBlits()) {
422 return ResizeOnCpu(bitmap, target_size, allocator);
423 }
424
425 std::shared_ptr<impeller::DeviceBuffer> buffer =
426 bitmap_allocator->GetDeviceBuffer();
427 if (!buffer) {
428 return absl::InternalError("Unable to get device buffer");
429 }
430 buffer->Flush();
431
432 std::optional<SkImageInfo> resize_info =
433 bitmap->dimensions() == target_size
434 ? std::nullopt
435 : std::optional<SkImageInfo>(
436 image_info.value().makeDimensions(target_size));
437
438 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
439 ToImageInfo(bitmap->info());
440 if (!decoded_image_info.ok()) {
441 return decoded_image_info.status();
442 }
443
445 .device_buffer = std::move(buffer),
446 .image_info = decoded_image_info.value(),
447 .resize_info = resize_info};
448}
449
450// static
451std::pair<sk_sp<DlImage>, std::string>
452ImageDecoderImpeller::UnsafeUploadTextureToPrivate(
453 const std::shared_ptr<impeller::Context>& context,
454 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
455 const ImageDecoderImpeller::ImageInfo& image_info,
456 const std::optional<SkImageInfo>& resize_info) {
457 impeller::TextureDescriptor texture_descriptor;
459 texture_descriptor.format = image_info.format;
460 texture_descriptor.size = {image_info.size.width, image_info.size.height};
461 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
462 if (context->GetBackendType() == impeller::Context::BackendType::kMetal &&
463 resize_info.has_value()) {
464 // The MPS used to resize images on iOS does not require mip generation.
465 // Remove mip count if we are resizing the image on the GPU.
466 texture_descriptor.mip_count = 1;
467 }
468
469 auto dest_texture =
470 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
471 if (!dest_texture) {
472 std::string decode_error("Could not create Impeller texture.");
473 FML_DLOG(ERROR) << decode_error;
474 return std::make_pair(nullptr, decode_error);
475 }
476
477 dest_texture->SetLabel(
478 std::format("ui.Image({})", static_cast<const void*>(dest_texture.get()))
479 .c_str());
480
481 auto command_buffer = context->CreateCommandBuffer();
482 if (!command_buffer) {
483 std::string decode_error(
484 "Could not create command buffer for mipmap generation.");
485 FML_DLOG(ERROR) << decode_error;
486 return std::make_pair(nullptr, decode_error);
487 }
488 command_buffer->SetLabel("Mipmap Command Buffer");
489
490 auto blit_pass = command_buffer->CreateBlitPass();
491 if (!blit_pass) {
492 std::string decode_error(
493 "Could not create blit pass for mipmap generation.");
494 FML_DLOG(ERROR) << decode_error;
495 return std::make_pair(nullptr, decode_error);
496 }
497 blit_pass->SetLabel("Mipmap Blit Pass");
498 blit_pass->AddCopy(impeller::DeviceBuffer::AsBufferView(buffer),
499 dest_texture);
500 if (texture_descriptor.mip_count > 1) {
501 blit_pass->GenerateMipmap(dest_texture);
502 }
503
504 std::shared_ptr<impeller::Texture> result_texture = dest_texture;
505 if (resize_info.has_value()) {
506 impeller::TextureDescriptor resize_desc;
508 resize_desc.format = image_info.format;
509 resize_desc.size = {resize_info->width(), resize_info->height()};
510 resize_desc.mip_count = resize_desc.size.MipCount();
512 if (context->GetBackendType() == impeller::Context::BackendType::kMetal) {
513 // Resizing requires a MPS on Metal platforms.
516 }
517 auto resize_texture =
518 context->GetResourceAllocator()->CreateTexture(resize_desc);
519 if (!resize_texture) {
520 std::string decode_error("Could not create resized Impeller texture.");
521 FML_DLOG(ERROR) << decode_error;
522 return std::make_pair(nullptr, decode_error);
523 }
524
525 blit_pass->ResizeTexture(/*source=*/dest_texture,
526 /*destination=*/resize_texture);
527 if (resize_desc.mip_count > 1) {
528 blit_pass->GenerateMipmap(resize_texture);
529 }
530
531 result_texture = std::move(resize_texture);
532 }
533 blit_pass->EncodeCommands();
534 if (!context->GetCommandQueue()
535 ->Submit(
536 {command_buffer},
538 if (status == impeller::CommandBuffer::Status::kError) {
539 FML_LOG(ERROR)
540 << "GPU Error submitting image decoding command buffer.";
541 }
542 },
543 /*block_on_schedule=*/true)
544 .ok()) {
545 std::string decode_error("Failed to submit image decoding command buffer.");
546 FML_DLOG(ERROR) << decode_error;
547 return std::make_pair(nullptr, decode_error);
548 }
549
550 // Flush the pending command buffer to ensure that its output becomes visible
551 // to the raster thread.
552 if (context->AddTrackingFence(result_texture)) {
553 command_buffer->WaitUntilScheduled();
554 } else {
555 command_buffer->WaitUntilCompleted();
556 }
557
558 context->DisposeThreadLocalCachedResources();
559
560 return std::make_pair(
561 impeller::DlImageImpeller::Make(std::move(result_texture)),
562 std::string());
563}
564
565void ImageDecoderImpeller::UploadTextureToPrivate(
566 ImageResult result,
567 const std::shared_ptr<impeller::Context>& context,
568 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
569 const ImageDecoderImpeller::ImageInfo& image_info,
570 const std::optional<SkImageInfo>& resize_info,
571 const std::shared_ptr<const fml::SyncSwitch>& gpu_disabled_switch) {
572 TRACE_EVENT0("impeller", __FUNCTION__);
573 if (!context) {
574 result(nullptr, "No Impeller context is available");
575 return;
576 }
577 if (!buffer) {
578 result(nullptr, "No Impeller device buffer is available");
579 return;
580 }
581
582 gpu_disabled_switch->Execute(
584 .SetIfFalse([&result, context, buffer, image_info, resize_info] {
585 sk_sp<DlImage> image;
586 std::string decode_error;
587 std::tie(image, decode_error) = std::tie(image, decode_error) =
588 UnsafeUploadTextureToPrivate(context, buffer, image_info,
589 resize_info);
590 result(image, decode_error);
591 })
592 .SetIfTrue([&result, context, buffer, image_info, resize_info] {
593 auto result_ptr = std::make_shared<ImageResult>(std::move(result));
594 context->StoreTaskForGPU(
595 [result_ptr, context, buffer, image_info, resize_info]() {
596 sk_sp<DlImage> image;
597 std::string decode_error;
598 std::tie(image, decode_error) = UnsafeUploadTextureToPrivate(
599 context, buffer, image_info, resize_info);
600 (*result_ptr)(image, decode_error);
601 },
602 [result_ptr]() {
603 (*result_ptr)(
604 nullptr,
605 "Image upload failed due to loss of GPU access.");
606 });
607 }));
608}
609
610std::pair<sk_sp<DlImage>, std::string>
611ImageDecoderImpeller::UploadTextureToStorage(
612 const std::shared_ptr<impeller::Context>& context,
613 std::shared_ptr<SkBitmap> bitmap) {
614 TRACE_EVENT0("impeller", __FUNCTION__);
615 if (!context) {
616 return std::make_pair(nullptr, "No Impeller context is available");
617 }
618 if (!bitmap) {
619 return std::make_pair(nullptr, "No texture bitmap is available");
620 }
621 const auto image_info = bitmap->info();
622 const auto pixel_format = ToPixelFormat(image_info.colorType());
623 if (!pixel_format) {
624 std::string decode_error(
625 std::format("Unsupported pixel format (SkColorType={})",
626 static_cast<int>(image_info.colorType())));
627 FML_DLOG(ERROR) << decode_error;
628 return std::make_pair(nullptr, decode_error);
629 }
630
631 impeller::TextureDescriptor texture_descriptor;
633 texture_descriptor.format = pixel_format.value();
634 texture_descriptor.size = {image_info.width(), image_info.height()};
635 texture_descriptor.mip_count = 1;
636
637 auto texture =
638 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
639 if (!texture) {
640 std::string decode_error("Could not create Impeller texture.");
641 FML_DLOG(ERROR) << decode_error;
642 return std::make_pair(nullptr, decode_error);
643 }
644
645 auto mapping = std::make_shared<fml::NonOwnedMapping>(
646 reinterpret_cast<const uint8_t*>(bitmap->getAddr(0, 0)), // data
647 texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
648 [bitmap](auto, auto) mutable { bitmap.reset(); } // proc
649 );
650
651 if (!texture->SetContents(mapping)) {
652 std::string decode_error("Could not copy contents into Impeller texture.");
653 FML_DLOG(ERROR) << decode_error;
654 return std::make_pair(nullptr, decode_error);
655 }
656
657 texture->SetLabel(
658 std::format("ui.Image({})", static_cast<const void*>(texture.get()))
659 .c_str());
660
661 context->DisposeThreadLocalCachedResources();
662
663 return std::make_pair(impeller::DlImageImpeller::Make(std::move(texture)),
664 std::string());
665}
666
667// |ImageDecoder|
668void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
669 const ImageDecoder::Options& options,
670 const ImageResult& p_result) {
671 FML_DCHECK(descriptor);
672 FML_DCHECK(p_result);
673
674 // Wrap the result callback so that it can be invoked from any thread.
675 auto raw_descriptor = descriptor.get();
676 raw_descriptor->AddRef();
677 ImageResult result = [p_result, //
678 raw_descriptor, //
679 ui_runner = runners_.GetUITaskRunner() //
680 ](const auto& image, const auto& decode_error) {
681 ui_runner->PostTask([raw_descriptor, p_result, image, decode_error]() {
682 raw_descriptor->Release();
683 p_result(std::move(image), decode_error);
684 });
685 };
686
687 concurrent_task_runner_->PostTask(
688 [raw_descriptor, //
689 context = context_.get(), //
690 options,
691 io_runner = runners_.GetIOTaskRunner(), //
692 result,
693 wide_gamut_enabled = wide_gamut_enabled_, //
694 gpu_disabled_switch = gpu_disabled_switch_]() {
695#if FML_OS_IOS_SIMULATOR
696 // No-op backend.
697 if (!context) {
698 return;
699 }
700#endif // FML_OS_IOS_SIMULATOR
701
702 if (!context) {
703 result(nullptr, "No Impeller context is available");
704 return;
705 }
706 auto max_size_supported =
707 context->GetResourceAllocator()->GetMaxTextureSizeSupported();
708
709 // Always decompress on the concurrent runner.
710 auto bitmap_result = DecompressTexture(
711 raw_descriptor, options, max_size_supported,
712 /*supports_wide_gamut=*/wide_gamut_enabled &&
713 context->GetCapabilities()->SupportsExtendedRangeFormats(),
714 context->GetCapabilities(), context->GetResourceAllocator());
715 if (!bitmap_result.ok()) {
716 result(nullptr, std::string(bitmap_result.status().message()));
717 return;
718 }
719
720 auto upload_texture_and_invoke_result = [result, context, bitmap_result,
721 gpu_disabled_switch]() {
722 UploadTextureToPrivate(result, context, //
723 bitmap_result->device_buffer, //
724 bitmap_result->image_info, //
725 bitmap_result->resize_info, //
726 gpu_disabled_switch //
727 );
728 };
729 // The I/O image uploads are not threadsafe on GLES.
730 if (context->GetBackendType() ==
732 io_runner->PostTask(upload_texture_and_invoke_result);
733 } else {
734 upload_texture_and_invoke_result();
735 }
736 });
737}
738
739ImpellerAllocator::ImpellerAllocator(
740 std::shared_ptr<impeller::Allocator> allocator)
741 : allocator_(std::move(allocator)) {}
742
743std::shared_ptr<impeller::DeviceBuffer> ImpellerAllocator::GetDeviceBuffer()
744 const {
745 return buffer_;
746}
747
749 if (!bitmap) {
750 return false;
751 }
752 const SkImageInfo& info = bitmap->info();
753 if (kUnknown_SkColorType == info.colorType() || info.width() < 0 ||
754 info.height() < 0 || !info.validRowBytes(bitmap->rowBytes())) {
755 return false;
756 }
757
760 descriptor.size = ((bitmap->height() - 1) * bitmap->rowBytes()) +
761 (bitmap->width() * bitmap->bytesPerPixel());
762
763 std::shared_ptr<impeller::DeviceBuffer> device_buffer =
764 allocator_->CreateBuffer(descriptor);
765 if (!device_buffer) {
766 return false;
767 }
768
769 struct ImpellerPixelRef final : public SkPixelRef {
770 ImpellerPixelRef(int w, int h, void* s, size_t r)
771 : SkPixelRef(w, h, s, r) {}
772
773 ~ImpellerPixelRef() override {}
774 };
775
776 auto pixel_ref = sk_sp<SkPixelRef>(
777 new ImpellerPixelRef(info.width(), info.height(),
778 device_buffer->OnGetContents(), bitmap->rowBytes()));
779
780 bitmap->setPixelRef(std::move(pixel_ref), 0, 0);
781 buffer_ = std::move(device_buffer);
782 return true;
783}
784
785} // namespace flutter
GLenum type
std::function< void(sk_sp< DlImage >, std::string)> ImageResult
@ kDontCare
Explicitly declare the target pixel is left for the engine to decide.
ImageDecoderImpeller(const TaskRunners &runners, std::shared_ptr< fml::ConcurrentTaskRunner > concurrent_task_runner, const fml::WeakPtr< IOManager > &io_manager, bool supports_wide_gamut, const std::shared_ptr< fml::SyncSwitch > &gpu_disabled_switch)
static absl::StatusOr< DecompressResult > DecompressTexture(ImageDescriptor *descriptor, const ImageDecoder::Options &options, impeller::ISize max_texture_size, bool supports_wide_gamut, const std::shared_ptr< const impeller::Capabilities > &capabilities, const std::shared_ptr< impeller::Allocator > &allocator)
static std::optional< impeller::PixelFormat > ToPixelFormat(SkColorType type)
Creates an image descriptor for encoded or decoded image data, describing the width,...
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)
bool is_compressed() const
Whether this descriptor represents compressed (encoded) data or not.
const ImageInfo & image_info() const
The orientation corrected image info for this image.
sk_sp< SkData > data() const
The underlying buffer for this image.
bool allocPixelRef(SkBitmap *bitmap) override
std::shared_ptr< impeller::DeviceBuffer > GetDeviceBuffer() const
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
T * get() const
Definition ref_ptr.h:117
virtual void PostTask(const fml::closure &task) override
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
static int input(yyscan_t yyscanner)
FlutterVulkanImage * image
const uint8_t uint32_t uint32_t GError ** error
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_LOG(severity)
Definition logging.h:101
#define FML_DCHECK(condition)
Definition logging.h:122
std::shared_ptr< SkBitmap > bitmap
std::shared_ptr< ImpellerAllocator > allocator
FlTexture * texture
constexpr impeller::PixelFormat ToImpellerPixelFormat(FlutterGPUPixelFormat value)
Definition formats.h:60
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder h
Definition switch_defs.h:54
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
internal::CopyableLambda< T > MakeCopyable(T lambda)
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition formats_vk.h:185
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
ISize64 ISize
Definition size.h:162
Definition ref_ptr.h:261
uint32_t color_type
uint32_t alpha_type
TargetPixelFormat target_format
std::shared_ptr< impeller::DeviceBuffer > device_buffer
Represents the 2 code paths available when calling |SyncSwitchExecute|.
Definition sync_switch.h:35
Type height
Definition size.h:29
Type width
Definition size.h:28
constexpr size_t MipCount() const
Return the mip count of the texture.
Definition size.h:137
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
constexpr size_t GetByteSizeOfBaseMipLevel() const
#define TRACE_EVENT0(category_group, name)