@@ -1662,6 +1662,16 @@ mod tests {
1662
1662
write_chunk ( w, b"fcTL" , & data) ;
1663
1663
}
1664
1664
1665
+ /// Writes an fdAT chunk.
1666
+ /// See https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk
1667
+ fn write_fdat ( w : & mut impl Write , sequence_number : u32 , image_data : & [ u8 ] ) {
1668
+ let mut data = Vec :: new ( ) ;
1669
+ data. write_u32 :: < byteorder:: BigEndian > ( sequence_number)
1670
+ . unwrap ( ) ;
1671
+ data. write_all ( & image_data) . unwrap ( ) ;
1672
+ write_chunk ( w, b"fdAT" , & data) ;
1673
+ }
1674
+
1665
1675
/// Writes PNG signature and chunks that can precede an fdAT chunk that is expected
1666
1676
/// to have
1667
1677
/// - `sequence_number` set to 0
@@ -1724,4 +1734,75 @@ mod tests {
1724
1734
let msg = format ! ( "{err}" ) ;
1725
1735
assert_eq ! ( "fdAT chunk shorter than 4 bytes" , msg) ;
1726
1736
}
1737
+
1738
+ #[ test]
1739
+ fn test_frame_split_across_two_fdat_chunks ( ) {
1740
+ // Generate test data where the 2nd animation frame is split across 2 fdAT chunks.
1741
+ //
1742
+ // This is similar to the example given in
1743
+ // https://wiki.mozilla.org/APNG_Specification#Chunk_Sequence_Numbers:
1744
+ //
1745
+ // ```
1746
+ // Sequence number Chunk
1747
+ // (none) `acTL`
1748
+ // 0 `fcTL` first frame
1749
+ // (none) `IDAT` first frame / default image
1750
+ // 1 `fcTL` second frame
1751
+ // 2 first `fdAT` for second frame
1752
+ // 3 second `fdAT` for second frame
1753
+ // ```
1754
+ let png = {
1755
+ let mut png = Vec :: new ( ) ;
1756
+ write_fdat_prefix ( & mut png, 2 , 8 ) ;
1757
+ let image_data = generate_rgba8_with_width ( 8 ) ;
1758
+ write_fdat ( & mut png, 2 , & image_data[ ..30 ] ) ;
1759
+ write_fdat ( & mut png, 3 , & image_data[ 30 ..] ) ;
1760
+ write_iend ( & mut png) ;
1761
+ png
1762
+ } ;
1763
+
1764
+ // Start decoding.
1765
+ let decoder = Decoder :: new ( png. as_slice ( ) ) ;
1766
+ let mut reader = decoder. read_info ( ) . unwrap ( ) ;
1767
+ let mut buf = vec ! [ 0 ; reader. output_buffer_size( ) ] ;
1768
+ let Some ( animation_control) = reader. info ( ) . animation_control else {
1769
+ panic ! ( "No acTL" ) ;
1770
+ } ;
1771
+ assert_eq ! ( animation_control. num_frames, 2 ) ;
1772
+
1773
+ // Process the 1st animation frame.
1774
+ let first_frame: Vec < u8 > ;
1775
+ {
1776
+ reader. next_frame ( & mut buf) . unwrap ( ) ;
1777
+ first_frame = buf. clone ( ) ;
1778
+
1779
+ // Note that the doc comment of `Reader::next_frame` says that "[...]
1780
+ // can be checked afterwards by calling `info` **after** a successful call and
1781
+ // inspecting the `frame_control` data.". (Note the **emphasis** on "after".)
1782
+ let Some ( frame_control) = reader. info ( ) . frame_control else {
1783
+ panic ! ( "No fcTL (1st frame)" ) ;
1784
+ } ;
1785
+ // The sequence number is taken from the `fcTL` chunk that comes before the `IDAT`
1786
+ // chunk.
1787
+ assert_eq ! ( frame_control. sequence_number, 0 ) ;
1788
+ }
1789
+
1790
+ // Process the 2nd animation frame.
1791
+ let second_frame: Vec < u8 > ;
1792
+ {
1793
+ reader. next_frame ( & mut buf) . unwrap ( ) ;
1794
+ second_frame = buf. clone ( ) ;
1795
+
1796
+ // Same as above - updated `frame_control` is available *after* the `next_frame` call.
1797
+ let Some ( frame_control) = reader. info ( ) . frame_control else {
1798
+ panic ! ( "No fcTL (2nd frame)" ) ;
1799
+ } ;
1800
+ // The sequence number is taken from the `fcTL` chunk that comes before the two `fdAT`
1801
+ // chunks. Note that sequence numbers inside `fdAT` chunks are not publically exposed
1802
+ // (but they are still checked when decoding to verify that they are sequential).
1803
+ assert_eq ! ( frame_control. sequence_number, 1 ) ;
1804
+ }
1805
+
1806
+ assert_eq ! ( first_frame, second_frame) ;
1807
+ }
1727
1808
}
0 commit comments