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
1214namespace 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
0 commit comments