libjpeg_turbo_rs/api/
scanline.rs1use crate::common::error::{JpegError, Result};
11use crate::common::types::{ColorSpace, DctMethod, FrameHeader, PixelFormat, Subsampling};
12use crate::decode::pipeline::{Decoder, Image};
13#[allow(unused_imports)]
14use alloc::vec::Vec;
15#[allow(unused_imports)]
16use alloc::{format, vec};
17
18pub struct ScanlineDecoder<'a> {
20 decoder: Decoder<'a>,
21 decoded_image: Option<Image>,
22 current_line: usize,
23 crop_x: Option<(usize, usize)>,
24 bottom_up: bool,
25}
26
27impl<'a> ScanlineDecoder<'a> {
28 pub fn new(data: &'a [u8]) -> Result<Self> {
30 let decoder: Decoder<'a> = Decoder::new(data)?;
31 Ok(Self {
32 decoder,
33 decoded_image: None,
34 current_line: 0,
35 crop_x: None,
36 bottom_up: false,
37 })
38 }
39
40 pub fn header(&self) -> &FrameHeader {
42 self.decoder.header()
43 }
44
45 pub fn output_scanline(&self) -> usize {
47 self.current_line
48 }
49
50 pub fn set_output_format(&mut self, format: PixelFormat) {
52 self.decoder.set_output_format(format);
53 }
54
55 pub fn set_fast_upsample(&mut self, fast: bool) {
57 self.decoder.fast_upsample = fast;
58 }
59
60 pub fn set_fast_dct(&mut self, fast: bool) {
62 self.decoder.set_fast_dct(fast);
63 }
64
65 pub fn set_dct_method(&mut self, method: DctMethod) {
67 self.decoder.dct_method = method;
68 }
69
70 pub fn set_block_smoothing(&mut self, smooth: bool) {
72 self.decoder.block_smoothing = smooth;
73 }
74
75 pub fn set_output_colorspace(&mut self, cs: ColorSpace) {
77 self.decoder.output_colorspace = Some(cs);
78 }
79
80 pub fn set_merged_upsample(&mut self, enabled: bool) {
86 self.decoder.merged_upsample = enabled;
87 }
88
89 pub fn set_crop_x(&mut self, x: usize, width: usize) {
91 self.crop_x = Some((x, width));
92 }
93
94 pub fn set_bottom_up(&mut self, bottom_up: bool) {
100 self.bottom_up = bottom_up;
101 }
102
103 pub fn read_scanline(&mut self, buf: &mut [u8]) -> Result<()> {
105 self.ensure_decoded()?;
106 let img: &Image = self.decoded_image.as_ref().unwrap();
107 let bpp: usize = img.pixel_format.bytes_per_pixel();
108 let row_bytes: usize = img.width * bpp;
109 if self.current_line >= img.height {
110 return Err(JpegError::Unsupported("no more scanlines".into()));
111 }
112 let start: usize = self.current_line * row_bytes;
113 buf[..row_bytes].copy_from_slice(&img.data[start..start + row_bytes]);
114 self.current_line += 1;
115 Ok(())
116 }
117
118 pub fn skip_scanlines(&mut self, count: usize) -> Result<usize> {
120 self.ensure_decoded()?;
121 let img: &Image = self.decoded_image.as_ref().unwrap();
122 let remaining: usize = img.height - self.current_line;
123 let actual: usize = count.min(remaining);
124 self.current_line += actual;
125 Ok(actual)
126 }
127
128 pub fn finish(mut self) -> Result<Image> {
130 self.ensure_decoded()?;
131 Ok(self.decoded_image.take().unwrap())
132 }
133
134 fn ensure_decoded(&mut self) -> Result<()> {
135 if self.decoded_image.is_none() {
136 let mut img: Image = self.decoder.decode_image()?;
137 if let Some((crop_x_offset, crop_width)) = self.crop_x {
138 img = Self::apply_horizontal_crop(img, crop_x_offset, crop_width)?;
139 }
140 if self.bottom_up {
141 img = Self::flip_rows_image(img);
142 }
143 self.decoded_image = Some(img);
144 }
145 Ok(())
146 }
147
148 fn flip_rows_image(mut img: Image) -> Image {
150 let bpp: usize = img.pixel_format.bytes_per_pixel();
151 let row_bytes: usize = img.width * bpp;
152 let mut flipped: Vec<u8> = Vec::with_capacity(img.data.len());
153 for row in (0..img.height).rev() {
154 let start: usize = row * row_bytes;
155 flipped.extend_from_slice(&img.data[start..start + row_bytes]);
156 }
157 img.data = flipped;
158 img
159 }
160
161 fn apply_horizontal_crop(img: Image, x: usize, width: usize) -> Result<Image> {
162 let bpp: usize = img.pixel_format.bytes_per_pixel();
163 if x + width > img.width {
164 return Err(JpegError::Unsupported(format!(
165 "crop region {}..{} exceeds image width {}",
166 x,
167 x + width,
168 img.width
169 )));
170 }
171 let src_row_bytes: usize = img.width * bpp;
172 let dst_row_bytes: usize = width * bpp;
173 let mut data: Vec<u8> = Vec::with_capacity(dst_row_bytes * img.height);
174 for y in 0..img.height {
175 let src_start: usize = y * src_row_bytes + x * bpp;
176 data.extend_from_slice(&img.data[src_start..src_start + dst_row_bytes]);
177 }
178 Ok(Image {
179 xmp_data: None,
180 iptc_data: None,
181 width,
182 height: img.height,
183 pixel_format: img.pixel_format,
184 precision: img.precision,
185 data,
186 icc_profile: img.icc_profile,
187 exif_data: img.exif_data,
188 comment: img.comment,
189 density: img.density,
190 saved_markers: img.saved_markers,
191 warnings: img.warnings,
192 })
193 }
194}
195
196pub struct ScanlineEncoder {
198 pixels: Vec<u8>,
199 width: usize,
200 height: usize,
201 pixel_format: PixelFormat,
202 quality: u8,
203 subsampling: Subsampling,
204 current_line: usize,
205}
206
207impl ScanlineEncoder {
208 pub fn new(width: usize, height: usize, pixel_format: PixelFormat) -> Self {
210 let bpp: usize = pixel_format.bytes_per_pixel();
211 Self {
212 pixels: vec![0u8; width * height * bpp],
213 width,
214 height,
215 pixel_format,
216 quality: 75,
217 subsampling: Subsampling::S420,
218 current_line: 0,
219 }
220 }
221
222 pub fn set_quality(&mut self, quality: u8) {
224 self.quality = quality;
225 }
226
227 pub fn set_subsampling(&mut self, subsampling: Subsampling) {
229 self.subsampling = subsampling;
230 }
231
232 pub fn next_scanline(&self) -> usize {
234 self.current_line
235 }
236
237 pub fn write_scanline(&mut self, row: &[u8]) -> Result<()> {
239 if self.current_line >= self.height {
240 return Err(JpegError::Unsupported("all scanlines written".into()));
241 }
242 let bpp: usize = self.pixel_format.bytes_per_pixel();
243 let row_bytes: usize = self.width * bpp;
244 let start: usize = self.current_line * row_bytes;
245 self.pixels[start..start + row_bytes].copy_from_slice(&row[..row_bytes]);
246 self.current_line += 1;
247 Ok(())
248 }
249
250 pub fn finish(self) -> Result<Vec<u8>> {
252 if self.current_line != self.height {
253 return Err(JpegError::Unsupported(format!(
254 "not all scanlines written: {} of {}",
255 self.current_line, self.height
256 )));
257 }
258 crate::encode::pipeline::compress(
259 &self.pixels,
260 self.width,
261 self.height,
262 self.pixel_format,
263 self.quality,
264 self.subsampling,
265 crate::common::types::DctMethod::IsLow,
266 )
267 }
268}