1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 // the json code is largely unfinished; allow these to silence a bunch of warnings
6 #![allow(unused_variables)]
7 #![allow(dead_code)]
8 
9 use app_units::Au;
10 use image::{save_buffer, ColorType};
11 use premultiply::unpremultiply;
12 use serde_json;
13 use std::collections::HashMap;
14 use std::io::Write;
15 use std::path::{Path, PathBuf};
16 use std::{fmt, fs};
17 use super::CURRENT_FRAME_NUMBER;
18 use time;
19 use webrender;
20 use webrender::api::*;
21 use webrender::api::channel::Payload;
22 
23 enum CachedFont {
24     Native(NativeFontHandle),
25     Raw(Option<Vec<u8>>, u32, Option<PathBuf>),
26 }
27 
28 struct CachedFontInstance {
29     font_key: FontKey,
30     glyph_size: Au,
31 }
32 
33 struct CachedImage {
34     width: u32,
35     height: u32,
36     stride: u32,
37     format: ImageFormat,
38     bytes: Option<Vec<u8>>,
39     path: Option<PathBuf>,
40 }
41 
42 pub struct JsonFrameWriter {
43     frame_base: PathBuf,
44     rsrc_base: PathBuf,
45     rsrc_prefix: String,
46     next_rsrc_num: u32,
47     images: HashMap<ImageKey, CachedImage>,
48     fonts: HashMap<FontKey, CachedFont>,
49     font_instances: HashMap<FontInstanceKey, CachedFontInstance>,
50 
51     last_frame_written: u32,
52 
53     dl_descriptor: Option<BuiltDisplayListDescriptor>,
54 }
55 
56 impl JsonFrameWriter {
new(path: &Path) -> Self57     pub fn new(path: &Path) -> Self {
58         let mut rsrc_base = path.to_owned();
59         rsrc_base.push("res");
60         fs::create_dir_all(&rsrc_base).ok();
61 
62         let rsrc_prefix = format!("{}", time::get_time().sec);
63 
64         JsonFrameWriter {
65             frame_base: path.to_owned(),
66             rsrc_base,
67             rsrc_prefix,
68             next_rsrc_num: 1,
69             images: HashMap::new(),
70             fonts: HashMap::new(),
71             font_instances: HashMap::new(),
72 
73             dl_descriptor: None,
74 
75             last_frame_written: u32::max_value(),
76         }
77     }
78 
begin_write_display_list( &mut self, _: &Epoch, _: &PipelineId, _: &Option<ColorF>, _: &LayoutSize, display_list: &BuiltDisplayListDescriptor, )79     pub fn begin_write_display_list(
80         &mut self,
81         _: &Epoch,
82         _: &PipelineId,
83         _: &Option<ColorF>,
84         _: &LayoutSize,
85         display_list: &BuiltDisplayListDescriptor,
86     ) {
87         unsafe {
88             if CURRENT_FRAME_NUMBER == self.last_frame_written {
89                 return;
90             }
91             self.last_frame_written = CURRENT_FRAME_NUMBER;
92         }
93 
94         self.dl_descriptor = Some(display_list.clone());
95     }
96 
finish_write_display_list(&mut self, frame: u32, data: &[u8])97     pub fn finish_write_display_list(&mut self, frame: u32, data: &[u8]) {
98         let payload = Payload::from_data(data);
99         let dl_desc = self.dl_descriptor.take().unwrap();
100 
101         let dl = BuiltDisplayList::from_data(payload.display_list_data, dl_desc);
102 
103         let mut frame_file_name = self.frame_base.clone();
104         let current_shown_frame = unsafe { CURRENT_FRAME_NUMBER };
105         frame_file_name.push(format!("frame-{}.json", current_shown_frame));
106 
107         let mut file = fs::File::create(&frame_file_name).unwrap();
108 
109         let s = serde_json::to_string_pretty(&dl).unwrap();
110         file.write_all(&s.into_bytes()).unwrap();
111         file.write_all(b"\n").unwrap();
112     }
113 
update_resources(&mut self, updates: &ResourceUpdates)114     fn update_resources(&mut self, updates: &ResourceUpdates) {
115         for update in &updates.updates {
116             match *update {
117                 ResourceUpdate::AddImage(ref img) => {
118                     let stride = img.descriptor.stride.unwrap_or(
119                         img.descriptor.width * img.descriptor.format.bytes_per_pixel(),
120                     );
121                     let bytes = match img.data {
122                         ImageData::Raw(ref v) => (**v).clone(),
123                         ImageData::External(_) | ImageData::Blob(_) => {
124                             return;
125                         }
126                     };
127                     self.images.insert(
128                         img.key,
129                         CachedImage {
130                             width: img.descriptor.width,
131                             height: img.descriptor.height,
132                             stride,
133                             format: img.descriptor.format,
134                             bytes: Some(bytes),
135                             path: None,
136                         },
137                     );
138                 }
139                 ResourceUpdate::UpdateImage(ref img) => {
140                     if let Some(ref mut data) = self.images.get_mut(&img.key) {
141                         assert_eq!(data.width, img.descriptor.width);
142                         assert_eq!(data.height, img.descriptor.height);
143                         assert_eq!(data.format, img.descriptor.format);
144 
145                         if let ImageData::Raw(ref bytes) = img.data {
146                             data.path = None;
147                             data.bytes = Some((**bytes).clone());
148                         } else {
149                             // Other existing image types only make sense within the gecko integration.
150                             println!(
151                                 "Wrench only supports updating buffer images ({}).",
152                                 "ignoring update commands"
153                             );
154                         }
155                     }
156                 }
157                 ResourceUpdate::DeleteImage(img) => {
158                     self.images.remove(&img);
159                 }
160                 ResourceUpdate::AddFont(ref font) => match font {
161                     &AddFont::Raw(key, ref bytes, index) => {
162                         self.fonts
163                             .insert(key, CachedFont::Raw(Some(bytes.clone()), index, None));
164                     }
165                     &AddFont::Native(key, ref handle) => {
166                         self.fonts.insert(key, CachedFont::Native(handle.clone()));
167                     }
168                 },
169                 ResourceUpdate::DeleteFont(_) => {}
170                 ResourceUpdate::AddFontInstance(ref instance) => {
171                     self.font_instances.insert(
172                         instance.key,
173                         CachedFontInstance {
174                             font_key: instance.font_key,
175                             glyph_size: instance.glyph_size,
176                         },
177                     );
178                 }
179                 ResourceUpdate::DeleteFontInstance(_) => {}
180             }
181         }
182     }
183 
next_rsrc_paths( prefix: &str, counter: &mut u32, base_path: &Path, base: &str, ext: &str, ) -> (PathBuf, PathBuf)184     fn next_rsrc_paths(
185         prefix: &str,
186         counter: &mut u32,
187         base_path: &Path,
188         base: &str,
189         ext: &str,
190     ) -> (PathBuf, PathBuf) {
191         let mut path_file = base_path.to_owned();
192         let mut path = PathBuf::from("res");
193 
194         let fstr = format!("{}-{}-{}.{}", prefix, base, counter, ext);
195         path_file.push(&fstr);
196         path.push(&fstr);
197 
198         *counter += 1;
199 
200         (path_file, path)
201     }
202 
path_for_image(&mut self, key: ImageKey) -> Option<PathBuf>203     fn path_for_image(&mut self, key: ImageKey) -> Option<PathBuf> {
204         if let Some(ref mut data) = self.images.get_mut(&key) {
205             if data.path.is_some() {
206                 return data.path.clone();
207             }
208         } else {
209             return None;
210         };
211 
212         // Remove the data to munge it
213         let mut data = self.images.remove(&key).unwrap();
214         let mut bytes = data.bytes.take().unwrap();
215         let (path_file, path) = Self::next_rsrc_paths(
216             &self.rsrc_prefix,
217             &mut self.next_rsrc_num,
218             &self.rsrc_base,
219             "img",
220             "png",
221         );
222 
223         let ok = match data.format {
224             ImageFormat::BGRA8 => if data.stride == data.width * 4 {
225                 unpremultiply(bytes.as_mut_slice());
226                 save_buffer(
227                     &path_file,
228                     &bytes,
229                     data.width,
230                     data.height,
231                     ColorType::RGBA(8),
232                 ).unwrap();
233                 true
234             } else {
235                 false
236             },
237             ImageFormat::R8 => if data.stride == data.width {
238                 save_buffer(
239                     &path_file,
240                     &bytes,
241                     data.width,
242                     data.height,
243                     ColorType::Gray(8),
244                 ).unwrap();
245                 true
246             } else {
247                 false
248             },
249             _ => false,
250         };
251 
252         if !ok {
253             println!(
254                 "Failed to write image with format {:?}, dimensions {}x{}, stride {}",
255                 data.format,
256                 data.width,
257                 data.height,
258                 data.stride
259             );
260             return None;
261         }
262 
263         data.path = Some(path.clone());
264         // put it back
265         self.images.insert(key, data);
266         Some(path)
267     }
268 }
269 
270 impl fmt::Debug for JsonFrameWriter {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result271     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272         write!(f, "JsonFrameWriter")
273     }
274 }
275 
276 impl webrender::ApiRecordingReceiver for JsonFrameWriter {
write_msg(&mut self, _: u32, msg: &ApiMsg)277     fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
278         match *msg {
279             ApiMsg::UpdateResources(ref updates) => self.update_resources(updates),
280 
281             ApiMsg::UpdateDocument(_, ref txn) => {
282                 self.update_resources(&txn.resource_updates);
283                 for doc_msg in &txn.scene_ops {
284                     match *doc_msg {
285                         SceneMsg::SetDisplayList {
286                             ref epoch,
287                             ref pipeline_id,
288                             ref background,
289                             ref viewport_size,
290                             ref list_descriptor,
291                             ..
292                         } => {
293                             self.begin_write_display_list(
294                                 epoch,
295                                 pipeline_id,
296                                 background,
297                                 viewport_size,
298                                 list_descriptor,
299                             );
300                         }
301                         _ => {}
302                     }
303                 }
304             }
305             ApiMsg::CloneApi(..) => {}
306             _ => {}
307         }
308     }
309 
write_payload(&mut self, frame: u32, data: &[u8])310     fn write_payload(&mut self, frame: u32, data: &[u8]) {
311         if self.dl_descriptor.is_some() {
312             self.finish_write_display_list(frame, data);
313         }
314     }
315 }
316