fast image operations
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::Image;
mod affine;
mod convert;
#[cfg(feature = "scale")]
mod scale;

#[derive(Clone, Debug, PartialEq)]
/// Dynamic image.
/// Can be any of {`Y8`, `YA8`, `RGB8`, `RGB16`}.
pub enum DynImage<T> {
    /// Y image
    Y(Image<T, 1>),
    /// YA image
    Ya(Image<T, 2>),
    /// RGB image
    Rgb(Image<T, 3>),
    /// RGBA image
    Rgba(Image<T, 4>),
}

impl Copy for DynImage<&[u8]> {}

macro_rules! e {
    ($dyn:expr => |$image: pat_param| $do:expr) => {
        match $dyn {
            DynImage::Y($image) => DynImage::Y($do),
            DynImage::Ya($image) => DynImage::Ya($do),
            DynImage::Rgb($image) => DynImage::Rgb($do),
            DynImage::Rgba($image) => DynImage::Rgba($do),
        }
    };
    ($dyn:expr, |$image: pat_param| $do:expr) => {
        match $dyn {
            DynImage::Y($image) => $do,
            DynImage::Ya($image) => $do,
            DynImage::Rgb($image) => $do,
            DynImage::Rgba($image) => $do,
        }
    };
}
use e;

impl<T> DynImage<T> {
    /// Get the width of this image.
    pub const fn width(&self) -> u32 {
        e!(self, |i| i.width())
    }

    /// Get the height of this image.
    pub const fn height(&self) -> u32 {
        e!(self, |i| i.height())
    }

    /// Get the image buffer.
    pub const fn buffer(&self) -> &T {
        e!(self, |i| i.buffer())
    }

    /// Take the image buffer.
    pub fn take_buffer(self) -> T {
        e!(self, |i| i.take_buffer())
    }
}

impl<T: AsRef<[u8]>> DynImage<T> {
    /// Reference this image.
    pub fn as_ref(&self) -> DynImage<&[u8]> {
        e!(self => |i| i.as_ref())
    }

    /// Bytes of this image.
    pub fn bytes(&self) -> &[u8] {
        e!(self, |i| i.bytes())
    }
}

impl DynImage<Box<[u8]>> {
    #[cfg(feature = "save")]
    /// Open a PNG image
    pub fn open(f: impl AsRef<std::path::Path>) -> Self {
        use png::Transformations as T;
        let p = std::fs::File::open(f).unwrap();
        let r = std::io::BufReader::new(p);
        let mut dec = png::Decoder::new(r);
        dec.set_transformations(T::STRIP_16 | T::EXPAND);
        let mut reader = dec.read_info().unwrap();
        let mut buf = vec![0; reader.output_buffer_size()].into_boxed_slice();
        let info = reader.next_frame(&mut buf).unwrap();
        use png::ColorType::*;
        match info.color_type {
            Indexed | Grayscale => Self::Y(Image::build(info.width, info.height).buf(buf)),
            Rgb => Self::Rgb(Image::build(info.width, info.height).buf(buf)),
            Rgba => Self::Rgba(Image::build(info.width, info.height).buf(buf)),
            GrayscaleAlpha => Self::Ya(Image::build(info.width, info.height).buf(buf)),
        }
    }

    #[cfg(feature = "save")]
    /// Save this image to a PNG.
    pub fn save(&self, f: impl AsRef<std::path::Path>) {
        let p = std::fs::File::create(f).unwrap();
        let w = &mut std::io::BufWriter::new(p);
        let mut enc = png::Encoder::new(w, self.width(), self.height());
        enc.set_depth(png::BitDepth::Eight);
        enc.set_color(match self {
            Self::Y(_) => png::ColorType::Grayscale,
            Self::Ya(_) => png::ColorType::GrayscaleAlpha,
            Self::Rgb(_) => png::ColorType::Rgb,
            Self::Rgba(_) => png::ColorType::Rgba,
        });
        enc.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2));
        enc.set_source_chromaticities(png::SourceChromaticities::new(
            (0.31270, 0.32900),
            (0.64000, 0.33000),
            (0.30000, 0.60000),
            (0.15000, 0.06000),
        ));
        let mut writer = enc.write_header().unwrap();
        writer.write_image_data(self.bytes()).unwrap();
    }
}