diff --git a/impeller/image/backends/skia/compressed_image_skia.cc b/impeller/image/backends/skia/compressed_image_skia.cc index 49630583446f2..66d917e4ab8b7 100644 --- a/impeller/image/backends/skia/compressed_image_skia.cc +++ b/impeller/image/backends/skia/compressed_image_skia.cc @@ -9,8 +9,9 @@ #include "impeller/base/validation.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkData.h" -#include "third_party/skia/include/core/SkImageGenerator.h" +#include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkPixmap.h" +#include "third_party/skia/include/core/SkRefCnt.h" namespace impeller { @@ -46,12 +47,12 @@ DecompressedImage CompressedImageSkia::Decode() const { }, src); - auto generator = SkImageGenerator::MakeFromEncoded(sk_data); - if (!generator) { + auto image = SkImages::DeferredFromEncodedData(sk_data); + if (!image) { return {}; } - const auto dims = generator->getInfo().dimensions(); + const auto dims = image->imageInfo().dimensions(); auto info = SkImageInfo::Make(dims.width(), dims.height(), kRGBA_8888_SkColorType, kPremul_SkAlphaType); @@ -61,7 +62,7 @@ DecompressedImage CompressedImageSkia::Decode() const { return {}; } - if (!generator->getPixels(bitmap->pixmap())) { + if (!image->readPixels(nullptr, bitmap->pixmap(), 0, 0)) { VALIDATION_LOG << "Could not decompress image into arena."; return {}; } diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index 571b96c7d4a84..5ddee5b49bd11 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc @@ -838,7 +838,8 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) { auto data = OpenFixtureAsSkData("Horizontal.jpg"); auto image = SkImages::DeferredFromEncodedData(data); ASSERT_TRUE(image != nullptr); - ASSERT_EQ(SkISize::Make(600, 200), image->dimensions()); + ASSERT_EQ(600, image->width()); + ASSERT_EQ(200, image->height()); ImageGeneratorRegistry registry; std::shared_ptr generator = @@ -847,11 +848,10 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) { auto descriptor = fml::MakeRefCounted(std::move(data), std::move(generator)); - - ASSERT_EQ(ImageDecoderSkia::ImageFromCompressedData( - descriptor.get(), 6, 2, fml::tracing::TraceFlow("")) - ->dimensions(), - SkISize::Make(6, 2)); + auto compressed_image = ImageDecoderSkia::ImageFromCompressedData( + descriptor.get(), 6, 2, fml::tracing::TraceFlow("")); + ASSERT_EQ(compressed_image->width(), 6); + ASSERT_EQ(compressed_image->height(), 2); #if IMPELLER_SUPPORTS_RENDERING std::shared_ptr allocator = @@ -859,12 +859,14 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) { auto result_1 = ImageDecoderImpeller::DecompressTexture( descriptor.get(), SkISize::Make(6, 2), {100, 100}, /*supports_wide_gamut=*/false, allocator); - ASSERT_EQ(result_1->sk_bitmap->dimensions(), SkISize::Make(6, 2)); + ASSERT_EQ(result_1->sk_bitmap->width(), 6); + ASSERT_EQ(result_1->sk_bitmap->height(), 2); auto result_2 = ImageDecoderImpeller::DecompressTexture( descriptor.get(), SkISize::Make(60, 20), {10, 10}, /*supports_wide_gamut=*/false, allocator); - ASSERT_EQ(result_2->sk_bitmap->dimensions(), SkISize::Make(10, 10)); + ASSERT_EQ(result_2->sk_bitmap->width(), 10); + ASSERT_EQ(result_2->sk_bitmap->height(), 10); #endif // IMPELLER_SUPPORTS_RENDERING } @@ -878,9 +880,15 @@ TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) { auto descriptor = fml::MakeRefCounted(data, std::move(generator)); + // If Exif metadata is ignored, the height and width will be swapped because + // "Rotate 90 CW" is what is encoded there. + ASSERT_EQ(600, descriptor->width()); + ASSERT_EQ(200, descriptor->height()); + auto image = SkImages::DeferredFromEncodedData(data); ASSERT_TRUE(image != nullptr); - ASSERT_EQ(SkISize::Make(600, 200), image->dimensions()); + ASSERT_EQ(600, image->width()); + ASSERT_EQ(200, image->height()); auto decode = [descriptor](uint32_t target_width, uint32_t target_height) { return ImageDecoderSkia::ImageFromCompressedData( @@ -894,8 +902,9 @@ TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) { auto assert_image = [&](auto decoded_image) { ASSERT_EQ(decoded_image->dimensions(), SkISize::Make(300, 100)); - ASSERT_TRUE(SkPngEncoder::Encode(nullptr, decoded_image.get(), {}) - ->equals(expected_data.get())); + sk_sp encoded = + SkPngEncoder::Encode(nullptr, decoded_image.get(), {}); + ASSERT_TRUE(encoded->equals(expected_data.get())); }; assert_image(decode(300, 100)); diff --git a/lib/ui/painting/image_generator.cc b/lib/ui/painting/image_generator.cc index c86e53ac4fd97..949cfc9648605 100644 --- a/lib/ui/painting/image_generator.cc +++ b/lib/ui/painting/image_generator.cc @@ -7,6 +7,8 @@ #include #include "flutter/fml/logging.h" +#include "third_party/skia/include/codec/SkEncodedOrigin.h" +#include "third_party/skia/include/codec/SkPixmapUtils.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkImage.h" @@ -81,34 +83,43 @@ std::unique_ptr BuiltinSkiaImageGenerator::MakeFromGenerator( BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default; +static SkImageInfo getInfoIncludingExif(SkCodec* codec) { + SkImageInfo info = codec->getInfo(); + if (SkEncodedOriginSwapsWidthHeight(codec->getOrigin())) { + info = SkPixmapUtils::SwapWidthHeight(info); + } + return info; +} + BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator( std::unique_ptr codec) - : codec_generator_(static_cast( - SkCodecImageGenerator::MakeFromCodec(std::move(codec)).release())) {} + : codec_(std::move(codec)) { + image_info_ = getInfoIncludingExif(codec_.get()); +} BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator( sk_sp buffer) - : codec_generator_(static_cast( - SkCodecImageGenerator::MakeFromEncodedCodec(std::move(buffer)) - .release())) {} + : codec_(SkCodec::MakeFromData(std::move(buffer)).release()) { + image_info_ = getInfoIncludingExif(codec_.get()); +} const SkImageInfo& BuiltinSkiaCodecImageGenerator::GetInfo() { - return codec_generator_->getInfo(); + return image_info_; } unsigned int BuiltinSkiaCodecImageGenerator::GetFrameCount() const { - return codec_generator_->getFrameCount(); + return codec_->getFrameCount(); } unsigned int BuiltinSkiaCodecImageGenerator::GetPlayCount() const { - auto repetition_count = codec_generator_->getRepetitionCount(); + auto repetition_count = codec_->getRepetitionCount(); return repetition_count < 0 ? kInfinitePlayCount : repetition_count + 1; } const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo( unsigned int frame_index) { SkCodec::FrameInfo info = {}; - codec_generator_->getFrameInfo(frame_index, &info); + codec_->getFrameInfo(frame_index, &info); return { .required_frame = info.fRequiredFrame == SkCodec::kNoFrame ? std::nullopt @@ -119,7 +130,11 @@ const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo( SkISize BuiltinSkiaCodecImageGenerator::GetScaledDimensions( float desired_scale) { - return codec_generator_->getScaledDimensions(desired_scale); + SkISize size = codec_->getScaledDimensions(desired_scale); + if (SkEncodedOriginSwapsWidthHeight(codec_->getOrigin())) { + std::swap(size.fWidth, size.fHeight); + } + return size; } bool BuiltinSkiaCodecImageGenerator::GetPixels( @@ -133,7 +148,40 @@ bool BuiltinSkiaCodecImageGenerator::GetPixels( if (prior_frame.has_value()) { options.fPriorFrame = prior_frame.value(); } - return codec_generator_->getPixels(info, pixels, row_bytes, &options); + SkEncodedOrigin origin = codec_->getOrigin(); + + SkPixmap output_pixmap(info, pixels, row_bytes); + SkPixmap temp_pixmap; + SkBitmap temp_bitmap; + if (origin == kTopLeft_SkEncodedOrigin) { + // We can decode directly into the output buffer. + temp_pixmap = output_pixmap; + } else { + // We need to decode into a different buffer so we can re-orient + // the pixels later. + SkImageInfo temp_info = output_pixmap.info(); + if (SkEncodedOriginSwapsWidthHeight(origin)) { + // We'll be decoding into a buffer that has height and width swapped. + temp_info = SkPixmapUtils::SwapWidthHeight(temp_info); + } + if (!temp_bitmap.tryAllocPixels(temp_info)) { + FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size " + << temp_info.computeMinByteSize() << "B"; + return false; + } + temp_pixmap = temp_bitmap.pixmap(); + } + + SkCodec::Result result = codec_->getPixels(temp_pixmap, &options); + if (result != SkCodec::kSuccess) { + FML_DLOG(WARNING) << "codec could not get pixels. " + << SkCodec::ResultToString(result); + return false; + } + if (origin == kTopLeft_SkEncodedOrigin) { + return true; + } + return SkPixmapUtils::Orient(output_pixmap, temp_pixmap, origin); } std::unique_ptr BuiltinSkiaCodecImageGenerator::MakeFromData( diff --git a/lib/ui/painting/image_generator.h b/lib/ui/painting/image_generator.h index a5515c8057319..dcfe4b1f069c7 100644 --- a/lib/ui/painting/image_generator.h +++ b/lib/ui/painting/image_generator.h @@ -11,9 +11,9 @@ #include "third_party/skia/include/codec/SkCodecAnimation.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkImageGenerator.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSize.h" -#include "third_party/skia/src/codec/SkCodecImageGenerator.h" // nogncheck namespace flutter { @@ -213,7 +213,8 @@ class BuiltinSkiaCodecImageGenerator : public ImageGenerator { private: FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BuiltinSkiaCodecImageGenerator); - std::unique_ptr codec_generator_; + std::unique_ptr codec_; + SkImageInfo image_info_; }; } // namespace flutter