[rust png] Implement `SkPngRustCodec::onGetRepetitionCount`.
This CL exposes `png::AnimationControl` over FFI, and then uses it to
implement `SkPngRustCodec::onGetRepetitionCount`. After this CL, the
`RustEnabled/AnimatedPNGTests.repetitionCountTest/0` test starts to pass
(after patching in https://crrev.com/c/5786777 in Chromium).
Bug: chromium:356922876
Change-Id: I2e4b010609f31a860f6356b7552376d84f5d106f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/897176
Auto-Submit: Ćukasz Anforowicz <lukasza@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/experimental/rust_png/ffi/FFI.rs b/experimental/rust_png/ffi/FFI.rs
index 7b0d5a5..e58380d 100644
--- a/experimental/rust_png/ffi/FFI.rs
+++ b/experimental/rust_png/ffi/FFI.rs
@@ -73,6 +73,9 @@
) -> bool;
fn try_get_gama(self: &Reader, gamma: &mut f32) -> bool;
unsafe fn try_get_iccp<'a>(self: &'a Reader, iccp: &mut &'a [u8]) -> bool;
+ fn has_actl_chunk(self: &Reader) -> bool;
+ fn get_actl_num_frames(self: &Reader) -> u32;
+ fn get_actl_num_plays(self: &Reader) -> u32;
fn output_buffer_size(self: &Reader) -> usize;
fn output_color_type(self: &Reader) -> ColorType;
fn output_bits_per_component(self: &Reader) -> u8;
@@ -256,6 +259,34 @@
}
}
+ /// Returns whether the `aCTL` chunk exists.
+ fn has_actl_chunk(&self) -> bool {
+ self.0.info().animation_control.is_some()
+ }
+
+ /// Returns `num_frames` from the `aCTL` chunk. Panics if there is no
+ /// `aCTL` chunk.
+ ///
+ /// The returned value is equal the number of `fcTL` chunks. (Note that it
+ /// doesn't count `IDAT` nor `fDAT` chunks. In particular, if an `fCTL`
+ /// chunk doesn't appear before an `IDAT` chunk then `IDAT` is not part
+ /// of the animation.)
+ ///
+ /// See also
+ /// <https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk>.
+ fn get_actl_num_frames(&self) -> u32 {
+ self.0.info().animation_control.as_ref().unwrap().num_frames
+ }
+
+ /// Returns `num_plays` from the `aCTL` chunk. Panics if there is no `aCTL`
+ /// chunk.
+ ///
+ /// `0` indicates that the animation should play indefinitely. See
+ /// <https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk>.
+ fn get_actl_num_plays(&self) -> u32 {
+ self.0.info().animation_control.as_ref().unwrap().num_plays
+ }
+
fn output_buffer_size(&self) -> usize {
self.0.output_buffer_size()
}
diff --git a/experimental/rust_png/impl/SkPngRustCodec.cpp b/experimental/rust_png/impl/SkPngRustCodec.cpp
index 9d89b29..0d40457 100644
--- a/experimental/rust_png/impl/SkPngRustCodec.cpp
+++ b/experimental/rust_png/impl/SkPngRustCodec.cpp
@@ -7,6 +7,7 @@
#include "experimental/rust_png/impl/SkPngRustCodec.h"
+#include <limits>
#include <memory>
#include <utility>
@@ -390,6 +391,32 @@
return false;
}
+int SkPngRustCodec::onGetRepetitionCount() {
+ if (!fReader->has_actl_chunk()) {
+ return 0;
+ }
+
+ uint32_t num_frames = fReader->get_actl_num_frames();
+ if (num_frames <= 1) {
+ return 0;
+ }
+
+ // APNG spec says that "`num_plays` indicates the number of times that this
+ // animation should play; if it is 0, the animation should play
+ // indefinitely."
+ uint32_t num_plays = fReader->get_actl_num_plays();
+ constexpr unsigned int kMaxInt = static_cast<unsigned int>(std::numeric_limits<int>::max());
+ if ((num_plays == 0) || (num_plays > kMaxInt)) {
+ return kRepetitionCountInfinite;
+ }
+
+ // Subtracting 1, because `SkCodec::onGetRepetitionCount` doc comment says
+ // that "This number does not include the first play through of each frame.
+ // For example, a repetition count of 4 means that each frame is played 5
+ // times and then the animation stops."
+ return num_plays - 1;
+}
+
std::optional<SkSpan<const SkPngCodecBase::PaletteColorEntry>> SkPngRustCodec::onTryGetPlteChunk() {
if (fReader->output_color_type() != rust_png::ColorType::Indexed) {
return std::nullopt;
diff --git a/experimental/rust_png/impl/SkPngRustCodec.h b/experimental/rust_png/impl/SkPngRustCodec.h
index 22ec3cd..eeff43e 100644
--- a/experimental/rust_png/impl/SkPngRustCodec.h
+++ b/experimental/rust_png/impl/SkPngRustCodec.h
@@ -24,7 +24,7 @@
// Rust)
// * Skia's `SkSwizzler` and `skcms_Transform` (pixel format and color space
// transformations implemented in C++).
-class SkPngRustCodec : public SkPngCodecBase {
+class SkPngRustCodec final : public SkPngCodecBase {
public:
static std::unique_ptr<SkPngRustCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*);
@@ -71,6 +71,7 @@
const Options&) override;
Result onIncrementalDecode(int* rowsDecoded) override;
bool onGetFrameInfo(int, FrameInfo*) const override;
+ int onGetRepetitionCount() override;
// SkPngCodecBase overrides:
std::optional<SkSpan<const PaletteColorEntry>> onTryGetPlteChunk() override;