1use crate::ImageSize;
30use std::error::Error;
31use std::fmt::Display;
32
33#[derive(Copy, Clone, Debug)]
35pub struct PicScaleBufferMismatch {
36 pub expected: usize,
37 pub width: usize,
38 pub height: usize,
39 pub channels: usize,
40 pub slice_len: usize,
41}
42
43#[derive(Debug)]
45pub enum PicScaleError {
46 ZeroImageDimensions,
47 SourceImageIsTooLarge,
48 DestinationImageIsTooLarge,
49 BufferMismatch(PicScaleBufferMismatch),
50 InvalidStride(usize, usize),
51 UnsupportedBitDepth(usize),
52 UnknownResizingFilter,
53 OutOfMemory(usize),
54 Generic(String),
55 InvalidScratchSize {
56 expected: usize,
57 size: usize,
58 },
59 InvalidSourceSize {
60 expected: ImageSize,
61 size: ImageSize,
62 },
63 InvalidDestinationSize {
64 expected: ImageSize,
65 size: ImageSize,
66 },
67 EmptyPlan,
68 CropOutOfBounds {
69 x: usize,
70 y: usize,
71 width: usize,
72 height: usize,
73 image_width: usize,
74 image_height: usize,
75 },
76}
77
78impl PicScaleError {
79 #[inline]
81 pub fn code(&self) -> usize {
82 match self {
83 PicScaleError::ZeroImageDimensions => 1,
84 PicScaleError::SourceImageIsTooLarge => 2,
85 PicScaleError::DestinationImageIsTooLarge => 3,
86 PicScaleError::BufferMismatch(_) => 4,
87 PicScaleError::InvalidStride(_, _) => 5,
88 PicScaleError::UnsupportedBitDepth(_) => 6,
89 PicScaleError::UnknownResizingFilter => 7,
90 PicScaleError::OutOfMemory(_) => 8,
91 PicScaleError::InvalidScratchSize { .. } => 9,
92 PicScaleError::InvalidSourceSize { .. } => 10,
93 PicScaleError::InvalidDestinationSize { .. } => 11,
94 PicScaleError::EmptyPlan => 12,
95 PicScaleError::Generic(_) => 13,
96 PicScaleError::CropOutOfBounds { .. } => 14,
97 }
98 }
99}
100
101impl Display for PicScaleError {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 PicScaleError::InvalidStride(min_stride, real_stride) => f.write_fmt(format_args!(
105 "Stride must be at least {min_stride}, but received {real_stride}",
106 )),
107 PicScaleError::ZeroImageDimensions => {
108 f.write_str("One of image dimensions is 0, this should not happen")
109 }
110 PicScaleError::SourceImageIsTooLarge => {
111 f.write_str("Input image larger than memory capabilities")
112 }
113 PicScaleError::DestinationImageIsTooLarge => {
114 f.write_str("Destination image larger than memory capabilities")
115 }
116 PicScaleError::BufferMismatch(buffer_mismatch) => f.write_fmt(format_args!(
117 "Image buffer len expected to be {} [w({})*h({})*channels({})] but received {}",
118 buffer_mismatch.expected,
119 buffer_mismatch.width,
120 buffer_mismatch.height,
121 buffer_mismatch.channels,
122 buffer_mismatch.slice_len,
123 )),
124 PicScaleError::UnsupportedBitDepth(depth) => {
125 f.write_fmt(format_args!("Bit-depth must be in [1, 16] but got {depth}",))
126 }
127 PicScaleError::UnknownResizingFilter => {
128 f.write_str("Unknown resizing filter was requested")
129 }
130 PicScaleError::OutOfMemory(capacity) => f.write_fmt(format_args!(
131 "There is no enough memory to allocate {capacity} bytes"
132 )),
133 PicScaleError::InvalidScratchSize { expected, size } => f.write_fmt(format_args!(
134 "Scratch size must be at least {expected} bytes, but received {size}",
135 )),
136 PicScaleError::InvalidSourceSize { expected, size } => f.write_fmt(format_args!(
137 "Source size must be at least {:?} bytes, but received {:?}",
138 expected, size
139 )),
140 PicScaleError::InvalidDestinationSize { expected, size } => f.write_fmt(format_args!(
141 "Destination size must be at least {:?} bytes, but received {:?}",
142 expected, size
143 )),
144 PicScaleError::EmptyPlan => {
145 f.write_str("Multi-step scaling appeared to be an empty one")
146 }
147 PicScaleError::Generic(x) => f.write_str(x),
148 PicScaleError::CropOutOfBounds {
149 x,
150 y,
151 width,
152 height,
153 image_width,
154 image_height,
155 } => write!(
156 f,
157 "Crop region ({x}, {y}, {width}x{height}) exceeds image bounds ({image_width}x{image_height})"
158 ),
159 }
160 }
161}
162
163impl Error for PicScaleError {}
164
165macro_rules! try_vec {
166 () => {
167 Vec::new()
168 };
169 ($elem:expr; $n:expr) => {{
170 let mut v = Vec::new();
171 v.try_reserve_exact($n)
172 .map_err(|_| crate::validation::PicScaleError::OutOfMemory($n))?;
173 v.resize($n, $elem);
174 v
175 }};
176}
177
178pub(crate) use try_vec;
179
180macro_rules! validate_scratch {
181 ($scratch: expr, $required_size: expr) => {{
182 if $scratch.len() < $required_size {
183 return Err(PicScaleError::InvalidScratchSize {
184 expected: $required_size,
185 size: $scratch.len(),
186 });
187 }
188 &mut $scratch[..$required_size]
189 }};
190}
191
192pub(crate) use validate_scratch;
193
194macro_rules! validate_sizes {
195 ($store: expr, $into: expr, $src_size: expr, $dst_size: expr) => {{
196 $into.validate()?;
197 $store.validate()?;
198 if $store.width != $src_size.width && $store.height != $src_size.height {
199 return Err(PicScaleError::InvalidSourceSize {
200 expected: $src_size,
201 size: $store.size(),
202 });
203 }
204 if $into.width != $dst_size.width && $into.height != $dst_size.height {
205 return Err(PicScaleError::InvalidDestinationSize {
206 expected: $dst_size,
207 size: $into.size(),
208 });
209 }
210 if $store.width == $into.width && $store.height == $into.height {
211 $store.copied_to_mut($into);
212 return Ok(());
213 }
214 }};
215}
216
217pub(crate) use validate_sizes;