-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
mod.rs
105 lines (101 loc) · 2.63 KB
/
mod.rs
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
use crate::anim::settings::AnimSettings;
use crate::app::AppResult;
use crate::image::geometry::Geometry;
use crate::image::Image;
use crate::util::state::InputState;
use image::ExtendedColorType;
use png::{BitDepth, ColorType, Encoder, FilterType};
use std::io::{self, Write};
/* APNG encoder and settings */
pub struct ApngEncoder<'a, Output: Write> {
encoder: Encoder<'a, Output>,
settings: &'a AnimSettings,
}
impl<'a, Output: Write> ApngEncoder<'a, Output> {
/**
* Create a new ApngEncoder object.
*
* @param frame_count
* @param geometry
* @param output
* @param settings
* @return ApngEncoder (Result)
*/
pub fn new(
frame_count: u32,
geometry: Geometry,
output: Output,
settings: &'a AnimSettings,
) -> AppResult<Self> {
let mut encoder = Encoder::new(output, geometry.width, geometry.height);
encoder.set_animated(
frame_count,
settings.repeat.try_into().unwrap_or_default(),
)?;
encoder.set_color(ColorType::Rgba);
encoder.set_depth(BitDepth::Eight);
encoder.set_filter(FilterType::NoFilter);
Ok(Self { encoder, settings })
}
/**
* Encode images as frame and write to the APNG file.
*
* @param images
* @param input_state (Option)
* @return Result
*/
pub fn save(
self,
images: Vec<Image>,
input_state: Option<&'static InputState>,
) -> AppResult<()> {
let mut writer = self.encoder.write_header()?;
writer.set_frame_delay(1, self.settings.fps.try_into().unwrap_or(1))?;
for (i, image) in images.iter().enumerate() {
let percentage = ((i + 1) as f64 / images.len() as f64) * 100.;
info!("Saving... ({:.1}%)\r", percentage);
debug!(
"Encoding... ({:.1}%) [{}/{}]\r",
percentage,
i + 1,
images.len()
);
io::stdout().flush()?;
if let Some(state) = input_state {
if state.check_cancel_keys() {
info!("\n");
warn!("User interrupt detected.");
panic!("Failed to write the frames")
}
}
writer.write_image_data(&image.get_data(ExtendedColorType::Rgba8))?;
}
info!("\n");
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use image::Rgba;
#[test]
fn test_apng_encoder() -> AppResult<()> {
let geometry = Geometry::new(0, 0, 1, 2);
let data = vec![Rgba::from([128, 128, 128, 0]), Rgba::from([16, 16, 16, 0])];
let images = vec![
Image::new(data.clone(), false, geometry),
Image::new(data.into_iter().rev().collect(), false, geometry),
];
let mut output = Vec::new();
ApngEncoder::new(
images.len().try_into().unwrap(),
geometry,
&mut output,
&AnimSettings::default(),
)?
.save(images, None)?;
output.truncate(6);
assert_eq!(vec![0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a], output);
Ok(())
}
}