planetarium/export/
raw.rs

1//! Planetarium
2//! ===========
3//!
4//! Private RAW image export routines
5//! ---------------------------------
6//!
7//! Contains implementations of private methods
8//! for the existing public types.
9
10use crate::{Canvas, EncoderError, Window};
11
12#[allow(clippy::unnecessary_wraps)]
13impl Canvas {
14    /// Exports the canvas window contents in the 8-bit gamma-compressed RAW image format.
15    pub(super) fn export_raw8bpp(&self, window: Window) -> Result<Vec<u8>, EncoderError> {
16        // Memory buffer to encode the RAW pixel data to
17        let mut rawbuf: Vec<u8> = Vec::with_capacity(window.len());
18
19        // The window is bounds checked by the caller.
20        for span in self.window_spans(window).unwrap() {
21            rawbuf.extend(span.iter().map(|p| self.gamma_curve.transform(*p)));
22        }
23
24        Ok(rawbuf)
25    }
26
27    /// Exports the canvas window contents in the `X`-bit linear light grayscale
28    /// little-endian RAW image format.
29    ///
30    /// The const generic `X` must be in the range from 9 to 16.
31    pub(super) fn export_raw1xbpp<const X: u16>(
32        &self,
33        window: Window,
34    ) -> Result<Vec<u8>, EncoderError> {
35        // Memory buffer to encode the RAW pixel data to
36        let mut rawbuf: Vec<u8> = Vec::with_capacity(2 * window.len());
37
38        // The window is bounds checked by the caller.
39        for span in self.window_spans(window).unwrap() {
40            for p in span {
41                let bytes = (p >> (16 - X)).to_le_bytes();
42                rawbuf.extend_from_slice(&bytes);
43            }
44        }
45
46        Ok(rawbuf)
47    }
48
49    /// Exports the subsampled canvas contents in the 8-bit gamma-compressed
50    /// RAW image format.
51    pub(super) fn export_sub_raw8bpp(&self, factors: (u32, u32)) -> Result<Vec<u8>, EncoderError> {
52        // Subsampled image size in pixels
53        let pixlen = self.pixbuf.len() / (factors.0 * factors.1) as usize;
54
55        // Memory buffer to encode the RAW pixel data to
56        let mut rawbuf: Vec<u8> = Vec::with_capacity(pixlen);
57
58        for i in 0..(self.height / factors.1) {
59            let loffset = (i * factors.1 * self.width) as usize;
60
61            for j in 0..(self.width / factors.0) {
62                let offset = loffset + (j * factors.0) as usize;
63                let xval = self.gamma_curve.transform(self.pixbuf[offset]);
64                rawbuf.push(xval);
65            }
66        }
67
68        Ok(rawbuf)
69    }
70
71    /// Exports the subsampled canvas contents in the `X`-bit linear light grayscale
72    /// little-endian RAW image format.
73    ///
74    /// The const generic `X` must be in the range from 9 to 16.
75    pub(super) fn export_sub_raw1xbpp<const X: u16>(
76        &self,
77        factors: (u32, u32),
78    ) -> Result<Vec<u8>, EncoderError> {
79        // Subsampled image size in pixels
80        let pixlen = self.pixbuf.len() / (factors.0 * factors.1) as usize;
81
82        // Memory buffer to encode the RAW pixel data to
83        let mut rawbuf: Vec<u8> = Vec::with_capacity(2 * pixlen);
84
85        for i in 0..(self.height / factors.1) {
86            let loffset = (i * factors.1 * self.width) as usize;
87
88            for j in 0..(self.width / factors.0) {
89                let offset = loffset + (j * factors.0) as usize;
90                let bytes = (self.pixbuf[offset] >> (16 - X)).to_le_bytes();
91                rawbuf.extend_from_slice(&bytes);
92            }
93        }
94
95        Ok(rawbuf)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use crate::{ImageFormat, SpotShape};
102
103    use super::*;
104
105    /// Creates a 256x256 canvas image for all tests.
106    fn mkimage() -> Canvas {
107        let mut c = Canvas::new(256, 256);
108        c.set_background(1000);
109
110        let shape = SpotShape::default().scale(4.5);
111        let shape2 = shape.stretch(1.7, 0.7).rotate(45.0);
112
113        c.add_spot((100.6, 150.2), shape, 0.9);
114        c.add_spot((103.8, 146.5), shape2, 0.5);
115
116        c.draw();
117        c
118    }
119
120    #[test]
121    fn export_raw8bpp() {
122        let img = mkimage().export_image(ImageFormat::RawGamma8Bpp).unwrap();
123        assert_eq!(img.len(), 256 * 256);
124        assert_eq!(img[0], 33);
125        assert_eq!(img[150 * 256 + 100], 238);
126    }
127
128    #[test]
129    fn export_sub_raw8bpp() {
130        let img = mkimage()
131            .export_subsampled_image((2, 2), ImageFormat::RawGamma8Bpp)
132            .unwrap();
133        assert_eq!(img.len(), 256 * 256 / 2 / 2);
134        assert_eq!(img[0], 33);
135        assert_eq!(img[(150 * 128 + 100) / 2], 238);
136    }
137
138    #[test]
139    fn export_window_raw8bpp() {
140        let wnd = Window::new(32, 16).at(90, 140);
141
142        let img = mkimage()
143            .export_window_image(wnd, ImageFormat::RawGamma8Bpp)
144            .unwrap();
145        assert_eq!(img.len(), wnd.len());
146        assert_eq!(img[300], 185);
147    }
148
149    #[test]
150    fn export_raw10bpp() {
151        let img = mkimage()
152            .export_image(ImageFormat::RawLinear10BppLE)
153            .unwrap();
154        assert_eq!(img.len(), 256 * 256 * 2);
155        assert_eq!(img[0], 0x0F);
156        assert_eq!(img[1], 0x00);
157        assert_eq!(img[2 * (150 * 256 + 100)], 106);
158        assert_eq!(img[2 * (150 * 256 + 100) + 1], 3);
159    }
160
161    #[test]
162    fn export_sub_raw10bpp() {
163        let img = mkimage()
164            .export_subsampled_image((2, 2), ImageFormat::RawLinear10BppLE)
165            .unwrap();
166        assert_eq!(img.len(), 256 * 256 * 2 / 2 / 2);
167        assert_eq!(img[0], 0x0F);
168        assert_eq!(img[1], 0x00);
169        assert_eq!(img[2 * (150 * 128 + 100) / 2], 106);
170        assert_eq!(img[2 * (150 * 128 + 100) / 2 + 1], 3);
171    }
172
173    #[test]
174    fn export_window_raw10bpp() {
175        let wnd = Window::new(32, 16).at(90, 140);
176
177        let img = mkimage()
178            .export_window_image(wnd, ImageFormat::RawLinear10BppLE)
179            .unwrap();
180        assert_eq!(img.len(), 2 * wnd.len());
181        assert_eq!(img[300 * 2], 240);
182        assert_eq!(img[300 * 2 + 1], 1);
183    }
184
185    #[test]
186    fn export_raw12bpp() {
187        let img = mkimage()
188            .export_image(ImageFormat::RawLinear12BppLE)
189            .unwrap();
190        assert_eq!(img.len(), 256 * 256 * 2);
191        assert_eq!(img[0], 0x3E);
192        assert_eq!(img[1], 0x00);
193        assert_eq!(img[2 * (150 * 256 + 100)], 168);
194        assert_eq!(img[2 * (150 * 256 + 100) + 1], 13);
195    }
196
197    #[test]
198    fn export_sub_raw12bpp() {
199        let img = mkimage()
200            .export_subsampled_image((4, 2), ImageFormat::RawLinear12BppLE)
201            .unwrap();
202        assert_eq!(img.len(), 256 * 256 * 2 / 4 / 2);
203        assert_eq!(img[0], 0x3E);
204        assert_eq!(img[1], 0x00);
205        assert_eq!(img[2 * (150 / 2 * 64 + 100 / 4)], 168);
206        assert_eq!(img[2 * (150 / 2 * 64 + 100 / 4) + 1], 13);
207    }
208}