-
Notifications
You must be signed in to change notification settings - Fork 6
/
main.rs
154 lines (132 loc) · 4.6 KB
/
main.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
extern crate engiffen;
extern crate image;
extern crate getopts;
extern crate rand;
#[cfg(feature = "globbing")] extern crate glob;
use std::io::{self, BufWriter};
use std::{env, fmt, process};
use std::fs::{read_dir, File};
use std::path::PathBuf;
use std::time::{Instant, Duration};
use parse_args::{parse_args, Args, SourceImages, Modifier};
#[cfg(feature = "globbing")] use self::glob::glob;
use rand::distributions::exponential::Exp1;
use rand::distributions::{IndependentSample, Range};
use rand::Rng;
mod parse_args;
#[derive(Debug)]
enum RuntimeError {
Directory(PathBuf),
Destination(String),
Engiffen(engiffen::Error),
}
impl From<engiffen::Error> for RuntimeError {
fn from(err: engiffen::Error) -> RuntimeError {
RuntimeError::Engiffen(err)
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RuntimeError::Directory(ref dir) => write!(f, "No such directory {:?}", dir),
RuntimeError::Destination(ref dst) => write!(f, "Couldn't write to output '{}'", dst),
RuntimeError::Engiffen(ref e) => e.fmt(f,)
}
}
}
fn run_engiffen(args: &Args) -> Result<(Option<String>, Duration), RuntimeError> {
let mut source_images: Vec<_> = match args.source {
SourceImages::StartEnd(ref dir, ref start_path, ref end_path) => {
let start_string = start_path.as_os_str();
let end_string = end_path.as_os_str();
let mut files: Vec<_> = read_dir(dir)
.map_err(|_| RuntimeError::Directory(dir.clone()))?
.filter_map(|e| e.ok())
.collect();
// Filesystem probably already sorted by name, but just in case
files.sort_by_key(|f| f.file_name());
files.iter()
.skip_while(|path| path.file_name() < start_string)
.take_while(|path| path.file_name() <= end_string)
.map(|e| e.path())
.collect()
},
SourceImages::List(ref list) => list.into_iter().map(PathBuf::from).collect(),
#[cfg(feature = "globbing")]
SourceImages::Glob(ref string) => {
let paths: Vec<_> = glob(string).expect("glob parsing failed :(")
.filter_map(std::result::Result::ok)
.collect();
#[cfg(feature = "debug-stderr")]
eprintln!("Expanded {} into {} files.", string, paths.len());
paths
},
};
modify(&mut source_images, &args.modifiers);
let imgs = engiffen::load_images(&source_images);
let now = Instant::now();
let gif = engiffen::engiffen(&imgs, args.fps, args.quantizer)?;
match args.out_file {
Some(ref filename) => {
let mut file = BufWriter::new(
File::create(filename)
.map_err(|_| RuntimeError::Destination(filename.to_owned()))?
);
gif.write(&mut file)
},
None => {
let stdout = io::stdout();
let mut handle = BufWriter::new(stdout.lock());
gif.write(&mut handle)
}
}?;
let duration = now.elapsed();
Ok((args.out_file.clone(), duration))
}
fn main() {
let arg_strings: Vec<String> = env::args().collect();
let args = parse_args(&arg_strings).map_err(|e| {
eprintln!("{}", e);
process::exit(1);
}).unwrap();
match run_engiffen(&args) {
Ok((file, duration)) => {
let ms = duration.as_secs() * 1000 + duration.subsec_nanos() as u64 / 1000000;
let filename = file.unwrap_or("to stdout".to_owned());
eprintln!("Wrote {} in {} ms", filename, ms);
},
Err(e) => {
eprintln!("{}", e);
process::exit(1);
},
}
}
fn modify<P>(source_images: &mut [P], modifiers: &[Modifier]) {
for modifier in modifiers {
match *modifier {
Modifier::Reverse => reverse(source_images),
Modifier::Shuffle => shuffle(source_images),
}
}
}
fn reverse<T>(src: &mut [T]) {
let last_index = src.len()-1;
for n in 0..(src.len()/2) {
src.swap(n, last_index-n);
}
}
fn shuffle<T>(src: &mut [T]) {
use std::cmp::{max, min};
let mut rng = rand::thread_rng();
let lenf = src.len() as f64;
for n in 1..(src.len()) {
let i = src.len() - n;
let Exp1(e) = rng.gen();
let frame_weight = i as f64 / lenf;
if e * frame_weight > 0.5 {
let range = Range::new(max(i - i/2, 0), min(src.len() - 1, i + i/2));
let j = range.ind_sample(&mut rng);
src.swap(i, j);
}
}
}