Skip to main content

j2k_jpeg/decoder/
rows.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Row-oriented decode entry points and component-row routing.
4
5use super::{
6    checked_scratch_len, jpeg_downscale, scaled_rect_covering, ColorSpace, ComponentRowWriter,
7    CroppedWriter, DecodeOutcome, Decoder, Downscale, DownscaleFactor, JpegError, Rect, RowSink,
8    ScratchPool, SinkWriter, DEFAULT_SCRATCH,
9};
10
11impl Decoder<'_> {
12    /// Decode the full image into rows delivered to `sink`.
13    ///
14    /// DCT-backed and 8-bit lossless color paths emit interleaved RGB8 rows.
15    /// Lossless 16-bit grayscale SOF3 emits little-endian Gray16 rows, and
16    /// supported lossless 16-bit color SOF3 emits little-endian Rgb16 rows.
17    ///
18    /// # Errors
19    ///
20    /// Returns a scan decode error or an error reported by `sink`.
21    pub fn decode_rows<S>(&self, sink: &mut S) -> Result<DecodeOutcome, JpegError>
22    where
23        S: RowSink<u8, Error = JpegError>,
24    {
25        DEFAULT_SCRATCH.with(|pool| self.decode_rows_with_scratch(&mut pool.borrow_mut(), sink))
26    }
27
28    /// [`Self::decode_rows`] with caller-owned scratch. See
29    /// [`Self::decode_into_with_scratch`] for the reuse contract.
30    ///
31    /// # Errors
32    ///
33    /// Returns a scan decode error or an error reported by `sink`.
34    pub fn decode_rows_with_scratch<S>(
35        &self,
36        pool: &mut ScratchPool,
37        sink: &mut S,
38    ) -> Result<DecodeOutcome, JpegError>
39    where
40        S: RowSink<u8, Error = JpegError>,
41    {
42        if self.lossless_plan.is_some() {
43            return self.decode_lossless_rows_with_scratch(pool, sink);
44        }
45        let width = self.info.dimensions.0 as usize;
46        let scratch_bytes = self.prepare_decode_workspace(pool, 0)?;
47        let rows = pool.take_sink_rows(width.saturating_mul(3), scratch_bytes)?;
48        let mut writer = SinkWriter::new(sink, rows, self.backend);
49        let result = self.decode_rgb_with_writer(
50            pool,
51            &mut writer,
52            DownscaleFactor::Full,
53            Rect::full(self.info.dimensions),
54        );
55        pool.restore_sink_rows(writer.into_rows());
56        result
57    }
58
59    fn decode_lossless_rows_with_scratch<S>(
60        &self,
61        pool: &mut ScratchPool,
62        sink: &mut S,
63    ) -> Result<DecodeOutcome, JpegError>
64    where
65        S: RowSink<u8, Error = JpegError>,
66    {
67        let plan = self
68            .lossless_plan
69            .as_ref()
70            .ok_or(JpegError::NotImplemented {
71                sof: self.info.sof_kind,
72            })?;
73        if !(1..=7).contains(&plan.predictor) {
74            return Err(JpegError::UnsupportedPredictor {
75                predictor: plan.predictor,
76            });
77        }
78
79        let width = self.info.dimensions.0 as usize;
80        let scratch_bytes = self.prepare_decode_workspace(pool, 0)?;
81        match (self.info.color_space, plan.bit_depth) {
82            (ColorSpace::Grayscale, 8) => {
83                let predictor_len = checked_scratch_len(&[width])?;
84                let sink_len = checked_scratch_len(&[width, 3])?;
85                let mut rows =
86                    pool.prepare_lossless_rows(predictor_len, sink_len, scratch_bytes)?;
87                let result = self.decode_lossless_gray8_rows(
88                    sink,
89                    &mut pool.lossless_prev_row,
90                    &mut pool.lossless_curr_row,
91                    &mut rows.top_row,
92                );
93                pool.restore_sink_rows(rows);
94                result
95            }
96            (ColorSpace::Grayscale, 16) => {
97                let predictor_len = checked_scratch_len(&[width, 2])?;
98                let _rows = pool.prepare_lossless_rows(predictor_len, 0, scratch_bytes)?;
99                self.decode_lossless_gray16_rows(
100                    sink,
101                    &mut pool.lossless_prev_row,
102                    &mut pool.lossless_curr_row,
103                )
104            }
105            (ColorSpace::Rgb, 8) => {
106                let predictor_len = checked_scratch_len(&[width, 3])?;
107                let _rows = pool.prepare_lossless_rows(predictor_len, 0, scratch_bytes)?;
108                self.decode_lossless_color8_rows(
109                    sink,
110                    &mut pool.lossless_prev_row,
111                    &mut pool.lossless_curr_row,
112                    None,
113                    ColorSpace::Rgb,
114                )
115            }
116            (ColorSpace::YCbCr, 8) => {
117                let row_len = checked_scratch_len(&[width, 3])?;
118                let mut rows = pool.prepare_lossless_rows(row_len, row_len, scratch_bytes)?;
119                let result = self.decode_lossless_color8_rows(
120                    sink,
121                    &mut pool.lossless_prev_row,
122                    &mut pool.lossless_curr_row,
123                    Some(&mut rows.top_row),
124                    ColorSpace::YCbCr,
125                );
126                pool.restore_sink_rows(rows);
127                result
128            }
129            (ColorSpace::Rgb, 16) => {
130                let predictor_len = checked_scratch_len(&[width, 6])?;
131                let _rows = pool.prepare_lossless_rows(predictor_len, 0, scratch_bytes)?;
132                self.decode_lossless_color16_rows(
133                    sink,
134                    &mut pool.lossless_prev_row,
135                    &mut pool.lossless_curr_row,
136                    None,
137                    ColorSpace::Rgb,
138                )
139            }
140            (ColorSpace::YCbCr, 16) => {
141                let row_len = checked_scratch_len(&[width, 6])?;
142                let mut rows = pool.prepare_lossless_rows(row_len, row_len, scratch_bytes)?;
143                let result = self.decode_lossless_color16_rows(
144                    sink,
145                    &mut pool.lossless_prev_row,
146                    &mut pool.lossless_curr_row,
147                    Some(&mut rows.top_row),
148                    ColorSpace::YCbCr,
149                );
150                pool.restore_sink_rows(rows);
151                result
152            }
153            (_, depth) if depth != 8 && depth != 16 => {
154                Err(JpegError::UnsupportedBitDepth { depth })
155            }
156            _ => Err(JpegError::NotImplemented {
157                sof: self.info.sof_kind,
158            }),
159        }
160    }
161
162    /// Decode the full image into component rows.
163    ///
164    /// # Errors
165    ///
166    /// Returns a scan decode error or an error reported by `writer`.
167    pub fn decode_component_rows_with_scratch<W>(
168        &self,
169        pool: &mut ScratchPool,
170        writer: &mut W,
171    ) -> Result<DecodeOutcome, JpegError>
172    where
173        W: ComponentRowWriter,
174    {
175        self.decode_region_component_rows_with_scratch(
176            pool,
177            writer,
178            Rect::full(self.info.dimensions),
179            Downscale::None,
180        )
181    }
182
183    /// Decode `roi` into component rows, optionally at a reduced scale.
184    ///
185    /// # Errors
186    ///
187    /// Returns an invalid-region or scan error, or an error reported by
188    /// `writer`.
189    pub fn decode_region_component_rows_with_scratch<W>(
190        &self,
191        pool: &mut ScratchPool,
192        mut writer: &mut W,
193        roi: Rect,
194        scale: Downscale,
195    ) -> Result<DecodeOutcome, JpegError>
196    where
197        W: ComponentRowWriter,
198    {
199        if !roi.is_within(self.info.dimensions) {
200            return Err(JpegError::RectOutOfBounds {
201                rect: roi,
202                width: self.info.dimensions.0,
203                height: self.info.dimensions.1,
204            });
205        }
206
207        let downscale = jpeg_downscale(scale);
208        let scaled_roi = scaled_rect_covering(roi, downscale)?;
209
210        if roi == Rect::full(self.info.dimensions) {
211            self.decode_with_writer(pool, &mut writer, downscale, roi)
212        } else {
213            let (source_x0, source_width) =
214                self.source_window_for_output_rect(downscale, scaled_roi);
215            let mut cropped = CroppedWriter::new(writer, scaled_roi, source_x0, source_width)?;
216            self.decode_with_writer(pool, &mut cropped, downscale, roi)
217        }
218    }
219}