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

Commit 1427a39

Browse files
committed
Composite imaged onto canvas by direct blitting or alphablending
1 parent a5c6ae8 commit 1427a39

File tree

2 files changed

+100
-23
lines changed

2 files changed

+100
-23
lines changed

lib/ui/painting/image_generator_apng.cc

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
#include <cstddef>
77

88
#include "third_party/libpng/png.h"
9+
#include "third_party/skia/include/codec/SkCodecAnimation.h"
910
#include "third_party/skia/src/codec/SkPngCodec.h"
11+
#include "third_party/skia/src/core/SkRasterPipeline.h"
1012
#include "third_party/zlib/zlib.h" // For crc32
1113

1214
namespace flutter {
@@ -64,33 +66,46 @@ bool APNGImageGenerator::GetPixels(const SkImageInfo& info,
6466
FML_DCHECK(images_.size() > 0);
6567
unsigned int image_index = first_frame_index_ + frame_index;
6668

67-
// If the frame doesn't exist in the APNG chunk stream, fall back to the
68-
// mandatory "default" image (always at index 0).
69+
// 1. Demux the frame from the APNG stream.
70+
6971
if (!DemuxToImageIndex(image_index)) {
7072
FML_DLOG(ERROR) << "Couldn't demux image at index " << image_index
7173
<< " (frame index: " << frame_index
7274
<< ") from APNG stream.";
75+
return RenderDefaultImage(info, pixels, row_bytes);
76+
}
77+
78+
// 2. Decode the frame.
79+
80+
APNGImage& frame = images_[image_index];
81+
auto frame_info = frame.codec->getInfo();
82+
auto frame_row_bytes = frame_info.bytesPerPixel() * frame_info.width();
7383

74-
SkCodec::Result result =
75-
images_[0].codec->getPixels(info, pixels, row_bytes);
84+
if (frame.pixels.empty()) {
85+
frame.pixels.resize(frame_row_bytes * frame_info.height());
86+
SkCodec::Result result = frame.codec->getPixels(
87+
frame.codec->getInfo(), frame.pixels.data(), frame_row_bytes);
7688
if (result != SkCodec::kSuccess) {
77-
FML_DLOG(ERROR) << "Failed to decode the APNG's default/fallback image. "
78-
"SkCodec::Result: "
79-
<< result;
80-
return false;
89+
FML_DLOG(ERROR) << "Failed to decode image at index " << image_index
90+
<< " (frame index: " << frame_index
91+
<< ") of APNG. SkCodec::Result: " << result;
92+
return RenderDefaultImage(info, pixels, row_bytes);
8193
}
82-
return true;
8394
}
8495

85-
APNGImage& frame = images_[image_index];
96+
// 3. Composite the frame onto the canvas.
8697

87-
SkCodec::Result result = frame.codec->getPixels(info, pixels, row_bytes);
88-
if (result != SkCodec::kSuccess) {
89-
FML_DLOG(ERROR) << "Failed to decode image at index " << image_index
90-
<< " (frame index: " << frame_index
91-
<< ") of APNG. SkCodec::Result: " << result;
92-
return false;
98+
for (int i = 0; i < frame_info.height(); i++) {
99+
void* source = frame.pixels.data() + i * frame_row_bytes;
100+
void* destination = (uint8_t*)pixels +
101+
frame.x_offset * frame_info.bytesPerPixel() +
102+
(i + frame.y_offset) * row_bytes;
103+
104+
BlendLine(info.colorType(), destination, frame_info.colorType(), source,
105+
info.alphaType(), frame.frame_info->blend_mode,
106+
frame_info.width());
93107
}
108+
94109
return true;
95110
}
96111

@@ -412,11 +427,6 @@ bool APNGImageGenerator::DemuxNextImageInternal() {
412427
return false;
413428
}
414429

415-
// TODO(bdero): Cache rendered frames and discard their codecs. There is no
416-
// circumstance where frames can ever change on repeat (according to the
417-
// APNG spec).
418-
// TODO(bdero): Alpha blend with the `prior_frame` if the blending mode is
419-
// appropriate.
420430
std::optional<APNGImage> image;
421431
void* data_p = (void*)data_.get()->data();
422432
std::tie(image, next_chunk_p_) =
@@ -425,11 +435,21 @@ bool APNGImageGenerator::DemuxNextImageInternal() {
425435
return false;
426436
}
427437

438+
if (!images_.empty() && images_.back().frame_info->disposal_method ==
439+
SkCodecAnimation::DisposalMethod::kKeep) {
440+
image->frame_info->required_frame = images_.size() - 1;
441+
}
442+
428443
// Calling SkCodec::getInfo at least once prior to decoding is mandatory.
429444
SkImageInfo info = image.value().codec->getInfo();
430445
FML_DCHECK(info.colorInfo() == image_info_.colorInfo());
431446

432447
images_.push_back(std::move(image.value()));
448+
449+
auto default_info = images_[0].codec->getInfo();
450+
if (info.colorType() != default_info.colorType()) {
451+
return false;
452+
}
433453
return true;
434454
}
435455

@@ -479,4 +499,49 @@ uint32_t APNGImageGenerator::ChunkHeader::ComputeChunkCrc32() {
479499
return crc;
480500
}
481501

502+
void APNGImageGenerator::BlendLine(SkColorType dest_colortype,
503+
void* dest,
504+
SkColorType source_colortype,
505+
const void* source,
506+
SkAlphaType dest_alphatype,
507+
SkCodecAnimation::Blend blend_mode,
508+
int width) {
509+
SkRasterPipeline_MemoryCtx dst_ctx = {(void*)dest, 0},
510+
src_ctx = {(void*)source, 0};
511+
512+
SkRasterPipeline_<256> p;
513+
514+
p.append_load_dst(dest_colortype, &dst_ctx);
515+
if (kUnpremul_SkAlphaType == dest_alphatype) {
516+
p.append(SkRasterPipeline::premul_dst);
517+
}
518+
519+
p.append_load(source_colortype, &src_ctx);
520+
p.append(SkRasterPipeline::premul);
521+
522+
if (blend_mode == SkCodecAnimation::Blend::kSrcOver) {
523+
p.append(SkRasterPipeline::srcover);
524+
}
525+
526+
if (kUnpremul_SkAlphaType == dest_alphatype) {
527+
p.append(SkRasterPipeline::unpremul);
528+
}
529+
p.append_store(dest_colortype, &dst_ctx);
530+
531+
p.run(0, 0, width, 1);
532+
}
533+
534+
bool APNGImageGenerator::RenderDefaultImage(const SkImageInfo& info,
535+
void* pixels,
536+
size_t row_bytes) {
537+
SkCodec::Result result = images_[0].codec->getPixels(info, pixels, row_bytes);
538+
if (result != SkCodec::kSuccess) {
539+
FML_DLOG(ERROR) << "Failed to decode the APNG's default/fallback image. "
540+
"SkCodec::Result: "
541+
<< result;
542+
return false;
543+
}
544+
return true;
545+
}
546+
482547
} // namespace flutter

lib/ui/painting/image_generator_apng.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ class APNGImageGenerator : public ImageGenerator {
112112
struct APNGImage {
113113
std::unique_ptr<SkCodec> codec;
114114

115-
// The composited image is populated
116-
std::shared_ptr<SkImage> composited_image;
115+
// The rendered frame pixels.
116+
std::vector<uint8_t> pixels;
117117

118118
// Absense of frame info is possible on the "default" image.
119119
std::optional<ImageGenerator::FrameInfo> frame_info;
@@ -185,6 +185,18 @@ class APNGImageGenerator : public ImageGenerator {
185185

186186
bool DemuxToImageIndex(unsigned int image_index);
187187

188+
static void BlendLine(SkColorType dest_colortype,
189+
void* dest,
190+
SkColorType source_colortype,
191+
const void* source,
192+
SkAlphaType dest_alphatype,
193+
SkCodecAnimation::Blend blend_mode,
194+
int width);
195+
196+
bool RenderDefaultImage(const SkImageInfo& info,
197+
void* pixels,
198+
size_t row_bytes);
199+
188200
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(APNGImageGenerator);
189201
sk_sp<SkData> data_;
190202
SkImageInfo image_info_;

0 commit comments

Comments
 (0)