Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b0da68e

Browse files
authored
Reland image encoder (#41632)
This is a reland of #41368 with fix for transparent images and a unit test to verify it. Skia would like to remove `SkImageGenerator::MakeFromEncoded` and this appears to be the only remaining usage. It appears to be easily swapped out for the `SkImages::DeferredFromEncodedData`. (skbug.com/13052) This also removes the use of the internal `SkCodecImageGenerator` for the public `SkCodec` API (which the image generator had just been deferring to anyway). While unbreaking some unit tests, I made a few assertions easier to debug and produce nicer error messages. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent b4551c7 commit b0da68e

File tree

5 files changed

+112
-29
lines changed

5 files changed

+112
-29
lines changed

impeller/image/backends/skia/compressed_image_skia.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
#include "impeller/base/validation.h"
1010
#include "third_party/skia/include/core/SkBitmap.h"
1111
#include "third_party/skia/include/core/SkData.h"
12-
#include "third_party/skia/include/core/SkImageGenerator.h"
12+
#include "third_party/skia/include/core/SkImage.h"
1313
#include "third_party/skia/include/core/SkPixmap.h"
14+
#include "third_party/skia/include/core/SkRefCnt.h"
1415

1516
namespace impeller {
1617

@@ -46,12 +47,12 @@ DecompressedImage CompressedImageSkia::Decode() const {
4647
},
4748
src);
4849

49-
auto generator = SkImageGenerator::MakeFromEncoded(sk_data);
50-
if (!generator) {
50+
auto image = SkImages::DeferredFromEncodedData(sk_data);
51+
if (!image) {
5152
return {};
5253
}
5354

54-
const auto dims = generator->getInfo().dimensions();
55+
const auto dims = image->imageInfo().dimensions();
5556
auto info = SkImageInfo::Make(dims.width(), dims.height(),
5657
kRGBA_8888_SkColorType, kPremul_SkAlphaType);
5758

@@ -61,7 +62,7 @@ DecompressedImage CompressedImageSkia::Decode() const {
6162
return {};
6263
}
6364

64-
if (!generator->getPixels(bitmap->pixmap())) {
65+
if (!image->readPixels(nullptr, bitmap->pixmap(), 0, 0)) {
6566
VALIDATION_LOG << "Could not decompress image into arena.";
6667
return {};
6768
}

lib/ui/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ if (enable_unittests) {
230230
"fixtures/DisplayP3Logo.png",
231231
"fixtures/Horizontal.jpg",
232232
"fixtures/Horizontal.png",
233+
"fixtures/heart_end.png",
233234
"fixtures/hello_loop_2.gif",
234235
"fixtures/hello_loop_2.webp",
235236
"fixtures/FontManifest.json",

lib/ui/painting/image_decoder_unittests.cc

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,8 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) {
838838
auto data = OpenFixtureAsSkData("Horizontal.jpg");
839839
auto image = SkImages::DeferredFromEncodedData(data);
840840
ASSERT_TRUE(image != nullptr);
841-
ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());
841+
ASSERT_EQ(600, image->width());
842+
ASSERT_EQ(200, image->height());
842843

843844
ImageGeneratorRegistry registry;
844845
std::shared_ptr<ImageGenerator> generator =
@@ -847,27 +848,47 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) {
847848

848849
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
849850
std::move(generator));
850-
851-
ASSERT_EQ(ImageDecoderSkia::ImageFromCompressedData(
852-
descriptor.get(), 6, 2, fml::tracing::TraceFlow(""))
853-
->dimensions(),
854-
SkISize::Make(6, 2));
851+
auto compressed_image = ImageDecoderSkia::ImageFromCompressedData(
852+
descriptor.get(), 6, 2, fml::tracing::TraceFlow(""));
853+
ASSERT_EQ(compressed_image->width(), 6);
854+
ASSERT_EQ(compressed_image->height(), 2);
855+
ASSERT_EQ(compressed_image->alphaType(), kOpaque_SkAlphaType);
855856

856857
#if IMPELLER_SUPPORTS_RENDERING
857858
std::shared_ptr<impeller::Allocator> allocator =
858859
std::make_shared<impeller::TestImpellerAllocator>();
859860
auto result_1 = ImageDecoderImpeller::DecompressTexture(
860861
descriptor.get(), SkISize::Make(6, 2), {100, 100},
861862
/*supports_wide_gamut=*/false, allocator);
862-
ASSERT_EQ(result_1->sk_bitmap->dimensions(), SkISize::Make(6, 2));
863+
ASSERT_EQ(result_1->sk_bitmap->width(), 6);
864+
ASSERT_EQ(result_1->sk_bitmap->height(), 2);
863865

864866
auto result_2 = ImageDecoderImpeller::DecompressTexture(
865867
descriptor.get(), SkISize::Make(60, 20), {10, 10},
866868
/*supports_wide_gamut=*/false, allocator);
867-
ASSERT_EQ(result_2->sk_bitmap->dimensions(), SkISize::Make(10, 10));
869+
ASSERT_EQ(result_2->sk_bitmap->width(), 10);
870+
ASSERT_EQ(result_2->sk_bitmap->height(), 10);
868871
#endif // IMPELLER_SUPPORTS_RENDERING
869872
}
870873

874+
TEST(ImageDecoderTest, ImagesWithTransparencyArePremulAlpha) {
875+
auto data = OpenFixtureAsSkData("heart_end.png");
876+
ASSERT_TRUE(data);
877+
ImageGeneratorRegistry registry;
878+
std::shared_ptr<ImageGenerator> generator =
879+
registry.CreateCompatibleGenerator(data);
880+
ASSERT_TRUE(generator);
881+
882+
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
883+
std::move(generator));
884+
auto compressed_image = ImageDecoderSkia::ImageFromCompressedData(
885+
descriptor.get(), 250, 250, fml::tracing::TraceFlow(""));
886+
ASSERT_TRUE(compressed_image);
887+
ASSERT_EQ(compressed_image->width(), 250);
888+
ASSERT_EQ(compressed_image->height(), 250);
889+
ASSERT_EQ(compressed_image->alphaType(), kPremul_SkAlphaType);
890+
}
891+
871892
TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) {
872893
auto data = OpenFixtureAsSkData("Horizontal.jpg");
873894

@@ -878,9 +899,15 @@ TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) {
878899
auto descriptor =
879900
fml::MakeRefCounted<ImageDescriptor>(data, std::move(generator));
880901

902+
// If Exif metadata is ignored, the height and width will be swapped because
903+
// "Rotate 90 CW" is what is encoded there.
904+
ASSERT_EQ(600, descriptor->width());
905+
ASSERT_EQ(200, descriptor->height());
906+
881907
auto image = SkImages::DeferredFromEncodedData(data);
882908
ASSERT_TRUE(image != nullptr);
883-
ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());
909+
ASSERT_EQ(600, image->width());
910+
ASSERT_EQ(200, image->height());
884911

885912
auto decode = [descriptor](uint32_t target_width, uint32_t target_height) {
886913
return ImageDecoderSkia::ImageFromCompressedData(
@@ -894,8 +921,9 @@ TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) {
894921

895922
auto assert_image = [&](auto decoded_image) {
896923
ASSERT_EQ(decoded_image->dimensions(), SkISize::Make(300, 100));
897-
ASSERT_TRUE(SkPngEncoder::Encode(nullptr, decoded_image.get(), {})
898-
->equals(expected_data.get()));
924+
sk_sp<SkData> encoded =
925+
SkPngEncoder::Encode(nullptr, decoded_image.get(), {});
926+
ASSERT_TRUE(encoded->equals(expected_data.get()));
899927
};
900928

901929
assert_image(decode(300, 100));

lib/ui/painting/image_generator.cc

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <utility>
88

99
#include "flutter/fml/logging.h"
10+
#include "third_party/skia/include/codec/SkEncodedOrigin.h"
11+
#include "third_party/skia/include/codec/SkPixmapUtils.h"
1012
#include "third_party/skia/include/core/SkBitmap.h"
1113
#include "third_party/skia/include/core/SkImage.h"
1214

@@ -81,34 +83,47 @@ std::unique_ptr<ImageGenerator> BuiltinSkiaImageGenerator::MakeFromGenerator(
8183

8284
BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default;
8385

86+
static SkImageInfo getInfoIncludingExif(SkCodec* codec) {
87+
SkImageInfo info = codec->getInfo();
88+
if (SkEncodedOriginSwapsWidthHeight(codec->getOrigin())) {
89+
info = SkPixmapUtils::SwapWidthHeight(info);
90+
}
91+
if (kUnpremul_SkAlphaType == info.alphaType()) {
92+
// Prefer premul over unpremul (this produces better filtering in general)
93+
info = info.makeAlphaType(kPremul_SkAlphaType);
94+
}
95+
return info;
96+
}
97+
8498
BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
8599
std::unique_ptr<SkCodec> codec)
86-
: codec_generator_(static_cast<SkCodecImageGenerator*>(
87-
SkCodecImageGenerator::MakeFromCodec(std::move(codec)).release())) {}
100+
: codec_(std::move(codec)) {
101+
image_info_ = getInfoIncludingExif(codec_.get());
102+
}
88103

89104
BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
90105
sk_sp<SkData> buffer)
91-
: codec_generator_(static_cast<SkCodecImageGenerator*>(
92-
SkCodecImageGenerator::MakeFromEncodedCodec(std::move(buffer))
93-
.release())) {}
106+
: codec_(SkCodec::MakeFromData(std::move(buffer)).release()) {
107+
image_info_ = getInfoIncludingExif(codec_.get());
108+
}
94109

95110
const SkImageInfo& BuiltinSkiaCodecImageGenerator::GetInfo() {
96-
return codec_generator_->getInfo();
111+
return image_info_;
97112
}
98113

99114
unsigned int BuiltinSkiaCodecImageGenerator::GetFrameCount() const {
100-
return codec_generator_->getFrameCount();
115+
return codec_->getFrameCount();
101116
}
102117

103118
unsigned int BuiltinSkiaCodecImageGenerator::GetPlayCount() const {
104-
auto repetition_count = codec_generator_->getRepetitionCount();
119+
auto repetition_count = codec_->getRepetitionCount();
105120
return repetition_count < 0 ? kInfinitePlayCount : repetition_count + 1;
106121
}
107122

108123
const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo(
109124
unsigned int frame_index) {
110125
SkCodec::FrameInfo info = {};
111-
codec_generator_->getFrameInfo(frame_index, &info);
126+
codec_->getFrameInfo(frame_index, &info);
112127
return {
113128
.required_frame = info.fRequiredFrame == SkCodec::kNoFrame
114129
? std::nullopt
@@ -119,7 +134,11 @@ const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo(
119134

120135
SkISize BuiltinSkiaCodecImageGenerator::GetScaledDimensions(
121136
float desired_scale) {
122-
return codec_generator_->getScaledDimensions(desired_scale);
137+
SkISize size = codec_->getScaledDimensions(desired_scale);
138+
if (SkEncodedOriginSwapsWidthHeight(codec_->getOrigin())) {
139+
std::swap(size.fWidth, size.fHeight);
140+
}
141+
return size;
123142
}
124143

125144
bool BuiltinSkiaCodecImageGenerator::GetPixels(
@@ -133,7 +152,40 @@ bool BuiltinSkiaCodecImageGenerator::GetPixels(
133152
if (prior_frame.has_value()) {
134153
options.fPriorFrame = prior_frame.value();
135154
}
136-
return codec_generator_->getPixels(info, pixels, row_bytes, &options);
155+
SkEncodedOrigin origin = codec_->getOrigin();
156+
157+
SkPixmap output_pixmap(info, pixels, row_bytes);
158+
SkPixmap temp_pixmap;
159+
SkBitmap temp_bitmap;
160+
if (origin == kTopLeft_SkEncodedOrigin) {
161+
// We can decode directly into the output buffer.
162+
temp_pixmap = output_pixmap;
163+
} else {
164+
// We need to decode into a different buffer so we can re-orient
165+
// the pixels later.
166+
SkImageInfo temp_info = output_pixmap.info();
167+
if (SkEncodedOriginSwapsWidthHeight(origin)) {
168+
// We'll be decoding into a buffer that has height and width swapped.
169+
temp_info = SkPixmapUtils::SwapWidthHeight(temp_info);
170+
}
171+
if (!temp_bitmap.tryAllocPixels(temp_info)) {
172+
FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size "
173+
<< temp_info.computeMinByteSize() << "B";
174+
return false;
175+
}
176+
temp_pixmap = temp_bitmap.pixmap();
177+
}
178+
179+
SkCodec::Result result = codec_->getPixels(temp_pixmap, &options);
180+
if (result != SkCodec::kSuccess) {
181+
FML_DLOG(WARNING) << "codec could not get pixels. "
182+
<< SkCodec::ResultToString(result);
183+
return false;
184+
}
185+
if (origin == kTopLeft_SkEncodedOrigin) {
186+
return true;
187+
}
188+
return SkPixmapUtils::Orient(output_pixmap, temp_pixmap, origin);
137189
}
138190

139191
std::unique_ptr<ImageGenerator> BuiltinSkiaCodecImageGenerator::MakeFromData(

lib/ui/painting/image_generator.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
#include "third_party/skia/include/codec/SkCodecAnimation.h"
1212
#include "third_party/skia/include/core/SkData.h"
1313
#include "third_party/skia/include/core/SkImage.h"
14+
#include "third_party/skia/include/core/SkImageGenerator.h"
1415
#include "third_party/skia/include/core/SkImageInfo.h"
1516
#include "third_party/skia/include/core/SkSize.h"
16-
#include "third_party/skia/src/codec/SkCodecImageGenerator.h" // nogncheck
1717

1818
namespace flutter {
1919

@@ -213,7 +213,8 @@ class BuiltinSkiaCodecImageGenerator : public ImageGenerator {
213213

214214
private:
215215
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BuiltinSkiaCodecImageGenerator);
216-
std::unique_ptr<SkCodecImageGenerator> codec_generator_;
216+
std::unique_ptr<SkCodec> codec_;
217+
SkImageInfo image_info_;
217218
};
218219

219220
} // namespace flutter

0 commit comments

Comments
 (0)