Skip to main content

pic_scale/
validation.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1.  Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * 3.  Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::ImageSize;
30use std::error::Error;
31use std::fmt::Display;
32
33/// Buffer mismatch error description
34#[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/// Error enumeration type
44#[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    /// Returns error as int code
80    #[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;