Skip to content

Commit

Permalink
Add native Android image decoder supported by API 28+ (flutter#26746)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored Jul 21, 2021
1 parent 9fc7f56 commit 94beed9
Show file tree
Hide file tree
Showing 26 changed files with 596 additions and 91 deletions.
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,8 @@ FILE: ../../../flutter/shell/platform/android/android_environment_gl.h
FILE: ../../../flutter/shell/platform/android/android_exports.lst
FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.cc
FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.h
FILE: ../../../flutter/shell/platform/android/android_image_generator.cc
FILE: ../../../flutter/shell/platform/android/android_image_generator.h
FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc
FILE: ../../../flutter/shell/platform/android/android_shell_holder.h
FILE: ../../../flutter/shell/platform/android/android_surface_gl.cc
Expand Down
11 changes: 11 additions & 0 deletions fml/platform/android/jni_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,17 @@ bool ClearException(JNIEnv* env) {
return true;
}

bool CheckException(JNIEnv* env) {
if (!HasException(env))
return true;

jthrowable exception = env->ExceptionOccurred();
env->ExceptionClear();
FML_LOG(ERROR) << fml::jni::GetJavaExceptionInfo(env, exception);
env->DeleteLocalRef(exception);
return false;
}

std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
ScopedJavaLocalRef<jclass> throwable_clazz(
env, env->FindClass("java/lang/Throwable"));
Expand Down
1 change: 1 addition & 0 deletions fml/platform/android/jni_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ bool HasException(JNIEnv* env);

bool ClearException(JNIEnv* env);

bool CheckException(JNIEnv* env);
std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable);

} // namespace jni
Expand Down
20 changes: 10 additions & 10 deletions lib/ui/painting/image_decoder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class UnknownImageGenerator : public ImageGenerator {
public:
UnknownImageGenerator() : info_(SkImageInfo::MakeUnknown()){};
~UnknownImageGenerator() = default;
const SkImageInfo& GetInfo() const { return info_; }
const SkImageInfo& GetInfo() { return info_; }

unsigned int GetFrameCount() const { return 1; }

Expand All @@ -159,15 +159,15 @@ class UnknownImageGenerator : public ImageGenerator {
return {std::nullopt, 0, SkCodecAnimation::DisposalMethod::kKeep};
}

SkISize GetScaledDimensions(float scale) const {
SkISize GetScaledDimensions(float scale) {
return SkISize::Make(info_.width(), info_.height());
}

bool GetPixels(const SkImageInfo& info,
void* pixels,
size_t row_bytes,
unsigned int frame_index,
std::optional<unsigned int> prior_frame) const {
std::optional<unsigned int> prior_frame) {
return false;
};

Expand Down Expand Up @@ -236,7 +236,7 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) {
ASSERT_GE(data->size(), 0u);

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> generator =
std::shared_ptr<ImageGenerator> generator =
registry.CreateCompatibleGenerator(data);
ASSERT_TRUE(generator);

Expand Down Expand Up @@ -293,7 +293,7 @@ TEST_F(ImageDecoderFixtureTest, ExifDataIsRespectedOnDecode) {
ASSERT_GE(data->size(), 0u);

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> generator =
std::shared_ptr<ImageGenerator> generator =
registry.CreateCompatibleGenerator(data);
ASSERT_TRUE(generator);

Expand Down Expand Up @@ -352,7 +352,7 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithoutAGPUContext) {
ASSERT_GE(data->size(), 0u);

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> generator =
std::shared_ptr<ImageGenerator> generator =
registry.CreateCompatibleGenerator(data);
ASSERT_TRUE(generator);

Expand Down Expand Up @@ -427,7 +427,7 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithResizes) {
ASSERT_GE(data->size(), 0u);

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> generator =
std::shared_ptr<ImageGenerator> generator =
registry.CreateCompatibleGenerator(data);
ASSERT_TRUE(generator);

Expand Down Expand Up @@ -593,7 +593,7 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) {
ASSERT_EQ(SkISize::Make(600, 200), image->dimensions());

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> generator =
std::shared_ptr<ImageGenerator> generator =
registry.CreateCompatibleGenerator(data);
ASSERT_TRUE(generator);

Expand All @@ -610,7 +610,7 @@ TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) {
auto data = OpenFixtureAsSkData("Horizontal.jpg");

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> generator =
std::shared_ptr<ImageGenerator> generator =
registry.CreateCompatibleGenerator(data);
ASSERT_TRUE(generator);
auto descriptor =
Expand Down Expand Up @@ -662,7 +662,7 @@ TEST_F(ImageDecoderFixtureTest,
ASSERT_TRUE(gif_mapping);

ImageGeneratorRegistry registry;
std::unique_ptr<ImageGenerator> gif_generator =
std::shared_ptr<ImageGenerator> gif_generator =
registry.CreateCompatibleGenerator(gif_mapping);
ASSERT_TRUE(gif_generator);

Expand Down
19 changes: 3 additions & 16 deletions lib/ui/painting/image_descriptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
row_bytes_(row_bytes) {}

ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
std::unique_ptr<ImageGenerator> generator)
std::shared_ptr<ImageGenerator> generator)
: buffer_(std::move(buffer)),
generator_(std::move(generator)),
image_info_(CreateImageInfo()),
Expand Down Expand Up @@ -83,7 +83,7 @@ void ImageDescriptor::initEncoded(Dart_NativeArguments args) {
return;
}

std::unique_ptr<ImageGenerator> generator =
auto generator =
registry->CreateCompatibleGenerator(immutable_buffer->data());

if (!generator) {
Expand Down Expand Up @@ -140,20 +140,7 @@ void ImageDescriptor::instantiateCodec(Dart_Handle codec_handle,
}

sk_sp<SkImage> ImageDescriptor::image() const {
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(image_info_)) {
FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size "
<< image_info_.computeMinByteSize() << "B";
return nullptr;
}

const auto& pixmap = bitmap.pixmap();
if (!get_pixels(pixmap)) {
FML_DLOG(ERROR) << "Failed to get pixels for image.";
return nullptr;
}
bitmap.setImmutable();
return SkImage::MakeFromBitmap(bitmap);
return generator_->GetImage();
}

bool ImageDescriptor::get_pixels(const SkPixmap& pixmap) const {
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/painting/image_descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
const SkImageInfo& image_info,
std::optional<size_t> row_bytes);
ImageDescriptor(sk_sp<SkData> buffer,
std::unique_ptr<ImageGenerator> generator);
std::shared_ptr<ImageGenerator> generator);

sk_sp<SkData> buffer_;
std::shared_ptr<ImageGenerator> generator_;
Expand Down
38 changes: 29 additions & 9 deletions lib/ui/painting/image_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,38 @@

#include "flutter/lib/ui/painting/image_generator.h"

#include "flutter/fml/logging.h"

namespace flutter {

ImageGenerator::~ImageGenerator() = default;

sk_sp<SkImage> ImageGenerator::GetImage() {
SkImageInfo info = GetInfo();

SkBitmap bitmap;
if (!bitmap.tryAllocPixels(info)) {
FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size "
<< info.computeMinByteSize() << "B";
return nullptr;
}

const auto& pixmap = bitmap.pixmap();
if (!GetPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes())) {
FML_DLOG(ERROR) << "Failed to get pixels for image.";
return nullptr;
}
bitmap.setImmutable();
return SkImage::MakeFromBitmap(bitmap);
}

BuiltinSkiaImageGenerator::~BuiltinSkiaImageGenerator() = default;

BuiltinSkiaImageGenerator::BuiltinSkiaImageGenerator(
std::unique_ptr<SkImageGenerator> generator)
: generator_(std::move(generator)) {}

const SkImageInfo& BuiltinSkiaImageGenerator::GetInfo() const {
const SkImageInfo& BuiltinSkiaImageGenerator::GetInfo() {
return generator_->getInfo();
}

Expand All @@ -33,8 +54,7 @@ const ImageGenerator::FrameInfo BuiltinSkiaImageGenerator::GetFrameInfo(
.disposal_method = SkCodecAnimation::DisposalMethod::kKeep};
}

SkISize BuiltinSkiaImageGenerator::GetScaledDimensions(
float desired_scale) const {
SkISize BuiltinSkiaImageGenerator::GetScaledDimensions(float desired_scale) {
return generator_->getInfo().dimensions();
}

Expand All @@ -43,12 +63,10 @@ bool BuiltinSkiaImageGenerator::GetPixels(
void* pixels,
size_t row_bytes,
unsigned int frame_index,
std::optional<unsigned int> prior_frame) const {
std::optional<unsigned int> prior_frame) {
return generator_->getPixels(info, pixels, row_bytes);
}

BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default;

std::unique_ptr<ImageGenerator> BuiltinSkiaImageGenerator::MakeFromGenerator(
std::unique_ptr<SkImageGenerator> generator) {
if (!generator) {
Expand All @@ -57,6 +75,8 @@ std::unique_ptr<ImageGenerator> BuiltinSkiaImageGenerator::MakeFromGenerator(
return std::make_unique<BuiltinSkiaImageGenerator>(std::move(generator));
}

BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default;

BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
std::unique_ptr<SkCodec> codec)
: codec_generator_(static_cast<SkCodecImageGenerator*>(
Expand All @@ -67,7 +87,7 @@ BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
: codec_generator_(static_cast<SkCodecImageGenerator*>(
SkCodecImageGenerator::MakeFromEncodedCodec(buffer).release())) {}

const SkImageInfo& BuiltinSkiaCodecImageGenerator::GetInfo() const {
const SkImageInfo& BuiltinSkiaCodecImageGenerator::GetInfo() {
return codec_generator_->getInfo();
}

Expand All @@ -93,7 +113,7 @@ const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo(
}

SkISize BuiltinSkiaCodecImageGenerator::GetScaledDimensions(
float desired_scale) const {
float desired_scale) {
return codec_generator_->getScaledDimensions(desired_scale);
}

Expand All @@ -102,7 +122,7 @@ bool BuiltinSkiaCodecImageGenerator::GetPixels(
void* pixels,
size_t row_bytes,
unsigned int frame_index,
std::optional<unsigned int> prior_frame) const {
std::optional<unsigned int> prior_frame) {
SkCodec::Options options;
options.fFrameIndex = frame_index;
if (prior_frame.has_value()) {
Expand Down
27 changes: 17 additions & 10 deletions lib/ui/painting/image_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class ImageGenerator {
/// @note This method is executed on the UI thread and used for layout
/// purposes by the framework, and so this method should not perform
/// long synchronous tasks.
virtual const SkImageInfo& GetInfo() const = 0;
virtual const SkImageInfo& GetInfo() = 0;

/// @brief Get the number of frames that the encoded image stores. This
/// method is always expected to be called before `GetFrameInfo`, as
Expand Down Expand Up @@ -87,9 +87,11 @@ class ImageGenerator {
/// @note This method is called prior to `GetPixels` in order to query
/// for supported sizes.
/// @see `GetPixels`
virtual SkISize GetScaledDimensions(float scale) const = 0;
virtual SkISize GetScaledDimensions(float scale) = 0;

/// @brief Decode the image into a given buffer.
/// @brief Decode the image into a given buffer. This method is currently
/// always used for sub-pixel image decoding. For full-sized still
/// images, `GetImage` is always attempted first.
/// @param[in] info The desired size and color info of the decoded
/// image to be returned. The implementation of
/// `GetScaledDimensions` determines which sizes are
Expand Down Expand Up @@ -119,7 +121,12 @@ class ImageGenerator {
void* pixels,
size_t row_bytes,
unsigned int frame_index = 0,
std::optional<unsigned int> prior_frame = std::nullopt) const = 0;
std::optional<unsigned int> prior_frame = std::nullopt) = 0;

/// @brief Creates an `SkImage` based on the current `ImageInfo` of this
/// `ImageGenerator`.
/// @return A new `SkImage` containing the decoded image data.
sk_sp<SkImage> GetImage();
};

class BuiltinSkiaImageGenerator : public ImageGenerator {
Expand All @@ -129,7 +136,7 @@ class BuiltinSkiaImageGenerator : public ImageGenerator {
BuiltinSkiaImageGenerator(std::unique_ptr<SkImageGenerator> generator);

// |ImageGenerator|
const SkImageInfo& GetInfo() const override;
const SkImageInfo& GetInfo() override;

// |ImageGenerator|
unsigned int GetFrameCount() const override;
Expand All @@ -142,15 +149,15 @@ class BuiltinSkiaImageGenerator : public ImageGenerator {
unsigned int frame_index) const override;

// |ImageGenerator|
SkISize GetScaledDimensions(float desired_scale) const override;
SkISize GetScaledDimensions(float desired_scale) override;

// |ImageGenerator|
bool GetPixels(
const SkImageInfo& info,
void* pixels,
size_t row_bytes,
unsigned int frame_index = 0,
std::optional<unsigned int> prior_frame = std::nullopt) const override;
std::optional<unsigned int> prior_frame = std::nullopt) override;

static std::unique_ptr<ImageGenerator> MakeFromGenerator(
std::unique_ptr<SkImageGenerator> generator);
Expand All @@ -169,7 +176,7 @@ class BuiltinSkiaCodecImageGenerator : public ImageGenerator {
BuiltinSkiaCodecImageGenerator(sk_sp<SkData> buffer);

// |ImageGenerator|
const SkImageInfo& GetInfo() const override;
const SkImageInfo& GetInfo() override;

// |ImageGenerator|
unsigned int GetFrameCount() const override;
Expand All @@ -182,15 +189,15 @@ class BuiltinSkiaCodecImageGenerator : public ImageGenerator {
unsigned int frame_index) const override;

// |ImageGenerator|
SkISize GetScaledDimensions(float desired_scale) const override;
SkISize GetScaledDimensions(float desired_scale) override;

// |ImageGenerator|
bool GetPixels(
const SkImageInfo& info,
void* pixels,
size_t row_bytes,
unsigned int frame_index = 0,
std::optional<unsigned int> prior_frame = std::nullopt) const override;
std::optional<unsigned int> prior_frame = std::nullopt) override;

static std::unique_ptr<ImageGenerator> MakeFromData(sk_sp<SkData> data);

Expand Down
4 changes: 2 additions & 2 deletions lib/ui/painting/image_generator_registry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void ImageGeneratorRegistry::AddFactory(ImageGeneratorFactory factory,
{factory, priority, fml::tracing::TraceNonce()});
}

std::unique_ptr<ImageGenerator>
std::shared_ptr<ImageGenerator>
ImageGeneratorRegistry::CreateCompatibleGenerator(sk_sp<SkData> buffer) {
if (!image_generator_factories_.size()) {
FML_LOG(WARNING)
Expand All @@ -64,7 +64,7 @@ ImageGeneratorRegistry::CreateCompatibleGenerator(sk_sp<SkData> buffer) {
}

for (auto& factory : image_generator_factories_) {
std::unique_ptr<ImageGenerator> result = factory.callback(buffer);
std::shared_ptr<ImageGenerator> result = factory.callback(buffer);
if (result) {
return result;
}
Expand Down
Loading

0 comments on commit 94beed9

Please sign in to comment.