1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use super::{Encoder, Filter, Header, HeaderErr, Intent, Quantization};
use std::fmt::Display;

pub struct HeaderBuilder {
    pub width: u32,
    pub height: u32,
    pub layers: u16,
    pub channels: u16,
    pub quality: u16,
    pub chroma_scale: u8,
    pub block_size: u8,
    pub filter: Filter,
    pub encoder: Encoder,
    pub intent: Intent,
    pub metadata_size: u32,
}

impl HeaderBuilder {
    pub fn build(self) -> Result<Header, HeaderErr> {
        let width = check_range(self.width, 0, 1 << 30, "Width")?;
        let height = check_range(self.height, 0, 1 << 30, "Height")?;
        let bit_depth = 8;
        let channel_size = width as usize * height as usize;

        let layer_size = channel_size
            .checked_mul(self.layers as usize)
            .ok_or_else(|| HeaderErr::WrongValue(String::from("layer size is too large")))?;
        let image_size = layer_size
            .checked_mul(self.channels as usize)
            .ok_or_else(|| HeaderErr::WrongValue(String::from("Image size is too large")))?;
        let _bit_depth_image_size = image_size
            .checked_mul((bit_depth as usize + 7) / 8)
            .ok_or_else(|| {
                HeaderErr::WrongValue(String::from("Image size and bit_depth are too large"))
            })?;

        Ok(Header {
            version: 1,
            width,
            height,
            layers: self.layers,
            channels: self.channels,
            bit_depth,
            is_signed: false,
            quality: check_range(self.quality, 0, 1025, "Quality")?,
            chroma_scale: self.chroma_scale,
            block_size: check_range(self.block_size, 0, 31, "Block size")?,
            filter: self.filter,
            quantization: Quantization::Scalar,
            encoder: self.encoder,
            intent: self.intent,
            metadata_size: self.metadata_size,
            channel_size,
            image_size,
        })
    }
}

fn check_range<T>(value: T, min: T, max: T, name: &str) -> Result<T, HeaderErr>
where
    T: PartialOrd + Display + Copy,
{
    if min < value && value < max {
        Ok(value)
    } else {
        Err(HeaderErr::WrongValue(format!(
            "{} must be in range ({}..{})",
            name, min, max
        )))
    }
}