Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/ui/painting/image_generator_apng.cc
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,13 @@ bool APNGImageGenerator::DemuxNextImageInternal() {
SkCodecAnimation::DisposalMethod::kKeep) {
// Mark the required frame as the previous frame in all cases.
image->frame_info->required_frame = images_.size() - 1;
} else if (images_.size() > (first_frame_index_ + 1) &&
images_.back().frame_info->disposal_method ==
SkCodecAnimation::DisposalMethod::kRestorePrevious) {
// Mark the required frame as the last previous frame
// It is not valid if there are 2 or above frames set |disposal_method| to
// |kRestorePrevious|. But it also works in MultiFrameCodec.
image->frame_info->required_frame = images_.size() - 2;
}

// Calling SkCodec::getInfo at least once prior to decoding is mandatory.
Expand Down
34 changes: 24 additions & 10 deletions lib/ui/painting/multi_frame_codec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,16 @@ MultiFrameCodec::State::GetNextFrameImage(
std::optional<unsigned int> prior_frame_index = std::nullopt;

if (requiredFrameIndex != SkCodec::kNoFrame) {
// We currently assume that frames can only ever depend on the immediately
// previous frame, if any. This means that
// `DisposalMethod::kRestorePrevious` is not supported.
if (lastRequiredFrame_ == nullptr) {
// We are here when the frame said |disposal_method| is
// `DisposalMethod::kKeep` or `DisposalMethod::kRestorePrevious` and
// |requiredFrameIndex| is set to ex-frame or ex-ex-frame.
if (requiredFrameIndex == exExRequiredFrameIndex_ && exExRequiredFrame_) {
if (exExRequiredFrame_->getPixels() &&
CopyToBitmap(&bitmap, exExRequiredFrame_->colorType(),
*exExRequiredFrame_)) {
prior_frame_index = requiredFrameIndex;
}
} else if (lastRequiredFrame_ == nullptr) {
FML_DLOG(INFO)
<< "Frame " << nextFrameIndex_ << " depends on frame "
<< requiredFrameIndex
Expand All @@ -146,12 +152,20 @@ MultiFrameCodec::State::GetNextFrameImage(
return std::make_pair(nullptr, decode_error);
}

// Hold onto this if we need it to decode future frames.
if (frameInfo.disposal_method == SkCodecAnimation::DisposalMethod::kKeep ||
lastRequiredFrame_) {
lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
lastRequiredFrameIndex_ = nextFrameIndex_;
}
// Always keep the last frame to support kRestorePrevious disposal op.
//
// Example below shows we must keep the frame whatever disposal method is
// |kRestoreBGColor| or |kRestorePrevious| or |kKeep|.
//
// Frame 1 , Frame 2 , Frame 3 , Frame 4
// complete decode , complete decode , reuse frame 1 , reuse frame 2
// OP_BACKGROUND , OP_PREVIOUS , OP_PREVIOUS , OP_KEEP
// kRestoreBGColor , kRestorePrevious , kRestorePrevious , kKeep
exExRequiredFrame_ = std::move(lastRequiredFrame_);
exExRequiredFrameIndex_ = lastRequiredFrameIndex_;

lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
lastRequiredFrameIndex_ = nextFrameIndex_;

#if IMPELLER_SUPPORTS_RENDERING
if (is_impeller_enabled_) {
Expand Down
6 changes: 6 additions & 0 deletions lib/ui/painting/multi_frame_codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class MultiFrameCodec : public Codec {
// The index of the last decoded required frame.
int lastRequiredFrameIndex_ = -1;

// The frame before last frame
std::unique_ptr<SkBitmap> exExRequiredFrame_;

// The index of the frame before last frame
int exExRequiredFrameIndex_ = -1;

std::pair<sk_sp<DlImage>, std::string> GetNextFrameImage(
fml::WeakPtr<GrDirectContext> resourceContext,
const std::shared_ptr<const fml::SyncSwitch>& gpu_disable_sync_switch,
Expand Down