···
58
58
}
59
59
60
60
/// Corners of the region's outline
61
61
-
/// Does _not_ match .bottomright() etc., since
61
61
+
/// Does _not_ match .bottomright() etc., since
62
62
/// this method takes into account that the region is inclusive
63
63
/// topleft, topright, bottomright, bottomleft
64
64
pub fn corners(&self) -> [Point; 4] {
···
1
1
-
use crate::{
2
2
-
Video,
3
3
-
ui::{Log, Pretty},
4
4
-
video::{encoders::vgv::VGVTranscodeMode, engine::EngineOutput},
5
5
-
};
6
6
-
use anyhow::Result;
7
7
-
use std::path::PathBuf;
8
8
-
9
9
-
pub mod ffmpeg;
10
10
-
pub mod vgv;
11
11
-
12
12
-
pub trait Encoder {
13
13
-
fn name(&self) -> String;
14
14
-
fn encode_frame(&mut self, output: EngineOutput) -> Result<()>;
15
15
-
fn finish(&mut self) -> Result<()>;
16
16
-
fn finish_message(&self, time_elapsed: std::time::Duration) -> String;
17
17
-
fn progress_message(&self, current: u64, total: u64) -> String {
18
18
-
format!("{}/{} frames", current, total,)
19
19
-
}
20
20
-
}
21
21
-
22
22
-
impl<C: Default> Video<C> {
23
23
-
pub(crate) fn setup_encoder(
24
24
-
&mut self,
25
25
-
output_path: impl Into<PathBuf>,
26
26
-
) -> Result<Box<dyn Encoder + Send>> {
27
27
-
let (width, height) =
28
28
-
self.initial_canvas.resolution_to_size_even(self.resolution);
29
29
-
30
30
-
let destination = output_path.into();
31
31
-
let pb = &self.progress_bars.encoding;
32
32
-
33
33
-
if destination.exists() {
34
34
-
std::fs::remove_file(&destination)?;
35
35
-
}
36
36
-
37
37
-
std::fs::create_dir_all(
38
38
-
&destination
39
39
-
.parent()
40
40
-
.expect("Given output file has no parent"),
41
41
-
)?;
42
42
-
43
43
-
Ok(match destination.full_extension() {
44
44
-
".vgv.html" => {
45
45
-
self.progress_bars.encoding.log(
46
46
-
"Selecting",
47
47
-
&format!(
48
48
-
"VGV encoder with HTML transcoding as {} ends with .vgv.html",
49
49
-
destination.pretty(),
50
50
-
),
51
51
-
);
52
52
-
53
53
-
Box::new(self.setup_vgv_encoder(
54
54
-
VGVTranscodeMode::ToHTML,
55
55
-
width as _,
56
56
-
height as _,
57
57
-
&self.initial_canvas,
58
58
-
destination,
59
59
-
)?)
60
60
-
}
61
61
-
".vgv" => {
62
62
-
self.progress_bars.encoding.log(
63
63
-
"Selecting",
64
64
-
&format!(
65
65
-
"VGV encoder as {} ends with .vgv (use .vgv.html for HTML transcoding)",
66
66
-
destination.pretty(),
67
67
-
),
68
68
-
);
69
69
-
70
70
-
Box::new(self.setup_vgv_encoder(
71
71
-
VGVTranscodeMode::None,
72
72
-
width as _,
73
73
-
height as _,
74
74
-
&self.initial_canvas,
75
75
-
destination,
76
76
-
)?)
77
77
-
}
78
78
-
_ => {
79
79
-
pb.log(
80
80
-
"Selecting",
81
81
-
&format!(
82
82
-
"FFMpeg encoder as {} ends with {}",
83
83
-
destination.pretty(),
84
84
-
destination.full_extension()
85
85
-
),
86
86
-
);
87
87
-
88
88
-
self.initial_canvas.load_fonts()?;
89
89
-
Box::new(self.setup_ffmpeg_encoder(width, height, destination)?)
90
90
-
}
91
91
-
})
92
92
-
}
93
93
-
}
94
94
-
95
95
-
96
96
-
// Because .extension() sucks
97
97
-
98
98
-
trait FullExtension {
99
99
-
fn full_extension(&self) -> &str;
100
100
-
}
101
101
-
102
102
-
impl FullExtension for PathBuf {
103
103
-
fn full_extension(&self) -> &str {
104
104
-
let filename = self
105
105
-
.file_name()
106
106
-
.and_then(|f| f.to_str())
107
107
-
.unwrap_or_default();
108
108
-
let parts: Vec<&str> = filename.split('.').collect();
109
109
-
if parts.len() <= 1 {
110
110
-
""
111
111
-
} else {
112
112
-
&filename[filename.find('.').unwrap()..]
113
113
-
}
114
114
-
}
115
115
-
}
1
1
+
use crate::{
2
2
+
Video,
3
3
+
ui::{Log, Pretty},
4
4
+
video::{encoders::vgv::VGVTranscodeMode, engine::EngineOutput},
5
5
+
};
6
6
+
use anyhow::Result;
7
7
+
use std::path::PathBuf;
8
8
+
9
9
+
pub mod ffmpeg;
10
10
+
pub mod vgv;
11
11
+
12
12
+
pub trait Encoder {
13
13
+
fn name(&self) -> String;
14
14
+
fn encode_frame(&mut self, output: EngineOutput) -> Result<()>;
15
15
+
fn finish(&mut self) -> Result<()>;
16
16
+
fn finish_message(&self, time_elapsed: std::time::Duration) -> String;
17
17
+
fn progress_message(&self, current: u64, total: u64) -> String {
18
18
+
format!("{}/{} frames", current, total,)
19
19
+
}
20
20
+
}
21
21
+
22
22
+
impl<C: Default> Video<C> {
23
23
+
pub(crate) fn setup_encoder(
24
24
+
&mut self,
25
25
+
output_path: impl Into<PathBuf>,
26
26
+
) -> Result<Box<dyn Encoder + Send>> {
27
27
+
let (width, height) =
28
28
+
self.initial_canvas.resolution_to_size_even(self.resolution);
29
29
+
30
30
+
let destination = output_path.into();
31
31
+
let pb = &self.progress_bars.encoding;
32
32
+
33
33
+
if destination.exists() {
34
34
+
std::fs::remove_file(&destination)?;
35
35
+
}
36
36
+
37
37
+
std::fs::create_dir_all(
38
38
+
&destination
39
39
+
.parent()
40
40
+
.expect("Given output file has no parent"),
41
41
+
)?;
42
42
+
43
43
+
Ok(match destination.full_extension() {
44
44
+
".vgv.html" => {
45
45
+
self.progress_bars.encoding.log(
46
46
+
"Selecting",
47
47
+
&format!(
48
48
+
"VGV encoder with HTML transcoding as {} ends with .vgv.html",
49
49
+
destination.pretty(),
50
50
+
),
51
51
+
);
52
52
+
53
53
+
Box::new(self.setup_vgv_encoder(
54
54
+
VGVTranscodeMode::ToHTML,
55
55
+
width as _,
56
56
+
height as _,
57
57
+
&self.initial_canvas,
58
58
+
destination,
59
59
+
)?)
60
60
+
}
61
61
+
".vgv" => {
62
62
+
self.progress_bars.encoding.log(
63
63
+
"Selecting",
64
64
+
&format!(
65
65
+
"VGV encoder as {} ends with .vgv (use .vgv.html for HTML transcoding)",
66
66
+
destination.pretty(),
67
67
+
),
68
68
+
);
69
69
+
70
70
+
Box::new(self.setup_vgv_encoder(
71
71
+
VGVTranscodeMode::None,
72
72
+
width as _,
73
73
+
height as _,
74
74
+
&self.initial_canvas,
75
75
+
destination,
76
76
+
)?)
77
77
+
}
78
78
+
_ => {
79
79
+
pb.log(
80
80
+
"Selecting",
81
81
+
&format!(
82
82
+
"FFMpeg encoder as {} ends with {}",
83
83
+
destination.pretty(),
84
84
+
destination.full_extension()
85
85
+
),
86
86
+
);
87
87
+
88
88
+
self.initial_canvas.load_fonts()?;
89
89
+
Box::new(self.setup_ffmpeg_encoder(width, height, destination)?)
90
90
+
}
91
91
+
})
92
92
+
}
93
93
+
}
94
94
+
95
95
+
// Because .extension() sucks
96
96
+
97
97
+
trait FullExtension {
98
98
+
fn full_extension(&self) -> &str;
99
99
+
}
100
100
+
101
101
+
impl FullExtension for PathBuf {
102
102
+
fn full_extension(&self) -> &str {
103
103
+
let filename = self
104
104
+
.file_name()
105
105
+
.and_then(|f| f.to_str())
106
106
+
.unwrap_or_default();
107
107
+
let parts: Vec<&str> = filename.split('.').collect();
108
108
+
if parts.len() <= 1 {
109
109
+
""
110
110
+
} else {
111
111
+
&filename[filename.find('.').unwrap()..]
112
112
+
}
113
113
+
}
114
114
+
}