1use std::fs::File;
6use std::path::{Path, PathBuf};
7
8use api::{ExternalImageData, ImageDescriptor};
9#[cfg(feature = "png")]
10use api::ImageFormat;
11use api::units::TexelRect;
12#[cfg(feature = "png")]
13use api::units::DeviceIntSize;
14#[cfg(feature = "capture")]
15use crate::print_tree::{PrintableTree, PrintTree};
16use crate::render_api::CaptureBits;
17
18
19#[derive(Clone)]
20pub struct CaptureConfig {
21 pub root: PathBuf,
22 pub bits: CaptureBits,
23 pub scene_id: u32,
25 pub frame_id: u32,
27 pub resource_id: u32,
29 #[cfg(feature = "capture")]
30 pretty: ron::ser::PrettyConfig,
31}
32
33impl CaptureConfig {
34 #[cfg(any(feature = "capture", feature = "replay"))]
35 pub fn new(root: PathBuf, bits: CaptureBits) -> Self {
36 CaptureConfig {
37 root,
38 bits,
39 scene_id: 0,
40 frame_id: 0,
41 resource_id: 0,
42 #[cfg(feature = "capture")]
43 pretty: ron::ser::PrettyConfig::new()
44 .enumerate_arrays(true)
45 .indentor(" ".to_string()),
46 }
47 }
48
49 #[cfg(feature = "capture")]
50 pub fn prepare_scene(&mut self) {
51 use std::fs::create_dir_all;
52 self.scene_id += 1;
53 let _ = create_dir_all(&self.scene_root());
54 }
55
56 #[cfg(feature = "capture")]
57 pub fn prepare_frame(&mut self) {
58 use std::fs::create_dir_all;
59 self.frame_id += 1;
60 let _ = create_dir_all(&self.frame_root());
61 }
62
63 #[cfg(feature = "capture")]
64 pub fn prepare_resource(&mut self) {
65 use std::fs::create_dir_all;
66 self.resource_id += 1;
67 let _ = create_dir_all(&self.resource_root());
68 }
69
70 #[cfg(any(feature = "capture", feature = "replay"))]
71 pub fn scene_root(&self) -> PathBuf {
72 if self.scene_id > 0 {
73 let path = format!("scenes/{:05}", self.scene_id);
74 self.root.join(path)
75 } else {
76 self.root.clone()
77 }
78 }
79
80 #[cfg(any(feature = "capture", feature = "replay"))]
81 pub fn frame_root(&self) -> PathBuf {
82 if self.frame_id > 0 {
83 let path = format!("frames/{:05}", self.frame_id);
84 self.scene_root().join(path)
85 } else {
86 self.root.clone()
87 }
88 }
89
90 #[cfg(any(feature = "capture", feature = "replay"))]
91 pub fn resource_root(&self) -> PathBuf {
92 if self.resource_id > 0 {
93 let path = format!("resources/{:05}", self.resource_id);
94 self.root.join(path)
95 } else {
96 self.root.clone()
97 }
98 }
99
100 #[cfg(feature = "capture")]
101 pub fn serialize_for_scene<T, P>(&self, data: &T, name: P)
102 where
103 T: serde::Serialize,
104 P: AsRef<Path>,
105 {
106 self.serialize(data, self.scene_root(), name)
107 }
108
109 #[cfg(feature = "capture")]
110 pub fn serialize_for_frame<T, P>(&self, data: &T, name: P)
111 where
112 T: serde::Serialize,
113 P: AsRef<Path>,
114 {
115 self.serialize(data, self.frame_root(), name)
116 }
117
118 #[cfg(feature = "capture")]
119 pub fn serialize_for_resource<T, P>(&self, data: &T, name: P)
120 where
121 T: serde::Serialize,
122 P: AsRef<Path>,
123 {
124 self.serialize(data, self.resource_root(), name)
125 }
126
127 #[cfg(feature = "capture")]
128 pub fn file_path_for_frame<P>(&self, name: P, ext: &str) -> PathBuf
129 where P: AsRef<Path> {
130 self.frame_root().join(name).with_extension(ext)
131 }
132
133 #[cfg(feature = "capture")]
134 fn serialize<T, P>(&self, data: &T, path: PathBuf, name: P)
135 where
136 T: serde::Serialize,
137 P: AsRef<Path>,
138 {
139 use std::io::Write;
140 let ron = ron::ser::to_string_pretty(data, self.pretty.clone())
141 .unwrap();
142 let mut file = File::create(path.join(name).with_extension("ron"))
143 .unwrap();
144 write!(file, "{}\n", ron)
145 .unwrap();
146 }
147
148 #[cfg(feature = "capture")]
149 fn serialize_tree<T, P>(data: &T, root: PathBuf, name: P)
150 where
151 T: PrintableTree,
152 P: AsRef<Path>
153 {
154 let path = root
155 .join(name)
156 .with_extension("tree");
157 let file = File::create(path)
158 .unwrap();
159 let mut pt = PrintTree::new_with_sink("", file);
160 data.print_with(&mut pt);
161 }
162
163 #[cfg(feature = "capture")]
164 pub fn serialize_tree_for_frame<T, P>(&self, data: &T, name: P)
165 where
166 T: PrintableTree,
167 P: AsRef<Path>
168 {
169 Self::serialize_tree(data, self.frame_root(), name)
170 }
171
172 #[cfg(feature = "replay")]
173 fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T>
174 where
175 T: for<'a> serde::Deserialize<'a>,
176 P: AsRef<Path>,
177 {
178 use std::io::Read;
179
180 let mut string = String::new();
181 let path = root
182 .join(name.as_ref())
183 .with_extension("ron");
184 File::open(path)
185 .ok()?
186 .read_to_string(&mut string)
187 .unwrap();
188 match ron::de::from_str(&string) {
189 Ok(out) => Some(out),
190 Err(e) => panic!("File {:?} deserialization failed: {:?}", name.as_ref(), e),
191 }
192 }
193
194 #[cfg(feature = "replay")]
195 pub fn deserialize_for_scene<T, P>(&self, name: P) -> Option<T>
196 where
197 T: for<'a> serde::Deserialize<'a>,
198 P: AsRef<Path>,
199 {
200 Self::deserialize(&self.scene_root(), name)
201 }
202
203 #[cfg(feature = "replay")]
204 pub fn deserialize_for_frame<T, P>(&self, name: P) -> Option<T>
205 where
206 T: for<'a> serde::Deserialize<'a>,
207 P: AsRef<Path>,
208 {
209 Self::deserialize(&self.frame_root(), name)
210 }
211
212 #[cfg(feature = "replay")]
213 pub fn deserialize_for_resource<T, P>(&self, name: P) -> Option<T>
214 where
215 T: for<'a> serde::Deserialize<'a>,
216 P: AsRef<Path>,
217 {
218 Self::deserialize(&self.resource_root(), name)
219 }
220
221 #[cfg(feature = "png")]
222 pub fn save_png(
223 path: PathBuf, size: DeviceIntSize, format: ImageFormat, stride: Option<i32>, data: &[u8],
224 ) {
225 use png::{BitDepth, ColorType, Encoder};
226 use std::io::BufWriter;
227 use std::borrow::Cow;
228
229 let data = match stride {
231 Some(stride) if stride != format.bytes_per_pixel() * size.width => {
232 let mut unstrided = Vec::new();
233 for y in 0..size.height {
234 let start = (y * stride) as usize;
235 unstrided.extend_from_slice(&data[start..start+(size.width * format.bytes_per_pixel()) as usize]);
236 }
237 Cow::from(unstrided)
238 }
239 _ => Cow::from(data),
240 };
241
242 let color_type = match format {
243 ImageFormat::RGBA8 => ColorType::RGBA,
244 ImageFormat::BGRA8 => {
245 warn!("Unable to swizzle PNG of BGRA8 type");
246 ColorType::RGBA
247 },
248 ImageFormat::R8 => ColorType::Grayscale,
249 ImageFormat::RG8 => ColorType::GrayscaleAlpha,
250 _ => {
251 error!("Unable to save PNG of {:?}", format);
252 return;
253 }
254 };
255 let w = BufWriter::new(File::create(path).unwrap());
256 let mut enc = Encoder::new(w, size.width as u32, size.height as u32);
257 enc.set_color(color_type);
258 enc.set_depth(BitDepth::Eight);
259 enc
260 .write_header()
261 .unwrap()
262 .write_image_data(&*data)
263 .unwrap();
264 }
265}
266
267#[derive(Deserialize, Serialize)]
271pub struct ExternalCaptureImage {
272 pub short_path: String,
273 pub descriptor: ImageDescriptor,
274 pub external: ExternalImageData,
275}
276
277#[derive(Deserialize, Serialize)]
281pub struct PlainExternalImage {
282 pub data: String,
284 pub external: ExternalImageData,
286 pub uv: TexelRect,
288}