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 // Use 10-bit for opaque images (less memory: 4 bytes vs 8 bytes per pixel).
140 // Use F16 for images with alpha channel.
141 SkColorType color_type = alpha_type == SkAlphaType::kOpaque_SkAlphaType
142 ? kBGR_101010x_XR_SkColorType
143 : kRGBA_F16_SkColorType;
144 return base_image_info.makeWH(decode_size.width(), decode_size.height())
145 .makeColorType(color_type)
146 .makeAlphaType(alpha_type)
147 .makeColorSpace(SkColorSpace::MakeSRGB());
148 } else {
149 return base_image_info.makeWH(decode_size.width(), decode_size.height())
150 .makeColorType(ChooseCompatibleColorType(base_image_info.colorType()))
151 .makeAlphaType(alpha_type)
152 .makeColorSpace(SkColorSpace::MakeSRGB());
153 }
154}
155
156struct DecodedBitmap {
157 std::shared_ptr<SkBitmap> bitmap;
158 std::shared_ptr<ImpellerAllocator> allocator;
159};
160
161absl::StatusOr<DecodedBitmap> DecodeToBitmap(
162 ImageDescriptor* descriptor,
163 const SkImageInfo& image_info,
164 const SkImageInfo& base_image_info,
165 const std::shared_ptr<impeller::Allocator>& allocator) {
166 std::shared_ptr<SkBitmap> bitmap = std::make_shared<SkBitmap>();
167 bitmap->setInfo(image_info);
168 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
169 std::make_shared<ImpellerAllocator>(allocator);
170
171 if (descriptor->is_compressed()) {
172 if (!bitmap->tryAllocPixels(bitmap_allocator.get())) {
173 std::string error =
174 "Could not allocate intermediate for image decompression.";
175 FML_DLOG(ERROR) << error;
176 return absl::InvalidArgumentError(error);
177 }
178 if (!descriptor->get_pixels(bitmap->pixmap())) {
179 std::string error = "Could not decompress image.";
180 FML_DLOG(ERROR) << error;
181 return absl::InvalidArgumentError(error);
182 }
183 } else {
184 std::shared_ptr<SkBitmap> temp_bitmap = std::make_shared<SkBitmap>();
185 temp_bitmap->setInfo(base_image_info);
186 sk_sp<SkPixelRef> pixel_ref = SkMallocPixelRef::MakeWithData(
187 base_image_info, descriptor->row_bytes(), descriptor->data());
188 temp_bitmap->setPixelRef(pixel_ref, 0, 0);
189
190 if (!bitmap->tryAllocPixels(bitmap_allocator.get())) {
191 std::string error =
192 "Could not allocate intermediate for pixel conversion.";
193 FML_DLOG(ERROR) << error;
194 return absl::ResourceExhaustedError(error);
195 }
196 temp_bitmap->readPixels(bitmap->pixmap());
197 bitmap->setImmutable();
198 }
199 return DecodedBitmap{.bitmap = bitmap, .allocator = bitmap_allocator};
200}
201
202absl::StatusOr<DecodedBitmap> HandlePremultiplication(
203 const std::shared_ptr<SkBitmap>& bitmap,
204 std::shared_ptr<ImpellerAllocator> bitmap_allocator,
205 const std::shared_ptr<impeller::Allocator>& allocator) {
206 if (bitmap->alphaType() != SkAlphaType::kUnpremul_SkAlphaType) {
207 return DecodedBitmap{.bitmap = bitmap,
208 .allocator = std::move(bitmap_allocator)};
209 }
210
211 std::shared_ptr<ImpellerAllocator> premul_allocator =
212 std::make_shared<ImpellerAllocator>(allocator);
213 std::shared_ptr<SkBitmap> premul_bitmap = std::make_shared<SkBitmap>();
214 premul_bitmap->setInfo(bitmap->info().makeAlphaType(kPremul_SkAlphaType));
215 if (!premul_bitmap->tryAllocPixels(premul_allocator.get())) {
216 std::string error =
217 "Could not allocate intermediate for premultiplication conversion.";
218 FML_DLOG(ERROR) << error;
219 return absl::ResourceExhaustedError(error);
220 }
221 bitmap->readPixels(premul_bitmap->pixmap());
222 premul_bitmap->setImmutable();
223 return DecodedBitmap{.bitmap = premul_bitmap, .allocator = premul_allocator};
224}
225
226absl::StatusOr<ImageDecoderImpeller::DecompressResult> ResizeOnCpu(
227 const std::shared_ptr<SkBitmap>& bitmap,
228 const SkISize& target_size,
229 const std::shared_ptr<impeller::Allocator>& allocator) {
230 TRACE_EVENT0("impeller", "SlowCPUDecodeScale");
231 const SkImageInfo scaled_image_info =
232 bitmap->info().makeDimensions(target_size);
233
234 std::shared_ptr<SkBitmap> scaled_bitmap = std::make_shared<SkBitmap>();
235 std::shared_ptr<ImpellerAllocator> scaled_allocator =
236 std::make_shared<ImpellerAllocator>(allocator);
237 scaled_bitmap->setInfo(scaled_image_info);
238 if (!scaled_bitmap->tryAllocPixels(scaled_allocator.get())) {
239 std::string error =
240 "Could not allocate scaled bitmap for image decompression.";
241 FML_DLOG(ERROR) << error;
242 return absl::ResourceExhaustedError(error);
243 }
244 if (!bitmap->pixmap().scalePixels(
245 scaled_bitmap->pixmap(),
246 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone))) {
247 FML_LOG(ERROR) << "Could not scale decoded bitmap data.";
248 }
249 scaled_bitmap->setImmutable();
250
251 std::shared_ptr<impeller::DeviceBuffer> buffer =
252 scaled_allocator->GetDeviceBuffer();
253 if (!buffer) {
254 return absl::InternalError("Unable to get device buffer");
255 }
256 buffer->Flush();
257
258 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
259 ToImageInfo(scaled_bitmap->info());
260 if (!decoded_image_info.ok()) {
261 return decoded_image_info.status();
262 }
263 return ImageDecoderImpeller::DecompressResult{
264 .device_buffer = std::move(buffer),
265 .image_info = decoded_image_info.value()};
266}
267
268bool IsZeroOpConversion(ImageDescriptor::PixelFormat input,
270 switch (input) {
273 default:
274 return false;
275 }
276}
277
280 switch (format) {
283 default:
284 FML_DCHECK(false) << "Unsupported pixel format";
286 }
287}
288} // namespace
289
290std::optional<impeller::PixelFormat> ImageDecoderImpeller::ToPixelFormat(
291 SkColorType type) {
292 switch (type) {
293 case kRGBA_8888_SkColorType:
295 case kBGRA_8888_SkColorType:
297 case kRGBA_F16_SkColorType:
299 case kBGR_101010x_XR_SkColorType:
301 case kRGBA_F32_SkColorType:
303 default:
304 return std::nullopt;
305 }
306 return std::nullopt;
307}
308
310 const TaskRunners& runners,
311 std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
312 const fml::WeakPtr<IOManager>& io_manager,
313 bool wide_gamut_enabled,
314 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch)
315 : ImageDecoder(runners, std::move(concurrent_task_runner), io_manager),
316 wide_gamut_enabled_(wide_gamut_enabled),
317 gpu_disabled_switch_(gpu_disabled_switch) {
318 std::promise<std::shared_ptr<impeller::Context>> context_promise;
319 context_ = context_promise.get_future();
321 [promise = std::move(context_promise), io_manager]() mutable {
322 if (io_manager) {
323 promise.set_value(io_manager->GetImpellerContext());
324 } else {
325 promise.set_value(nullptr);
326 }
327 }));
328}
329
331
332absl::StatusOr<ImageDecoderImpeller::DecompressResult>
334 ImageDescriptor* descriptor,
335 const ImageDecoder::Options& options,
336 impeller::ISize max_texture_size,
337 bool supports_wide_gamut,
338 const std::shared_ptr<const impeller::Capabilities>& capabilities,
339 const std::shared_ptr<impeller::Allocator>& allocator) {
340 TRACE_EVENT0("impeller", __FUNCTION__);
341 if (!descriptor) {
342 std::string decode_error("Invalid descriptor (should never happen)");
343 FML_DLOG(ERROR) << decode_error;
344 return absl::InvalidArgumentError(decode_error);
345 }
346
347 const SkISize source_size = SkISize::Make(descriptor->image_info().width,
348 descriptor->image_info().height);
349 const SkISize target_size =
350 SkISize::Make(std::min(max_texture_size.width,
351 static_cast<int64_t>(options.target_width)),
352 std::min(max_texture_size.height,
353 static_cast<int64_t>(options.target_height)));
354
355 // Fast path for when the input requires no decompressing or conversion.
356 if (!descriptor->is_compressed() && source_size == target_size &&
357 IsZeroOpConversion(descriptor->image_info().format,
358 options.target_format)) {
361 desc.size = descriptor->data()->size();
362 std::shared_ptr<impeller::DeviceBuffer> buffer =
363 allocator->CreateBuffer(desc);
364 if (!buffer) {
365 return absl::ResourceExhaustedError("Could not create buffer for image.");
366 }
367 sk_sp<SkData> data = descriptor->data();
368 memcpy(buffer->OnGetContents(), data->bytes(), data->size());
369 buffer->Flush();
370
372 .device_buffer = std::move(buffer),
373 .image_info =
374 {
375 .size =
376 impeller::ISize(source_size.width(), source_size.height()),
377 .format = ToImpellerPixelFormat(options.target_format),
378 },
379 };
380 }
381
382 SkISize decode_size = source_size;
383 if (descriptor->is_compressed()) {
384 decode_size = descriptor->get_scaled_dimensions(std::max(
385 static_cast<float>(target_size.width()) / source_size.width(),
386 static_cast<float>(target_size.height()) / source_size.height()));
387 }
388
389 const SkImageInfo base_image_info =
391 const absl::StatusOr<SkImageInfo> image_info = CreateImageInfo(
392 base_image_info, decode_size, supports_wide_gamut, options.target_format);
393
394 if (!image_info.ok()) {
395 return image_info.status();
396 }
397
398 if (!ToPixelFormat(image_info.value().colorType()).has_value()) {
399 std::string decode_error =
400 std::format("Codec pixel format is not supported (SkColorType={})",
401 static_cast<int>(image_info.value().colorType()));
402 FML_DLOG(ERROR) << decode_error;
403 return absl::InvalidArgumentError(decode_error);
404 }
405
406 absl::StatusOr<DecodedBitmap> decoded = DecodeToBitmap(
407 descriptor, image_info.value(), base_image_info, allocator);
408 if (!decoded.ok()) {
409 return decoded.status();
410 }
411
412 absl::StatusOr<DecodedBitmap> premultiplied =
413 HandlePremultiplication(decoded->bitmap, decoded->allocator, allocator);
414 if (!premultiplied.ok()) {
415 return premultiplied.status();
416 }
417 std::shared_ptr<SkBitmap> bitmap = premultiplied->bitmap;
418 std::shared_ptr<ImpellerAllocator> bitmap_allocator =
419 premultiplied->allocator;
420
421 if (source_size.width() > max_texture_size.width ||
422 source_size.height() > max_texture_size.height ||
423 !capabilities->SupportsTextureToTextureBlits()) {
424 return ResizeOnCpu(bitmap, target_size, allocator);
425 }
426
427 std::shared_ptr<impeller::DeviceBuffer> buffer =
428 bitmap_allocator->GetDeviceBuffer();
429 if (!buffer) {
430 return absl::InternalError("Unable to get device buffer");
431 }
432 buffer->Flush();
433
434 std::optional<SkImageInfo> resize_info =
435 bitmap->dimensions() == target_size
436 ? std::nullopt
437 : std::optional<SkImageInfo>(
438 image_info.value().makeDimensions(target_size));
439
440 absl::StatusOr<ImageDecoderImpeller::ImageInfo> decoded_image_info =
441 ToImageInfo(bitmap->info());
442 if (!decoded_image_info.ok()) {
443 return decoded_image_info.status();
444 }
445
447 .device_buffer = std::move(buffer),
448 .image_info = decoded_image_info.value(),
449 .resize_info = resize_info};
450}
451
452// static
453std::pair<sk_sp<DlImage>, std::string>
454ImageDecoderImpeller::UnsafeUploadTextureToPrivate(
455 const std::shared_ptr<impeller::Context>& context,
456 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
457 const ImageDecoderImpeller::ImageInfo& image_info,
458 const std::optional<SkImageInfo>& resize_info) {
459 impeller::TextureDescriptor texture_descriptor;
461 texture_descriptor.format = image_info.format;
462 texture_descriptor.size = {image_info.size.width, image_info.size.height};
463 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
464 if (context->GetBackendType() == impeller::Context::BackendType::kMetal &&
465 resize_info.has_value()) {
466 // The MPS used to resize images on iOS does not require mip generation.
467 // Remove mip count if we are resizing the image on the GPU.
468 texture_descriptor.mip_count = 1;
469 }
470
471 auto dest_texture =
472 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
473 if (!dest_texture) {
474 std::string decode_error("Could not create Impeller texture.");
475 FML_DLOG(ERROR) << decode_error;
476 return std::make_pair(nullptr, decode_error);
477 }
478
479 dest_texture->SetLabel(
480 std::format("ui.Image({})", static_cast<const void*>(dest_texture.get()))
481 .c_str());
482
483 auto command_buffer = context->CreateCommandBuffer();
484 if (!command_buffer) {
485 std::string decode_error(
486 "Could not create command buffer for mipmap generation.");
487 FML_DLOG(ERROR) << decode_error;
488 return std::make_pair(nullptr, decode_error);
489 }
490 command_buffer->SetLabel("Mipmap Command Buffer");
491
492 auto blit_pass = command_buffer->CreateBlitPass();
493 if (!blit_pass) {
494 std::string decode_error(
495 "Could not create blit pass for mipmap generation.");
496 FML_DLOG(ERROR) << decode_error;
497 return std::make_pair(nullptr, decode_error);
498 }
499 blit_pass->SetLabel("Mipmap Blit Pass");
500 blit_pass->AddCopy(impeller::DeviceBuffer::AsBufferView(buffer),
501 dest_texture);
502 if (texture_descriptor.mip_count > 1) {
503 blit_pass->GenerateMipmap(dest_texture);
504 }
505
506 std::shared_ptr<impeller::Texture> result_texture = dest_texture;
507 if (resize_info.has_value()) {
508 impeller::TextureDescriptor resize_desc;
510 resize_desc.format = image_info.format;
511 resize_desc.size = {resize_info->width(), resize_info->height()};
512 resize_desc.mip_count = resize_desc.size.MipCount();
514 if (context->GetBackendType() == impeller::Context::BackendType::kMetal) {
515 // Resizing requires a MPS on Metal platforms.
518 }
519 auto resize_texture =
520 context->GetResourceAllocator()->CreateTexture(resize_desc);
521 if (!resize_texture) {
522 std::string decode_error("Could not create resized Impeller texture.");
523 FML_DLOG(ERROR) << decode_error;
524 return std::make_pair(nullptr, decode_error);
525 }
526
527 blit_pass->ResizeTexture(/*source=*/dest_texture,
528 /*destination=*/resize_texture);
529 if (resize_desc.mip_count > 1) {
530 blit_pass->GenerateMipmap(resize_texture);
531 }
532
533 result_texture = std::move(resize_texture);
534 }
535 blit_pass->EncodeCommands();
536 if (!context->GetCommandQueue()
537 ->Submit(
538 {command_buffer},
540 if (status == impeller::CommandBuffer::Status::kError) {
541 FML_LOG(ERROR)
542 << "GPU Error submitting image decoding command buffer.";
543 }
544 },
545 /*block_on_schedule=*/true)
546 .ok()) {
547 std::string decode_error("Failed to submit image decoding command buffer.");
548 FML_DLOG(ERROR) << decode_error;
549 return std::make_pair(nullptr, decode_error);
550 }
551
552 // Flush the pending command buffer to ensure that its output becomes visible
553 // to the raster thread.
554 if (context->AddTrackingFence(result_texture)) {
555 command_buffer->WaitUntilScheduled();
556 } else {
557 command_buffer->WaitUntilCompleted();
558 }
559
560 context->DisposeThreadLocalCachedResources();
561
562 return std::make_pair(
563 impeller::DlImageImpeller::Make(std::move(result_texture)),
564 std::string());
565}
566
567void ImageDecoderImpeller::UploadTextureToPrivate(
568 ImageResult result,
569 const std::shared_ptr<impeller::Context>& context,
570 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
571 const ImageDecoderImpeller::ImageInfo& image_info,
572 const std::optional<SkImageInfo>& resize_info,
573 const std::shared_ptr<const fml::SyncSwitch>& gpu_disabled_switch) {
574 TRACE_EVENT0("impeller", __FUNCTION__);
575 if (!context) {
576 result(nullptr, "No Impeller context is available");
577 return;
578 }
579 if (!buffer) {
580 result(nullptr, "No Impeller device buffer is available");
581 return;
582 }
583
584 gpu_disabled_switch->Execute(
586 .SetIfFalse([&result, context, buffer, image_info, resize_info] {
587 sk_sp<DlImage> image;
588 std::string decode_error;
589 std::tie(image, decode_error) = std::tie(image, decode_error) =
590 UnsafeUploadTextureToPrivate(context, buffer, image_info,
591 resize_info);
592 result(image, decode_error);
593 })
594 .SetIfTrue([&result, context, buffer, image_info, resize_info] {
595 auto result_ptr = std::make_shared<ImageResult>(std::move(result));
596 context->StoreTaskForGPU(
597 [result_ptr, context, buffer, image_info, resize_info]() {
598 sk_sp<DlImage> image;
599 std::string decode_error;
600 std::tie(image, decode_error) = UnsafeUploadTextureToPrivate(
601 context, buffer, image_info, resize_info);
602 (*result_ptr)(image, decode_error);
603 },
604 [result_ptr]() {
605 (*result_ptr)(
606 nullptr,
607 "Image upload failed due to loss of GPU access.");
608 });
609 }));
610}
611
612std::pair<sk_sp<DlImage>, std::string>
613ImageDecoderImpeller::UploadTextureToStorage(
614 const std::shared_ptr<impeller::Context>& context,
615 std::shared_ptr<SkBitmap> bitmap) {
616 TRACE_EVENT0("impeller", __FUNCTION__);
617 if (!context) {
618 return std::make_pair(nullptr, "No Impeller context is available");
619 }
620 if (!bitmap) {
621 return std::make_pair(nullptr, "No texture bitmap is available");
622 }
623 const auto image_info = bitmap->info();
624 const auto pixel_format = ToPixelFormat(image_info.colorType());
625 if (!pixel_format) {
626 std::string decode_error(
627 std::format("Unsupported pixel format (SkColorType={})",
628 static_cast<int>(image_info.colorType())));
629 FML_DLOG(ERROR) << decode_error;
630 return std::make_pair(nullptr, decode_error);
631 }
632
633 impeller::TextureDescriptor texture_descriptor;
635 texture_descriptor.format = pixel_format.value();
636 texture_descriptor.size = {image_info.width(), image_info.height()};
637 texture_descriptor.mip_count = 1;
638
639 auto texture =
640 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
641 if (!texture) {
642 std::string decode_error("Could not create Impeller texture.");
643 FML_DLOG(ERROR) << decode_error;
644 return std::make_pair(nullptr, decode_error);
645 }
646
647 auto mapping = std::make_shared<fml::NonOwnedMapping>(
648 reinterpret_cast<const uint8_t*>(bitmap->getAddr(0, 0)), // data
649 texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
650 [bitmap](auto, auto) mutable { bitmap.reset(); } // proc
651 );
652
653 if (!texture->SetContents(mapping)) {
654 std::string decode_error("Could not copy contents into Impeller texture.");
655 FML_DLOG(ERROR) << decode_error;
656 return std::make_pair(nullptr, decode_error);
657 }
658
659 texture->SetLabel(
660 std::format("ui.Image({})", static_cast<const void*>(texture.get()))
661 .c_str());
662
663 context->DisposeThreadLocalCachedResources();
664
665 return std::make_pair(impeller::DlImageImpeller::Make(std::move(texture)),
666 std::string());
667}
668
669// |ImageDecoder|
670void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
671 const ImageDecoder::Options& options,
672 const ImageResult& p_result) {
673 FML_DCHECK(descriptor);
674 FML_DCHECK(p_result);
675
676 // Wrap the result callback so that it can be invoked from any thread.
677 auto raw_descriptor = descriptor.get();
678 raw_descriptor->AddRef();
679 ImageResult result = [p_result, //
680 raw_descriptor, //
681 ui_runner = runners_.GetUITaskRunner() //
682 ](const auto& image, const auto& decode_error) {
683 ui_runner->PostTask([raw_descriptor, p_result, image, decode_error]() {
684 raw_descriptor->Release();
685 p_result(std::move(image), decode_error);
686 });
687 };
688
689 concurrent_task_runner_->PostTask(
690 [raw_descriptor, //
691 context = context_.get(), //
692 options,
693 io_runner = runners_.GetIOTaskRunner(), //
694 result,
695 wide_gamut_enabled = wide_gamut_enabled_, //
696 gpu_disabled_switch = gpu_disabled_switch_]() {
697#if FML_OS_IOS_SIMULATOR
698 // No-op backend.
699 if (!context) {
700 return;
701 }
702#endif // FML_OS_IOS_SIMULATOR
703
704 if (!context) {
705 result(nullptr, "No Impeller context is available");
706 return;
707 }
708 auto max_size_supported =
709 context->GetResourceAllocator()->GetMaxTextureSizeSupported();
710
711 // Always decompress on the concurrent runner.
712 auto bitmap_result = DecompressTexture(
713 raw_descriptor, options, max_size_supported,
714 /*supports_wide_gamut=*/wide_gamut_enabled &&
715 context->GetCapabilities()->SupportsExtendedRangeFormats(),
716 context->GetCapabilities(), context->GetResourceAllocator());
717 if (!bitmap_result.ok()) {
718 result(nullptr, std::string(bitmap_result.status().message()));
719 return;
720 }
721
722 auto upload_texture_and_invoke_result = [result, context, bitmap_result,
723 gpu_disabled_switch]() {
724 UploadTextureToPrivate(result, context, //
725 bitmap_result->device_buffer, //
726 bitmap_result->image_info, //
727 bitmap_result->resize_info, //
728 gpu_disabled_switch //
729 );
730 };
731 // The I/O image uploads are not threadsafe on GLES.
732 if (context->GetBackendType() ==
734 io_runner->PostTask(upload_texture_and_invoke_result);
735 } else {
736 upload_texture_and_invoke_result();
737 }
738 });
739}
740
741ImpellerAllocator::ImpellerAllocator(
742 std::shared_ptr<impeller::Allocator> allocator)
743 : allocator_(std::move(allocator)) {}
744
745std::shared_ptr<impeller::DeviceBuffer> ImpellerAllocator::GetDeviceBuffer()
746 const {
747 return buffer_;
748}
749
751 if (!bitmap) {
752 return false;
753 }
754 const SkImageInfo& info = bitmap->info();
755 if (kUnknown_SkColorType == info.colorType() || info.width() < 0 ||
756 info.height() < 0 || !info.validRowBytes(bitmap->rowBytes())) {
757 return false;
758 }
759
762 descriptor.size = ((bitmap->height() - 1) * bitmap->rowBytes()) +
763 (bitmap->width() * bitmap->bytesPerPixel());
764
765 std::shared_ptr<impeller::DeviceBuffer> device_buffer =
766 allocator_->CreateBuffer(descriptor);
767 if (!device_buffer) {
768 return false;
769 }
770
771 struct ImpellerPixelRef final : public SkPixelRef {
772 ImpellerPixelRef(int w, int h, void* s, size_t r)
773 : SkPixelRef(w, h, s, r) {}
774
775 ~ImpellerPixelRef() override {}
776 };
777
778 auto pixel_ref = sk_sp<SkPixelRef>(
779 new ImpellerPixelRef(info.width(), info.height(),
780 device_buffer->OnGetContents(), bitmap->rowBytes()));
781
782 bitmap->setPixelRef(std::move(pixel_ref), 0, 0);
783 buffer_ = std::move(device_buffer);
784 return true;
785}
786
787} // 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)