1use crate::DecodeError;
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
6pub struct DecodeOptions {
7 pub max_size: Option<[u32; 2]>,
13
14 pub mipmaps: bool,
16
17 pub limits: DecodeLimits,
19}
20
21impl DecodeOptions {
22 pub fn fit(&self, source: [u32; 2]) -> [u32; 2] {
24 match self.max_size {
25 Some(max) => fit_within(source, max),
26 None => source,
27 }
28 }
29}
30
31pub fn fit_within(source: [u32; 2], bounds: [u32; 2]) -> [u32; 2] {
34 if source[0] <= bounds[0] && source[1] <= bounds[1] {
35 return source;
36 }
37 let by_width = [bounds[0], scaled(source[1], source[0], bounds[0])];
38 if by_width[1] <= bounds[1] {
39 by_width
40 } else {
41 [scaled(source[0], source[1], bounds[1]), bounds[1]]
42 }
43}
44
45fn scaled(value: u32, from: u32, to: u32) -> u32 {
47 let from = u64::from(from.max(1));
48 let scaled = (u64::from(value) * u64::from(to) + from / 2) / from;
49 u32::try_from(scaled).unwrap_or(u32::MAX).max(1)
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub struct DecodeLimits {
55 pub max_encoded_bytes: usize,
57
58 pub max_pixels: u64,
60
61 pub max_frames: u32,
63}
64
65impl Default for DecodeLimits {
66 fn default() -> Self {
67 Self {
68 max_encoded_bytes: 256 * 1024 * 1024,
69 max_pixels: 64 * 1024 * 1024,
70 max_frames: 10_000,
71 }
72 }
73}
74
75impl DecodeLimits {
76 pub fn check_source_size(self, size: [u32; 2]) -> Result<(), DecodeError> {
78 if size.contains(&0) {
79 return Err(DecodeError::InvalidData("empty image".into()));
80 }
81 if u64::from(size[0]) * u64::from(size[1]) > self.max_pixels {
82 return Err(DecodeError::LimitExceeded("decoded pixels"));
83 }
84 Ok(())
85 }
86
87 pub fn check_frame_count(self, count: u32) -> Result<(), DecodeError> {
89 if count == 0 {
90 return Err(DecodeError::InvalidData("image has no frames".into()));
91 }
92 if count > self.max_frames {
93 return Err(DecodeError::LimitExceeded("animation frames"));
94 }
95 Ok(())
96 }
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq)]
101pub struct ImageInfo {
102 pub size: [u32; 2],
105
106 pub frame_count: u32,
108
109 pub repetition: Repetition,
111}
112
113#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
115pub enum Repetition {
116 #[default]
118 Once,
119
120 Times(u32),
122
123 Forever,
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn fitting_keeps_aspect_and_never_upscales() {
133 assert_eq!(fit_within([400, 200], [800, 800]), [400, 200]);
134 assert_eq!(fit_within([400, 200], [100, 100]), [100, 50]);
135 assert_eq!(fit_within([200, 400], [100, 100]), [50, 100]);
136 assert_eq!(fit_within([4000, 1], [100, 100]), [100, 1]);
137 assert_eq!(fit_within([3, 3], [2, 1]), [1, 1]);
138 }
139}