1*c8dee2aaSAndroid Build Coastguard WorkerFrom 657f75b63a9c1d05a6d8dd2307c371ab304f968a Mon Sep 17 00:00:00 2001
2*c8dee2aaSAndroid Build Coastguard WorkerFrom: Lukasz Anforowicz <[email protected]>
3*c8dee2aaSAndroid Build Coastguard WorkerDate: Thu, 3 Oct 2024 19:52:22 +0000
4*c8dee2aaSAndroid Build Coastguard WorkerSubject: [PATCH 104/113] Simplify how `Reader` tracks how many frames remain
5*c8dee2aaSAndroid Build Coastguard Worker to be decoded.
6*c8dee2aaSAndroid Build Coastguard Worker
7*c8dee2aaSAndroid Build Coastguard WorkerIn an earlier commit 278b1d4fc1da9cebdeb511d8b106d239155c35cc we
8*c8dee2aaSAndroid Build Coastguard Workerstopped using `Reader.next_frame` for deciding whether to call
9*c8dee2aaSAndroid Build Coastguard Worker`read_until_image_data` from `next_frame`.  This commit goes one
10*c8dee2aaSAndroid Build Coastguard Workerstep further and removes the `next_frame` field entirely.
11*c8dee2aaSAndroid Build Coastguard Worker
12*c8dee2aaSAndroid Build Coastguard WorkerThere are no known cases where the previous code would result
13*c8dee2aaSAndroid Build Coastguard Workerin incorrect behavior, but this commit still seems desirable:
14*c8dee2aaSAndroid Build Coastguard Worker
15*c8dee2aaSAndroid Build Coastguard Worker* It simplifies the code
16*c8dee2aaSAndroid Build Coastguard Worker* It ensures that a single "end-of-IDAT/fdAT-sequence" event kind
17*c8dee2aaSAndroid Build Coastguard Worker  controls both 1) `consumed_and_flushed` state, and 2) counting
18*c8dee2aaSAndroid Build Coastguard Worker  remaining frames.  (Before this commit `next_frame` would also be
19*c8dee2aaSAndroid Build Coastguard Worker  mutated after a separate "fcTL-encountered"  event.  And also after
20*c8dee2aaSAndroid Build Coastguard Worker  "end-of-IDAT/fdAT-sequence" but only from `next_frame` and not from
21*c8dee2aaSAndroid Build Coastguard Worker  `next_row`.)
22*c8dee2aaSAndroid Build Coastguard Worker---
23*c8dee2aaSAndroid Build Coastguard Worker src/decoder/mod.rs | 80 +++++++++++++++++-----------------------------
24*c8dee2aaSAndroid Build Coastguard Worker 1 file changed, 29 insertions(+), 51 deletions(-)
25*c8dee2aaSAndroid Build Coastguard Worker
26*c8dee2aaSAndroid Build Coastguard Workerdiff --git a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/mod.rs b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/mod.rs
27*c8dee2aaSAndroid Build Coastguard Workerindex 56570b3..d0a88e8 100644
28*c8dee2aaSAndroid Build Coastguard Worker--- a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/mod.rs
29*c8dee2aaSAndroid Build Coastguard Worker+++ b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/mod.rs
30*c8dee2aaSAndroid Build Coastguard Worker@@ -205,8 +205,7 @@ impl<R: Read> Decoder<R> {
31*c8dee2aaSAndroid Build Coastguard Worker             decoder: self.read_decoder,
32*c8dee2aaSAndroid Build Coastguard Worker             bpp: BytesPerPixel::One,
33*c8dee2aaSAndroid Build Coastguard Worker             subframe: SubframeInfo::not_yet_init(),
34*c8dee2aaSAndroid Build Coastguard Worker-            fctl_read: 0,
35*c8dee2aaSAndroid Build Coastguard Worker-            next_frame: SubframeIdx::Initial,
36*c8dee2aaSAndroid Build Coastguard Worker+            remaining_frames: 0, // Temporary value - fixed below after reading `acTL` and `fcTL`.
37*c8dee2aaSAndroid Build Coastguard Worker             data_stream: Vec::new(),
38*c8dee2aaSAndroid Build Coastguard Worker             prev_start: 0,
39*c8dee2aaSAndroid Build Coastguard Worker             current_start: 0,
40*c8dee2aaSAndroid Build Coastguard Worker@@ -234,6 +233,19 @@ impl<R: Read> Decoder<R> {
41*c8dee2aaSAndroid Build Coastguard Worker         }
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker         reader.read_until_image_data()?;
44*c8dee2aaSAndroid Build Coastguard Worker+
45*c8dee2aaSAndroid Build Coastguard Worker+        reader.remaining_frames = match reader.info().animation_control.as_ref() {
46*c8dee2aaSAndroid Build Coastguard Worker+            None => 1, // No `acTL` => only expecting `IDAT` frame.
47*c8dee2aaSAndroid Build Coastguard Worker+            Some(animation) => {
48*c8dee2aaSAndroid Build Coastguard Worker+                let mut num_frames = animation.num_frames as usize;
49*c8dee2aaSAndroid Build Coastguard Worker+                if reader.info().frame_control.is_none() {
50*c8dee2aaSAndroid Build Coastguard Worker+                    // No `fcTL` before `IDAT` => `IDAT` is not part of the animation, but
51*c8dee2aaSAndroid Build Coastguard Worker+                    // represents an *extra*, default frame for non-APNG-aware decoders.
52*c8dee2aaSAndroid Build Coastguard Worker+                    num_frames += 1;
53*c8dee2aaSAndroid Build Coastguard Worker+                }
54*c8dee2aaSAndroid Build Coastguard Worker+                num_frames
55*c8dee2aaSAndroid Build Coastguard Worker+            }
56*c8dee2aaSAndroid Build Coastguard Worker+        };
57*c8dee2aaSAndroid Build Coastguard Worker         Ok(reader)
58*c8dee2aaSAndroid Build Coastguard Worker     }
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker@@ -349,11 +361,8 @@ pub struct Reader<R: Read> {
61*c8dee2aaSAndroid Build Coastguard Worker     decoder: ReadDecoder<R>,
62*c8dee2aaSAndroid Build Coastguard Worker     bpp: BytesPerPixel,
63*c8dee2aaSAndroid Build Coastguard Worker     subframe: SubframeInfo,
64*c8dee2aaSAndroid Build Coastguard Worker-    /// Number of frame control chunks read.
65*c8dee2aaSAndroid Build Coastguard Worker-    /// By the APNG specification the total number must equal the count specified in the animation
66*c8dee2aaSAndroid Build Coastguard Worker-    /// control chunk. The IDAT image _may_ have such a chunk applying to it.
67*c8dee2aaSAndroid Build Coastguard Worker-    fctl_read: u32,
68*c8dee2aaSAndroid Build Coastguard Worker-    next_frame: SubframeIdx,
69*c8dee2aaSAndroid Build Coastguard Worker+    /// How many frames remain to be decoded.  Decremented after each `IDAT` or `fdAT` sequence.
70*c8dee2aaSAndroid Build Coastguard Worker+    remaining_frames: usize,
71*c8dee2aaSAndroid Build Coastguard Worker     /// Vec containing the uncompressed image data currently being processed.
72*c8dee2aaSAndroid Build Coastguard Worker     data_stream: Vec<u8>,
73*c8dee2aaSAndroid Build Coastguard Worker     /// Index in `data_stream` where the previous row starts.
74*c8dee2aaSAndroid Build Coastguard Worker@@ -386,19 +395,6 @@ struct SubframeInfo {
75*c8dee2aaSAndroid Build Coastguard Worker     consumed_and_flushed: bool,
76*c8dee2aaSAndroid Build Coastguard Worker }
77*c8dee2aaSAndroid Build Coastguard Worker
78*c8dee2aaSAndroid Build Coastguard Worker-/// Denote a frame as given by sequence numbers.
79*c8dee2aaSAndroid Build Coastguard Worker-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
80*c8dee2aaSAndroid Build Coastguard Worker-enum SubframeIdx {
81*c8dee2aaSAndroid Build Coastguard Worker-    /// The initial frame in an IDAT chunk without fcTL chunk applying to it.
82*c8dee2aaSAndroid Build Coastguard Worker-    /// Note that this variant precedes `Some` as IDAT frames precede fdAT frames and all fdAT
83*c8dee2aaSAndroid Build Coastguard Worker-    /// frames must have a fcTL applying to it.
84*c8dee2aaSAndroid Build Coastguard Worker-    Initial,
85*c8dee2aaSAndroid Build Coastguard Worker-    /// An IDAT frame with fcTL or an fdAT frame.
86*c8dee2aaSAndroid Build Coastguard Worker-    Some(u32),
87*c8dee2aaSAndroid Build Coastguard Worker-    /// The past-the-end index.
88*c8dee2aaSAndroid Build Coastguard Worker-    End,
89*c8dee2aaSAndroid Build Coastguard Worker-}
90*c8dee2aaSAndroid Build Coastguard Worker-
91*c8dee2aaSAndroid Build Coastguard Worker impl<R: Read> Reader<R> {
92*c8dee2aaSAndroid Build Coastguard Worker     /// Reads all meta data until the next frame data starts.
93*c8dee2aaSAndroid Build Coastguard Worker     /// Requires IHDR before the IDAT and fcTL before fdAT.
94*c8dee2aaSAndroid Build Coastguard Worker@@ -414,14 +410,6 @@ impl<R: Read> Reader<R> {
95*c8dee2aaSAndroid Build Coastguard Worker             match state {
96*c8dee2aaSAndroid Build Coastguard Worker                 Some(Decoded::ChunkBegin(_, chunk::IDAT))
97*c8dee2aaSAndroid Build Coastguard Worker                 | Some(Decoded::ChunkBegin(_, chunk::fdAT)) => break,
98*c8dee2aaSAndroid Build Coastguard Worker-                Some(Decoded::FrameControl(_)) => {
99*c8dee2aaSAndroid Build Coastguard Worker-                    // The next frame is the one to which this chunk applies.
100*c8dee2aaSAndroid Build Coastguard Worker-                    self.next_frame = SubframeIdx::Some(self.fctl_read);
101*c8dee2aaSAndroid Build Coastguard Worker-                    // TODO: what about overflow here? That would imply there are more fctl chunks
102*c8dee2aaSAndroid Build Coastguard Worker-                    // than can be specified in the animation control but also that we have read
103*c8dee2aaSAndroid Build Coastguard Worker-                    // several gigabytes of data.
104*c8dee2aaSAndroid Build Coastguard Worker-                    self.fctl_read += 1;
105*c8dee2aaSAndroid Build Coastguard Worker-                }
106*c8dee2aaSAndroid Build Coastguard Worker                 None => {
107*c8dee2aaSAndroid Build Coastguard Worker                     return Err(DecodingError::Format(
108*c8dee2aaSAndroid Build Coastguard Worker                         FormatErrorInner::MissingImageData.into(),
109*c8dee2aaSAndroid Build Coastguard Worker@@ -473,7 +461,7 @@ impl<R: Read> Reader<R> {
110*c8dee2aaSAndroid Build Coastguard Worker     /// Output lines will be written in row-major, packed matrix with width and height of the read
111*c8dee2aaSAndroid Build Coastguard Worker     /// frame (or subframe), all samples are in big endian byte order where this matters.
112*c8dee2aaSAndroid Build Coastguard Worker     pub fn next_frame(&mut self, buf: &mut [u8]) -> Result<OutputInfo, DecodingError> {
113*c8dee2aaSAndroid Build Coastguard Worker-        if self.next_frame == SubframeIdx::End {
114*c8dee2aaSAndroid Build Coastguard Worker+        if self.remaining_frames == 0 {
115*c8dee2aaSAndroid Build Coastguard Worker             return Err(DecodingError::Parameter(
116*c8dee2aaSAndroid Build Coastguard Worker                 ParameterErrorKind::PolledAfterEndOfImage.into(),
117*c8dee2aaSAndroid Build Coastguard Worker             ));
118*c8dee2aaSAndroid Build Coastguard Worker@@ -534,37 +522,27 @@ impl<R: Read> Reader<R> {
119*c8dee2aaSAndroid Build Coastguard Worker         // Advance over the rest of data for this (sub-)frame.
120*c8dee2aaSAndroid Build Coastguard Worker         self.finish_decoding()?;
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker-        // Advance our state to expect the next frame.
123*c8dee2aaSAndroid Build Coastguard Worker-        let past_end_subframe = self
124*c8dee2aaSAndroid Build Coastguard Worker-            .info()
125*c8dee2aaSAndroid Build Coastguard Worker-            .animation_control()
126*c8dee2aaSAndroid Build Coastguard Worker-            .map(|ac| ac.num_frames)
127*c8dee2aaSAndroid Build Coastguard Worker-            .unwrap_or(0);
128*c8dee2aaSAndroid Build Coastguard Worker-        self.next_frame = match self.next_frame {
129*c8dee2aaSAndroid Build Coastguard Worker-            SubframeIdx::End => unreachable!("Next frame called when already at image end"),
130*c8dee2aaSAndroid Build Coastguard Worker-            // Reached the end of non-animated image.
131*c8dee2aaSAndroid Build Coastguard Worker-            SubframeIdx::Initial if past_end_subframe == 0 => SubframeIdx::End,
132*c8dee2aaSAndroid Build Coastguard Worker-            // An animated image, expecting first subframe.
133*c8dee2aaSAndroid Build Coastguard Worker-            SubframeIdx::Initial => SubframeIdx::Some(0),
134*c8dee2aaSAndroid Build Coastguard Worker-            // This was the last subframe, slightly fuzzy condition in case of programmer error.
135*c8dee2aaSAndroid Build Coastguard Worker-            SubframeIdx::Some(idx) if past_end_subframe <= idx + 1 => SubframeIdx::End,
136*c8dee2aaSAndroid Build Coastguard Worker-            // Expecting next subframe.
137*c8dee2aaSAndroid Build Coastguard Worker-            SubframeIdx::Some(idx) => SubframeIdx::Some(idx + 1),
138*c8dee2aaSAndroid Build Coastguard Worker-        };
139*c8dee2aaSAndroid Build Coastguard Worker-
140*c8dee2aaSAndroid Build Coastguard Worker         Ok(output_info)
141*c8dee2aaSAndroid Build Coastguard Worker     }
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker+    fn mark_subframe_as_consumed_and_flushed(&mut self) {
144*c8dee2aaSAndroid Build Coastguard Worker+        assert!(self.remaining_frames > 0);
145*c8dee2aaSAndroid Build Coastguard Worker+        self.remaining_frames -= 1;
146*c8dee2aaSAndroid Build Coastguard Worker+
147*c8dee2aaSAndroid Build Coastguard Worker+        self.subframe.consumed_and_flushed = true;
148*c8dee2aaSAndroid Build Coastguard Worker+    }
149*c8dee2aaSAndroid Build Coastguard Worker+
150*c8dee2aaSAndroid Build Coastguard Worker     /// Advance over the rest of data for this (sub-)frame.
151*c8dee2aaSAndroid Build Coastguard Worker     /// Called after decoding the last row of a frame.
152*c8dee2aaSAndroid Build Coastguard Worker     fn finish_decoding(&mut self) -> Result<(), DecodingError> {
153*c8dee2aaSAndroid Build Coastguard Worker-        // Double-check that all rows of this frame have been decoded.
154*c8dee2aaSAndroid Build Coastguard Worker+        // Double-check that all rows of this frame have been decoded (i.e. that the potential
155*c8dee2aaSAndroid Build Coastguard Worker+        // `finish_decoding` call below won't be discarding any data).
156*c8dee2aaSAndroid Build Coastguard Worker         assert!(self.subframe.current_interlace_info.is_none());
157*c8dee2aaSAndroid Build Coastguard Worker
158*c8dee2aaSAndroid Build Coastguard Worker         // Discard the remaining data in the current sequence of `IDAT` or `fdAT` chunks.
159*c8dee2aaSAndroid Build Coastguard Worker         if !self.subframe.consumed_and_flushed {
160*c8dee2aaSAndroid Build Coastguard Worker             self.decoder.finish_decoding()?;
161*c8dee2aaSAndroid Build Coastguard Worker-            self.subframe.consumed_and_flushed = true;
162*c8dee2aaSAndroid Build Coastguard Worker+            self.mark_subframe_as_consumed_and_flushed();
163*c8dee2aaSAndroid Build Coastguard Worker         }
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker         Ok(())
166*c8dee2aaSAndroid Build Coastguard Worker@@ -617,7 +595,7 @@ impl<R: Read> Reader<R> {
167*c8dee2aaSAndroid Build Coastguard Worker     /// Read the rest of the image and chunks and finish up, including text chunks or others
168*c8dee2aaSAndroid Build Coastguard Worker     /// This will discard the rest of the image if the image is not read already with [`Reader::next_frame`], [`Reader::next_row`] or [`Reader::next_interlaced_row`]
169*c8dee2aaSAndroid Build Coastguard Worker     pub fn finish(&mut self) -> Result<(), DecodingError> {
170*c8dee2aaSAndroid Build Coastguard Worker-        self.next_frame = SubframeIdx::End;
171*c8dee2aaSAndroid Build Coastguard Worker+        self.remaining_frames = 0;
172*c8dee2aaSAndroid Build Coastguard Worker         self.data_stream.clear();
173*c8dee2aaSAndroid Build Coastguard Worker         self.current_start = 0;
174*c8dee2aaSAndroid Build Coastguard Worker         self.prev_start = 0;
175*c8dee2aaSAndroid Build Coastguard Worker@@ -729,7 +707,7 @@ impl<R: Read> Reader<R> {
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker             match self.decoder.decode_next(&mut self.data_stream)? {
178*c8dee2aaSAndroid Build Coastguard Worker                 Some(Decoded::ImageData) => (),
179*c8dee2aaSAndroid Build Coastguard Worker-                Some(Decoded::ImageDataFlushed) => self.subframe.consumed_and_flushed = true,
180*c8dee2aaSAndroid Build Coastguard Worker+                Some(Decoded::ImageDataFlushed) => self.mark_subframe_as_consumed_and_flushed(),
181*c8dee2aaSAndroid Build Coastguard Worker                 _ => (),
182*c8dee2aaSAndroid Build Coastguard Worker             }
183*c8dee2aaSAndroid Build Coastguard Worker         }
184*c8dee2aaSAndroid Build Coastguard Worker--
185*c8dee2aaSAndroid Build Coastguard Worker2.47.0.rc0.187.ge670bccf7e-goog
186*c8dee2aaSAndroid Build Coastguard Worker
187