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}
69
70impl PicScaleError {
71    /// Returns error as int code
72    #[inline]
73    pub fn code(&self) -> usize {
74        match self {
75            PicScaleError::ZeroImageDimensions => 1,
76            PicScaleError::SourceImageIsTooLarge => 2,
77            PicScaleError::DestinationImageIsTooLarge => 3,
78            PicScaleError::BufferMismatch(_) => 4,
79            PicScaleError::InvalidStride(_, _) => 5,
80            PicScaleError::UnsupportedBitDepth(_) => 6,
81            PicScaleError::UnknownResizingFilter => 7,
82            PicScaleError::OutOfMemory(_) => 8,
83            PicScaleError::InvalidScratchSize { .. } => 9,
84            PicScaleError::InvalidSourceSize { .. } => 10,
85            PicScaleError::InvalidDestinationSize { .. } => 11,
86            PicScaleError::EmptyPlan => 12,
87            PicScaleError::Generic(_) => 13,
88        }
89    }
90}
91
92impl Display for PicScaleError {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match self {
95            PicScaleError::InvalidStride(min_stride, real_stride) => f.write_fmt(format_args!(
96                "Stride must be at least {min_stride}, but received {real_stride}",
97            )),
98            PicScaleError::ZeroImageDimensions => {
99                f.write_str("One of image dimensions is 0, this should not happen")
100            }
101            PicScaleError::SourceImageIsTooLarge => {
102                f.write_str("Input image larger than memory capabilities")
103            }
104            PicScaleError::DestinationImageIsTooLarge => {
105                f.write_str("Destination image larger than memory capabilities")
106            }
107            PicScaleError::BufferMismatch(buffer_mismatch) => f.write_fmt(format_args!(
108                "Image buffer len expected to be {} [w({})*h({})*channels({})] but received {}",
109                buffer_mismatch.expected,
110                buffer_mismatch.width,
111                buffer_mismatch.height,
112                buffer_mismatch.channels,
113                buffer_mismatch.slice_len,
114            )),
115            PicScaleError::UnsupportedBitDepth(depth) => {
116                f.write_fmt(format_args!("Bit-depth must be in [1, 16] but got {depth}",))
117            }
118            PicScaleError::UnknownResizingFilter => {
119                f.write_str("Unknown resizing filter was requested")
120            }
121            PicScaleError::OutOfMemory(capacity) => f.write_fmt(format_args!(
122                "There is no enough memory to allocate {capacity} bytes"
123            )),
124            PicScaleError::InvalidScratchSize { expected, size } => f.write_fmt(format_args!(
125                "Scratch size must be at least {expected} bytes, but received {size}",
126            )),
127            PicScaleError::InvalidSourceSize { expected, size } => f.write_fmt(format_args!(
128                "Source size must be at least {:?} bytes, but received {:?}",
129                expected, size
130            )),
131            PicScaleError::InvalidDestinationSize { expected, size } => f.write_fmt(format_args!(
132                "Destination size must be at least {:?} bytes, but received {:?}",
133                expected, size
134            )),
135            PicScaleError::EmptyPlan => {
136                f.write_str("Multi-step scaling appeared to be an empty one")
137            }
138            PicScaleError::Generic(x) => f.write_str(x),
139        }
140    }
141}
142
143impl Error for PicScaleError {}
144
145macro_rules! try_vec {
146    () => {
147        Vec::new()
148    };
149    ($elem:expr; $n:expr) => {{
150        let mut v = Vec::new();
151        v.try_reserve_exact($n)
152            .map_err(|_| crate::validation::PicScaleError::OutOfMemory($n))?;
153        v.resize($n, $elem);
154        v
155    }};
156}
157
158pub(crate) use try_vec;
159
160macro_rules! validate_scratch {
161    ($scratch: expr, $required_size: expr) => {{
162        if $scratch.len() < $required_size {
163            return Err(PicScaleError::InvalidScratchSize {
164                expected: $required_size,
165                size: $scratch.len(),
166            });
167        }
168        &mut $scratch[..$required_size]
169    }};
170}
171
172pub(crate) use validate_scratch;
173
174macro_rules! validate_sizes {
175    ($store: expr, $into: expr, $src_size: expr, $dst_size: expr) => {{
176        $into.validate()?;
177        $store.validate()?;
178        if $store.width != $src_size.width && $store.height != $src_size.height {
179            return Err(PicScaleError::InvalidSourceSize {
180                expected: $src_size,
181                size: $store.size(),
182            });
183        }
184        if $into.width != $dst_size.width && $into.height != $dst_size.height {
185            return Err(PicScaleError::InvalidDestinationSize {
186                expected: $dst_size,
187                size: $into.size(),
188            });
189        }
190        if $store.width == $into.width && $store.height == $into.height {
191            $store.copied_to_mut($into);
192            return Ok(());
193        }
194    }};
195}
196
197pub(crate) use validate_sizes;