Skip to content

Commit

Permalink
Add image generator registry (flutter#25987)
Browse files Browse the repository at this point in the history
Add image generator protocol and priority registry.

The protocol supports multi-frame images as well as optional
subpixel/downsampled decoding.
  • Loading branch information
bdero authored May 17, 2021
1 parent 5e487ba commit 4cab492
Show file tree
Hide file tree
Showing 19 changed files with 690 additions and 55 deletions.
5 changes: 5 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ FILE: ../../../flutter/lib/ui/painting/image_encoding.h
FILE: ../../../flutter/lib/ui/painting/image_encoding_unittests.cc
FILE: ../../../flutter/lib/ui/painting/image_filter.cc
FILE: ../../../flutter/lib/ui/painting/image_filter.h
FILE: ../../../flutter/lib/ui/painting/image_generator.cc
FILE: ../../../flutter/lib/ui/painting/image_generator.h
FILE: ../../../flutter/lib/ui/painting/image_generator_registry.cc
FILE: ../../../flutter/lib/ui/painting/image_generator_registry.h
FILE: ../../../flutter/lib/ui/painting/image_generator_registry_unittests.cc
FILE: ../../../flutter/lib/ui/painting/image_shader.cc
FILE: ../../../flutter/lib/ui/painting/image_shader.h
FILE: ../../../flutter/lib/ui/painting/immutable_buffer.cc
Expand Down
5 changes: 5 additions & 0 deletions lib/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ source_set("ui") {
"painting/image_encoding.h",
"painting/image_filter.cc",
"painting/image_filter.h",
"painting/image_generator.cc",
"painting/image_generator.h",
"painting/image_generator_registry.cc",
"painting/image_generator_registry.h",
"painting/image_shader.cc",
"painting/image_shader.h",
"painting/immutable_buffer.cc",
Expand Down Expand Up @@ -197,6 +201,7 @@ if (enable_unittests) {
sources = [
"painting/image_dispose_unittests.cc",
"painting/image_encoding_unittests.cc",
"painting/image_generator_registry_unittests.cc",
"painting/path_unittests.cc",
"painting/vertices_unittests.cc",
"window/platform_configuration_unittests.cc",
Expand Down
114 changes: 114 additions & 0 deletions lib/ui/painting/image_generator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

namespace flutter {

ImageGenerator::~ImageGenerator() = default;

BuiltinSkiaImageGenerator::~BuiltinSkiaImageGenerator() = default;

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

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

unsigned int BuiltinSkiaImageGenerator::GetFrameCount() const {
return 1;
}

const ImageGenerator::FrameInfo BuiltinSkiaImageGenerator::GetFrameInfo(
unsigned int frame_index) const {
return {.required_frame = std::nullopt,
.duration = 0,
.disposal_method = SkCodecAnimation::DisposalMethod::kKeep};
}

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

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

BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default;

std::unique_ptr<ImageGenerator> BuiltinSkiaImageGenerator::MakeFromGenerator(
std::unique_ptr<SkImageGenerator> generator) {
if (!generator) {
return nullptr;
}
return std::make_unique<BuiltinSkiaImageGenerator>(std::move(generator));
}

BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
std::unique_ptr<SkCodec> codec)
: codec_generator_(static_cast<SkCodecImageGenerator*>(
SkCodecImageGenerator::MakeFromCodec(std::move(codec)).release())) {}

BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator(
sk_sp<SkData> buffer)
: codec_generator_(static_cast<SkCodecImageGenerator*>(
SkCodecImageGenerator::MakeFromEncodedCodec(buffer).release())) {}

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

unsigned int BuiltinSkiaCodecImageGenerator::GetFrameCount() const {
return codec_generator_->getFrameCount();
}

const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo(
unsigned int frame_index) const {
SkCodec::FrameInfo info;
codec_generator_->getFrameInfo(frame_index, &info);
return {
.required_frame = info.fRequiredFrame == SkCodec::kNoFrame
? std::nullopt
: std::optional<unsigned int>(info.fRequiredFrame),
.duration = static_cast<unsigned int>(info.fDuration),
.disposal_method = info.fDisposalMethod};
}

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

bool BuiltinSkiaCodecImageGenerator::GetPixels(
const SkImageInfo& info,
void* pixels,
size_t row_bytes,
unsigned int frame_index,
std::optional<unsigned int> prior_frame) const {
SkCodec::Options options;
options.fFrameIndex = frame_index;
if (prior_frame.has_value()) {
options.fPriorFrame = prior_frame.value();
}
return codec_generator_->getPixels(info, pixels, row_bytes, &options);
}

std::unique_ptr<ImageGenerator> BuiltinSkiaCodecImageGenerator::MakeFromData(
sk_sp<SkData> data) {
auto codec = SkCodec::MakeFromData(data);
if (!codec) {
return nullptr;
}
return std::make_unique<BuiltinSkiaCodecImageGenerator>(std::move(codec));
}

} // namespace flutter
185 changes: 185 additions & 0 deletions lib/ui/painting/image_generator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_LIB_UI_PAINTING_IMAGE_GENERATOR_H_
#define FLUTTER_LIB_UI_PAINTING_IMAGE_GENERATOR_H_

#include "flutter/fml/macros.h"
#include "flutter/lib/ui/painting/codec.h"
#include "third_party/skia/include/core/SkImageInfo.h"

namespace flutter {

/// @brief The minimal interface necessary for defining a decoder that can be
/// used for both single and multi-frame image decoding. Image
/// generators can also optionally support decoding into a subscaled
/// buffer. Implementers of `ImageGenerator` regularly keep internal
/// state which is not thread safe, and so aliasing and parallel access
/// should never be done with `ImageGenerator`s.
/// @see `ImageGenerator::GetScaledDimensions`
class ImageGenerator {
public:
/// @brief Info about a single frame in the context of a multi-frame image,
/// useful for animation and blending.
struct FrameInfo {
/// The frame index of the frame that, if any, this frame needs to be
/// blended with.
std::optional<unsigned int> required_frame;

/// Number of milliseconds to show this frame.
unsigned int duration;

/// How this frame should be modified before decoding the next one.
SkCodecAnimation::DisposalMethod disposal_method;
};

virtual ~ImageGenerator();

/// @brief Returns basic information about the contents of the encoded
/// image. This information can almost always be collected by just
/// interpreting the header of a decoded image.
/// @return Size and color information describing the image.
/// @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;

/// @brief Get the number of frames that the encoded image stores. This
/// method is always expected to be called before `GetFrameInfo`, as
/// the underlying image decoder may interpret frame information that
/// is then used when calling `GetFrameInfo`.
/// @return The number of frames that the encoded image stores. This will
/// always be 1 for single-frame images.
virtual unsigned int GetFrameCount() const = 0;

/// @brief Get information about a single frame in the context of a
/// multi-frame image, useful for animation and frame blending.
/// This method should only ever be called after `GetFrameCount`
/// has been called. This information is nonsensical for
/// single-frame images.
/// @param[in] frame_index The index of the frame to get information about.
/// @return Information about the given frame. If the image is
/// single-frame, a default result is returned.
/// @see `GetFrameCount`
virtual const FrameInfo GetFrameInfo(unsigned int frame_index) const = 0;

/// @brief Given a scale value, find the closest image size that can be
/// used for efficiently decoding the image. If subpixel image
/// decoding is not supported by the decoder, this method should
/// just return the original image size.
/// @param[in] scale The desired scale factor of the image for decoding.
/// @return The closest image size that can be used for efficiently
/// decoding the image.
/// @note This method is called prior to `GetPixels` in order to query
/// for supported sizes.
/// @see `GetPixels`
virtual SkISize GetScaledDimensions(float scale) const = 0;

/// @brief Decode the image into a given buffer.
/// @param[in] info The desired size and color info of the decoded
/// image to be returned. The implementation of
/// `GetScaledDimensions` determines which sizes are
/// supported by the image decoder.
/// @param[in] pixels The location where the raw decoded image data
/// should be written.
/// @param[in] row_bytes The total number of bytes that should make up a
/// single row of decoded image data
/// (i.e. width * bytes_per_pixel).
/// @param[in] frame_index Which frame to decode. This is only useful for
/// multi-frame images.
/// @param[in] prior_frame Optional frame index parameter for multi-frame
/// images which specifies the previous frame that
/// should be use for blending. This hints to the
/// decoder that it should use a previously cached
/// frame instead of decoding dependency frame(s).
/// If an empty value is supplied, the decoder should
/// decode any necessary frames first.
/// @return True if the image was successfully decoded.
/// @note This method performs potentially long synchronous work, and so
/// it should never be executed on the UI thread. Image decoders
/// do not require GPU acceleration, and so threads without a GPU
/// context may also be used.
/// @see `GetScaledDimensions`
virtual 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 = 0;
};

class BuiltinSkiaImageGenerator : public ImageGenerator {
public:
~BuiltinSkiaImageGenerator();

BuiltinSkiaImageGenerator(std::unique_ptr<SkImageGenerator> generator);

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

// |ImageGenerator|
unsigned int GetFrameCount() const override;

// |ImageGenerator|
const ImageGenerator::FrameInfo GetFrameInfo(
unsigned int frame_index) const override;

// |ImageGenerator|
SkISize GetScaledDimensions(float desired_scale) const 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;

static std::unique_ptr<ImageGenerator> MakeFromGenerator(
std::unique_ptr<SkImageGenerator> generator);

private:
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BuiltinSkiaImageGenerator);
std::unique_ptr<SkImageGenerator> generator_;
};

class BuiltinSkiaCodecImageGenerator : public ImageGenerator {
public:
~BuiltinSkiaCodecImageGenerator();

BuiltinSkiaCodecImageGenerator(std::unique_ptr<SkCodec> codec);

BuiltinSkiaCodecImageGenerator(sk_sp<SkData> buffer);

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

// |ImageGenerator|
unsigned int GetFrameCount() const override;

// |ImageGenerator|
const ImageGenerator::FrameInfo GetFrameInfo(
unsigned int frame_index) const override;

// |ImageGenerator|
SkISize GetScaledDimensions(float desired_scale) const 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;

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

private:
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BuiltinSkiaCodecImageGenerator);
std::unique_ptr<SkCodecImageGenerator> codec_generator_;
};

} // namespace flutter

#endif // FLUTTER_LIB_UI_PAINTING_IMAGE_GENERATOR_H_
Loading

0 comments on commit 4cab492

Please sign in to comment.