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