diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index f512b191ae05f..e4a24a0d9630d 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -237,6 +237,7 @@ if (enable_unittests) { "fixtures/hello_loop_2.gif", "fixtures/hello_loop_2.webp", "fixtures/FontManifest.json", + "fixtures/unmultiplied_alpha.png", "fixtures/WideGamutIndexed.png", "//flutter/third_party/txt/third_party/fonts/Roboto-Medium.ttf", ] diff --git a/lib/ui/fixtures/unmultiplied_alpha.png b/lib/ui/fixtures/unmultiplied_alpha.png new file mode 100644 index 0000000000000..0ad70cd1d423e Binary files /dev/null and b/lib/ui/fixtures/unmultiplied_alpha.png differ diff --git a/lib/ui/painting/image_decoder_impeller.cc b/lib/ui/painting/image_decoder_impeller.cc index 7060d33dcb612..76a5b25202f14 100644 --- a/lib/ui/painting/image_decoder_impeller.cc +++ b/lib/ui/painting/image_decoder_impeller.cc @@ -205,6 +205,25 @@ DecompressResult ImageDecoderImpeller::DecompressTexture( bitmap->setImmutable(); } + // If the image is unpremultiplied, fix it. + if (alpha_type == SkAlphaType::kUnpremul_SkAlphaType) { + // Single copy of ImpellerAllocator crashes. + auto premul_allocator = std::make_shared(allocator); + auto premul_bitmap = std::make_shared(); + premul_bitmap->setInfo(bitmap->info().makeAlphaType(kPremul_SkAlphaType)); + if (!premul_bitmap->tryAllocPixels(premul_allocator.get())) { + std::string decode_error( + "Could not allocate intermediate for premultiplication conversion."); + FML_DLOG(ERROR) << decode_error; + return DecompressResult{.decode_error = decode_error}; + } + // readPixels() handles converting pixels to premultiplied form. + bitmap->readPixels(premul_bitmap->pixmap()); + premul_bitmap->setImmutable(); + bitmap_allocator = premul_allocator; + bitmap = premul_bitmap; + } + if (bitmap->dimensions() == target_size) { std::shared_ptr buffer = bitmap_allocator->GetDeviceBuffer(); diff --git a/lib/ui/painting/image_decoder_no_gl_unittests.cc b/lib/ui/painting/image_decoder_no_gl_unittests.cc index 975282328b48b..d935fa2e3e9f9 100644 --- a/lib/ui/painting/image_decoder_no_gl_unittests.cc +++ b/lib/ui/painting/image_decoder_no_gl_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/lib/ui/painting/image_decoder_no_gl_unittests.h" #include "flutter/fml/endianness.h" +#include "include/core/SkColorType.h" namespace flutter { namespace testing { @@ -186,5 +187,41 @@ TEST(ImageDecoderNoGLTest, ImpellerWideGamutIndexedPng) { #endif // IMPELLER_SUPPORTS_RENDERING } +TEST(ImageDecoderNoGLTest, ImepllerUnmultipliedAlphaPng) { +#if defined(OS_FUCHSIA) + GTEST_SKIP() << "Fuchsia can't load the test fixtures."; +#endif + auto data = flutter::testing::OpenFixtureAsSkData("unmultiplied_alpha.png"); + auto image = SkImages::DeferredFromEncodedData(data); + ASSERT_TRUE(image != nullptr); + ASSERT_EQ(SkISize::Make(11, 11), image->dimensions()); + + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); + + auto descriptor = fml::MakeRefCounted(std::move(data), + std::move(generator)); + +#if IMPELLER_SUPPORTS_RENDERING + std::shared_ptr allocator = + std::make_shared(); + std::optional result = + ImageDecoderImpeller::DecompressTexture( + descriptor.get(), SkISize::Make(11, 11), {11, 11}, + /*supports_wide_gamut=*/true, allocator); + ASSERT_EQ(result->image_info.colorType(), kRGBA_8888_SkColorType); + + const SkPixmap& pixmap = result->sk_bitmap->pixmap(); + const uint32_t* pixel_ptr = static_cast(pixmap.addr()); + // Test the upper left pixel is premultiplied and not solid red. + ASSERT_EQ(*pixel_ptr, (uint32_t)0x1000001); + // Test a pixel in the green box is still green. + ASSERT_EQ(*(pixel_ptr + 11 * 4 + 4), (uint32_t)0xFF00FF00); + +#endif // IMPELLER_SUPPORTS_RENDERING +} + } // namespace testing } // namespace flutter