1use std::io::Cursor;
31
32use image::{
33 ColorType, ImageDecoder, ImageDecoderRect, ImageResult,
34 error::{DecodingError, ImageError, ImageFormatHint},
35};
36
37use crate::djvu_document::DjVuPage;
38use crate::djvu_render::{RenderError, RenderOptions};
39
40#[derive(Debug, thiserror::Error)]
44pub enum ImageCompatError {
45 #[error("render error: {0}")]
47 Render(#[from] RenderError),
48}
49
50impl From<ImageCompatError> for ImageError {
51 fn from(e: ImageCompatError) -> Self {
52 ImageError::Decoding(DecodingError::new(
53 ImageFormatHint::Name("DjVu".to_string()),
54 e,
55 ))
56 }
57}
58
59pub struct DjVuDecoder<'a> {
66 page: &'a DjVuPage,
67 width: u32,
68 height: u32,
69}
70
71impl<'a> DjVuDecoder<'a> {
72 pub fn new(page: &'a DjVuPage) -> Result<Self, ImageCompatError> {
76 Ok(Self {
77 width: page.width() as u32,
78 height: page.height() as u32,
79 page,
80 })
81 }
82
83 #[must_use]
88 pub fn with_size(mut self, width: u32, height: u32) -> Self {
89 self.width = width;
90 self.height = height;
91 self
92 }
93
94 fn render_to_vec(&self) -> Result<Vec<u8>, ImageCompatError> {
96 let opts = RenderOptions {
97 width: self.width,
98 height: self.height,
99 scale: self.width as f32 / self.page.width().max(1) as f32,
100 ..RenderOptions::default()
101 };
102 let size = (self.width as usize)
103 .saturating_mul(self.height as usize)
104 .saturating_mul(4);
105 let mut buf = vec![0u8; size];
106 self.page.render_into(&opts, &mut buf)?;
107 Ok(buf)
108 }
109}
110
111impl<'a> ImageDecoder<'a> for DjVuDecoder<'a> {
114 type Reader = Cursor<Vec<u8>>;
115
116 fn dimensions(&self) -> (u32, u32) {
117 (self.width, self.height)
118 }
119
120 fn color_type(&self) -> ColorType {
121 ColorType::Rgba8
122 }
123
124 #[allow(deprecated)]
125 fn into_reader(self) -> ImageResult<Self::Reader> {
126 let data = self.render_to_vec().map_err(ImageError::from)?;
127 Ok(Cursor::new(data))
128 }
129
130 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
131 let data = self.render_to_vec().map_err(ImageError::from)?;
132 if buf.len() != data.len() {
133 return Err(ImageError::Decoding(DecodingError::new(
134 ImageFormatHint::Name("DjVu".to_string()),
135 format!(
136 "buffer size mismatch: expected {}, got {}",
137 data.len(),
138 buf.len()
139 ),
140 )));
141 }
142 buf.copy_from_slice(&data);
143 Ok(())
144 }
145}
146
147impl<'a> ImageDecoderRect<'a> for DjVuDecoder<'a> {
150 #[allow(deprecated)]
156 fn read_rect_with_progress<F: Fn(image::Progress)>(
157 &mut self,
158 x: u32,
159 y: u32,
160 width: u32,
161 height: u32,
162 buf: &mut [u8],
163 _progress_callback: F,
164 ) -> ImageResult<()> {
165 let bytes_per_pixel = self.color_type().bytes_per_pixel() as usize;
166 let row_stride = self.width as usize * bytes_per_pixel;
167 let rect_row_bytes = width as usize * bytes_per_pixel;
168
169 let x_end = x.checked_add(width).ok_or_else(|| {
171 ImageError::Decoding(DecodingError::new(
172 ImageFormatHint::Name("DjVu".to_string()),
173 "rectangle x+width overflows u32",
174 ))
175 })?;
176 let y_end = y.checked_add(height).ok_or_else(|| {
177 ImageError::Decoding(DecodingError::new(
178 ImageFormatHint::Name("DjVu".to_string()),
179 "rectangle y+height overflows u32",
180 ))
181 })?;
182 if x_end > self.width || y_end > self.height {
183 return Err(ImageError::Decoding(DecodingError::new(
184 ImageFormatHint::Name("DjVu".to_string()),
185 format!(
186 "rectangle ({x},{y},{width},{height}) out of image bounds ({}×{})",
187 self.width, self.height
188 ),
189 )));
190 }
191
192 let full = self.render_to_vec().map_err(ImageError::from)?;
193
194 for row in 0..height as usize {
195 let src_y = y as usize + row;
196 let src_start = src_y * row_stride + x as usize * bytes_per_pixel;
197 let src_end = src_start + rect_row_bytes;
198 let dst_start = row * rect_row_bytes;
199 let dst_end = dst_start + rect_row_bytes;
200
201 buf[dst_start..dst_end].copy_from_slice(&full[src_start..src_end]);
202 }
203
204 Ok(())
205 }
206}