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 extern crate yaml_rust;
6
7 use app_units::Au;
8 use euclid::{TypedPoint2D, TypedRect, TypedSize2D, TypedTransform3D, TypedVector2D};
9 use image::{save_buffer, ColorType};
10 use premultiply::unpremultiply;
11 use scene::{Scene, SceneProperties};
12 use std::collections::HashMap;
13 use std::io::Write;
14 use std::path::{Path, PathBuf};
15 use std::{fmt, fs};
16 use super::CURRENT_FRAME_NUMBER;
17 use time;
18 use webrender;
19 use webrender::api::*;
20 use webrender::api::SpecificDisplayItem::*;
21 use webrender::api::channel::Payload;
22 use yaml_helper::StringEnum;
23 use yaml_rust::{Yaml, YamlEmitter};
24
25 type Table = yaml_rust::yaml::Hash;
26
array_elements_are_same<T: PartialEq>(v: &[T]) -> bool27 fn array_elements_are_same<T: PartialEq>(v: &[T]) -> bool {
28 if !v.is_empty() {
29 let first = &v[0];
30 for o in v.iter() {
31 if *first != *o {
32 return false;
33 }
34 }
35 }
36 true
37 }
38
new_table() -> Table39 fn new_table() -> Table {
40 Table::new()
41 }
42
yaml_node(parent: &mut Table, key: &str, value: Yaml)43 fn yaml_node(parent: &mut Table, key: &str, value: Yaml) {
44 parent.insert(Yaml::String(key.to_owned()), value);
45 }
46
str_node(parent: &mut Table, key: &str, value: &str)47 fn str_node(parent: &mut Table, key: &str, value: &str) {
48 yaml_node(parent, key, Yaml::String(value.to_owned()));
49 }
50
path_node(parent: &mut Table, key: &str, value: &Path)51 fn path_node(parent: &mut Table, key: &str, value: &Path) {
52 let pstr = value.to_str().unwrap().to_owned().replace("\\", "/");
53 yaml_node(parent, key, Yaml::String(pstr));
54 }
55
56 fn enum_node<E: StringEnum>(parent: &mut Table, key: &str, value: E) {
57 yaml_node(parent, key, Yaml::String(value.as_str().to_owned()));
58 }
59
60 fn color_to_string(value: ColorF) -> String {
61 if value.r == 1.0 && value.g == 1.0 && value.b == 1.0 && value.a == 1.0 {
62 "white".to_owned()
63 } else if value.r == 0.0 && value.g == 0.0 && value.b == 0.0 && value.a == 1.0 {
64 "black".to_owned()
65 } else {
66 format!(
67 "{} {} {} {:.4}",
68 value.r * 255.0,
69 value.g * 255.0,
70 value.b * 255.0,
71 value.a
72 )
73 }
74 }
75
76 fn color_node(parent: &mut Table, key: &str, value: ColorF) {
77 yaml_node(parent, key, Yaml::String(color_to_string(value)));
78 }
79
80 fn point_node<U>(parent: &mut Table, key: &str, value: &TypedPoint2D<f32, U>) {
81 f32_vec_node(parent, key, &[value.x, value.y]);
82 }
83
84 fn vector_node<U>(parent: &mut Table, key: &str, value: &TypedVector2D<f32, U>) {
85 f32_vec_node(parent, key, &[value.x, value.y]);
86 }
87
88 fn size_node<U>(parent: &mut Table, key: &str, value: &TypedSize2D<f32, U>) {
89 f32_vec_node(parent, key, &[value.width, value.height]);
90 }
91
92 fn rect_yaml<U>(value: &TypedRect<f32, U>) -> Yaml {
93 f32_vec_yaml(
94 &[
95 value.origin.x,
96 value.origin.y,
97 value.size.width,
98 value.size.height,
99 ],
100 false,
101 )
102 }
103
104 fn rect_node<U>(parent: &mut Table, key: &str, value: &TypedRect<f32, U>) {
105 yaml_node(parent, key, rect_yaml(value));
106 }
107
108 fn matrix4d_node<U1, U2>(parent: &mut Table, key: &str, value: &TypedTransform3D<f32, U1, U2>) {
109 f32_vec_node(parent, key, &value.to_row_major_array());
110 }
111
112 fn u32_node(parent: &mut Table, key: &str, value: u32) {
113 yaml_node(parent, key, Yaml::Integer(value as i64));
114 }
115
116 fn usize_node(parent: &mut Table, key: &str, value: usize) {
117 yaml_node(parent, key, Yaml::Integer(value as i64));
118 }
119
120 fn f32_node(parent: &mut Table, key: &str, value: f32) {
121 yaml_node(parent, key, Yaml::Real(value.to_string()));
122 }
123
124 fn bool_node(parent: &mut Table, key: &str, value: bool) {
125 yaml_node(parent, key, Yaml::Boolean(value));
126 }
127
128 fn table_node(parent: &mut Table, key: &str, value: Table) {
129 yaml_node(parent, key, Yaml::Hash(value));
130 }
131
132 fn string_vec_yaml(value: &[String], check_unique: bool) -> Yaml {
133 if !value.is_empty() && check_unique && array_elements_are_same(value) {
134 Yaml::String(value[0].clone())
135 } else {
136 Yaml::Array(value.iter().map(|v| Yaml::String(v.clone())).collect())
137 }
138 }
139
140 fn u32_vec_yaml(value: &[u32], check_unique: bool) -> Yaml {
141 if !value.is_empty() && check_unique && array_elements_are_same(value) {
142 Yaml::Integer(value[0] as i64)
143 } else {
144 Yaml::Array(value.iter().map(|v| Yaml::Integer(*v as i64)).collect())
145 }
146 }
147
148 fn u32_vec_node(parent: &mut Table, key: &str, value: &[u32]) {
149 yaml_node(parent, key, u32_vec_yaml(value, false));
150 }
151
152 fn f32_vec_yaml(value: &[f32], check_unique: bool) -> Yaml {
153 if !value.is_empty() && check_unique && array_elements_are_same(value) {
154 Yaml::Real(value[0].to_string())
155 } else {
156 Yaml::Array(value.iter().map(|v| Yaml::Real(v.to_string())).collect())
157 }
158 }
159
160 fn f32_vec_node(parent: &mut Table, key: &str, value: &[f32]) {
161 yaml_node(parent, key, f32_vec_yaml(value, false));
162 }
163
164 fn maybe_radius_yaml(radius: &BorderRadius) -> Option<Yaml> {
165 if let Some(radius) = radius.is_uniform_size() {
166 if radius == LayoutSize::zero() {
167 None
168 } else {
169 Some(f32_vec_yaml(&[radius.width, radius.height], false))
170 }
171 } else {
172 let mut table = new_table();
173 size_node(&mut table, "top-left", &radius.top_left);
174 size_node(&mut table, "top-right", &radius.top_right);
175 size_node(&mut table, "bottom-left", &radius.bottom_left);
176 size_node(&mut table, "bottom-right", &radius.bottom_right);
177 Some(Yaml::Hash(table))
178 }
179 }
180
181 fn write_sc(parent: &mut Table, sc: &StackingContext, properties: &SceneProperties, filter_iter: AuxIter<FilterOp>) {
182 enum_node(parent, "scroll-policy", sc.scroll_policy);
183
184 matrix4d_node(parent, "transform", &properties.resolve_layout_transform(&sc.transform));
185
186 enum_node(parent, "transform-style", sc.transform_style);
187
188 if let Some(perspective) = sc.perspective {
189 matrix4d_node(parent, "perspective", &perspective);
190 }
191
192 // mix_blend_mode
193 if sc.mix_blend_mode != MixBlendMode::Normal {
194 enum_node(parent, "mix-blend-mode", sc.mix_blend_mode)
195 }
196 // filters
197 let mut filters = vec![];
198 for filter in filter_iter {
199 match filter {
200 FilterOp::Blur(x) => { filters.push(Yaml::String(format!("blur({})", x))) }
201 FilterOp::Brightness(x) => { filters.push(Yaml::String(format!("brightness({})", x))) }
202 FilterOp::Contrast(x) => { filters.push(Yaml::String(format!("contrast({})", x))) }
203 FilterOp::Grayscale(x) => { filters.push(Yaml::String(format!("grayscale({})", x))) }
204 FilterOp::HueRotate(x) => { filters.push(Yaml::String(format!("hue-rotate({})", x))) }
205 FilterOp::Invert(x) => { filters.push(Yaml::String(format!("invert({})", x))) }
206 FilterOp::Opacity(x, _) => {
207 filters.push(Yaml::String(format!("opacity({})",
208 properties.resolve_float(&x, 1.0))))
209 }
210 FilterOp::Saturate(x) => { filters.push(Yaml::String(format!("saturate({})", x))) }
211 FilterOp::Sepia(x) => { filters.push(Yaml::String(format!("sepia({})", x))) }
212 FilterOp::DropShadow(offset, blur, color) => {
213 filters.push(Yaml::String(format!("drop-shadow([{},{}],{},[{}])",
214 offset.x, offset.y,
215 blur,
216 color_to_string(color))))
217 }
218 FilterOp::ColorMatrix(matrix) => {
219 filters.push(Yaml::String(format!("color-matrix({:?})", matrix)))
220 }
221 }
222 }
223
224 yaml_node(parent, "filters", Yaml::Array(filters));
225 }
226
227 #[cfg(target_os = "windows")]
228 fn native_font_handle_to_yaml(
229 _rsrc: &mut ResourceGenerator,
230 handle: &NativeFontHandle,
231 parent: &mut yaml_rust::yaml::Hash,
232 _: &mut Option<PathBuf>,
233 ) {
234 str_node(parent, "family", &handle.family_name);
235 u32_node(parent, "weight", handle.weight.to_u32());
236 u32_node(parent, "style", handle.style.to_u32());
237 u32_node(parent, "stretch", handle.stretch.to_u32());
238 }
239
240 #[cfg(target_os = "macos")]
241 fn native_font_handle_to_yaml(
242 rsrc: &mut ResourceGenerator,
243 handle: &NativeFontHandle,
244 parent: &mut yaml_rust::yaml::Hash,
245 path_opt: &mut Option<PathBuf>,
246 ) {
247 let path = match *path_opt {
248 Some(ref path) => { path.clone() },
249 None => {
250 use cgfont_to_data;
251 let bytes = cgfont_to_data::font_to_data(handle.0.clone()).unwrap();
252 let (path_file, path) = rsrc.next_rsrc_paths(
253 "font",
254 "ttf",
255 );
256 let mut file = fs::File::create(&path_file).unwrap();
257 file.write_all(&bytes).unwrap();
258 *path_opt = Some(path.clone());
259 path
260 }
261 };
262
263 path_node(parent, "font", &path);
264 }
265
266 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
267 fn native_font_handle_to_yaml(
268 _rsrc: &mut ResourceGenerator,
269 handle: &NativeFontHandle,
270 parent: &mut yaml_rust::yaml::Hash,
271 _: &mut Option<PathBuf>,
272 ) {
273 str_node(parent, "font", &handle.pathname);
274 }
275
276 enum CachedFont {
277 Native(NativeFontHandle, Option<PathBuf>),
278 Raw(Option<Vec<u8>>, u32, Option<PathBuf>),
279 }
280
281 struct CachedFontInstance {
282 font_key: FontKey,
283 glyph_size: Au,
284 }
285
286 struct CachedImage {
287 width: u32,
288 height: u32,
289 stride: u32,
290 format: ImageFormat,
291 bytes: Option<Vec<u8>>,
292 path: Option<PathBuf>,
293 tiling: Option<u16>,
294 }
295
296 struct ResourceGenerator {
297 base: PathBuf,
298 next_num: u32,
299 prefix: String,
300 }
301
302 impl ResourceGenerator {
303 fn next_rsrc_paths(&mut self, base: &str, ext: &str) -> (PathBuf, PathBuf) {
304 let mut path_file = self.base.to_owned();
305 let mut path = PathBuf::from("res");
306
307 let fstr = format!("{}-{}-{}.{}", self.prefix, base, self.next_num, ext);
308 path_file.push(&fstr);
309 path.push(&fstr);
310
311 self.next_num += 1;
312
313 (path_file, path)
314 }
315 }
316
317 pub struct YamlFrameWriter {
318 frame_base: PathBuf,
319 rsrc_gen: ResourceGenerator,
320 images: HashMap<ImageKey, CachedImage>,
321 fonts: HashMap<FontKey, CachedFont>,
322 font_instances: HashMap<FontInstanceKey, CachedFontInstance>,
323
324 last_frame_written: u32,
325 pipeline_id: Option<PipelineId>,
326
327 dl_descriptor: Option<BuiltDisplayListDescriptor>,
328 }
329
330 pub struct YamlFrameWriterReceiver {
331 frame_writer: YamlFrameWriter,
332 scene: Scene,
333 }
334
335 impl YamlFrameWriterReceiver {
336 pub fn new(path: &Path) -> YamlFrameWriterReceiver {
337 YamlFrameWriterReceiver {
338 frame_writer: YamlFrameWriter::new(path),
339 scene: Scene::new(),
340 }
341 }
342 }
343
344 impl fmt::Debug for YamlFrameWriterReceiver {
345 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346 write!(f, "YamlFrameWriterReceiver")
347 }
348 }
349
350 impl YamlFrameWriter {
351 pub fn new(path: &Path) -> YamlFrameWriter {
352 let mut rsrc_base = path.to_owned();
353 rsrc_base.push("res");
354 fs::create_dir_all(&rsrc_base).ok();
355
356 let rsrc_prefix = format!("{}", time::get_time().sec);
357
358 YamlFrameWriter {
359 frame_base: path.to_owned(),
360 rsrc_gen: ResourceGenerator {
361 base: rsrc_base,
362 prefix: rsrc_prefix,
363 next_num: 1,
364 },
365 images: HashMap::new(),
366 fonts: HashMap::new(),
367 font_instances: HashMap::new(),
368
369 dl_descriptor: None,
370
371 pipeline_id: None,
372
373 last_frame_written: u32::max_value(),
374 }
375 }
376
377 pub fn begin_write_display_list(
378 &mut self,
379 scene: &mut Scene,
380 epoch: &Epoch,
381 pipeline_id: &PipelineId,
382 background_color: &Option<ColorF>,
383 viewport_size: &LayoutSize,
384 display_list: &BuiltDisplayListDescriptor,
385 ) {
386 unsafe {
387 if CURRENT_FRAME_NUMBER == self.last_frame_written {
388 return;
389 }
390 self.last_frame_written = CURRENT_FRAME_NUMBER;
391 }
392
393 self.dl_descriptor = Some(display_list.clone());
394 self.pipeline_id = Some(pipeline_id.clone());
395
396 scene.begin_display_list(pipeline_id, epoch, background_color, viewport_size);
397 }
398
399 pub fn finish_write_display_list(&mut self, scene: &mut Scene, data: &[u8]) {
400 let dl_desc = self.dl_descriptor.take().unwrap();
401
402 let payload = Payload::from_data(data);
403
404 let dl = BuiltDisplayList::from_data(payload.display_list_data, dl_desc);
405
406 let mut root_dl_table = new_table();
407 {
408 let mut iter = dl.iter();
409 self.write_display_list(&mut root_dl_table, &dl, scene, &mut iter, &mut ClipIdMapper::new());
410 }
411
412 let mut root = new_table();
413 if let Some(root_pipeline_id) = scene.root_pipeline_id {
414 u32_vec_node(
415 &mut root_dl_table,
416 "id",
417 &[root_pipeline_id.0, root_pipeline_id.1],
418 );
419
420 let mut referenced_pipeline_ids = vec![];
421 let mut traversal = dl.iter();
422 while let Some(item) = traversal.next() {
423 if let &SpecificDisplayItem::Iframe(k) = item.item() {
424 referenced_pipeline_ids.push(k.pipeline_id);
425 }
426 }
427
428 let mut pipelines = vec![];
429 for pipeline_id in referenced_pipeline_ids {
430 if !scene.display_lists.contains_key(&pipeline_id) {
431 continue;
432 }
433 let mut pipeline = new_table();
434 u32_vec_node(&mut pipeline, "id", &[pipeline_id.0, pipeline_id.1]);
435
436 let dl = scene.display_lists.get(&pipeline_id).unwrap();
437 let mut iter = dl.iter();
438 self.write_display_list(&mut pipeline, &dl, scene, &mut iter, &mut ClipIdMapper::new());
439 pipelines.push(Yaml::Hash(pipeline));
440 }
441
442 table_node(&mut root, "root", root_dl_table);
443
444 root.insert(Yaml::String("pipelines".to_owned()), Yaml::Array(pipelines));
445
446 let mut s = String::new();
447 // FIXME YamlEmitter wants a std::fmt::Write, not a io::Write, so we can't pass a file
448 // directly. This seems broken.
449 {
450 let mut emitter = YamlEmitter::new(&mut s);
451 emitter.dump(&Yaml::Hash(root)).unwrap();
452 }
453 let sb = s.into_bytes();
454 let mut frame_file_name = self.frame_base.clone();
455 let current_shown_frame = unsafe { CURRENT_FRAME_NUMBER };
456 frame_file_name.push(format!("frame-{}.yaml", current_shown_frame));
457 let mut file = fs::File::create(&frame_file_name).unwrap();
458 file.write_all(&sb).unwrap();
459 }
460
461 scene.finish_display_list(self.pipeline_id.unwrap(), dl);
462 }
463
464 fn update_resources(&mut self, updates: &ResourceUpdates) {
465 for update in &updates.updates {
466 match *update {
467 ResourceUpdate::AddImage(ref img) => {
468 if let Some(ref data) = self.images.get(&img.key) {
469 if data.path.is_some() {
470 return;
471 }
472 }
473
474 let stride = img.descriptor.stride.unwrap_or(
475 img.descriptor.width * img.descriptor.format.bytes_per_pixel(),
476 );
477 let bytes = match img.data {
478 ImageData::Raw(ref v) => (**v).clone(),
479 ImageData::External(_) | ImageData::Blob(_) => {
480 return;
481 }
482 };
483 self.images.insert(
484 img.key,
485 CachedImage {
486 width: img.descriptor.width,
487 height: img.descriptor.height,
488 stride,
489 format: img.descriptor.format,
490 bytes: Some(bytes),
491 tiling: img.tiling,
492 path: None,
493 },
494 );
495 }
496 ResourceUpdate::UpdateImage(ref img) => {
497 if let Some(ref mut data) = self.images.get_mut(&img.key) {
498 assert_eq!(data.width, img.descriptor.width);
499 assert_eq!(data.height, img.descriptor.height);
500 assert_eq!(data.format, img.descriptor.format);
501
502 if let ImageData::Raw(ref bytes) = img.data {
503 data.path = None;
504 data.bytes = Some((**bytes).clone());
505 } else {
506 // Other existing image types only make sense within the gecko integration.
507 println!(
508 "Wrench only supports updating buffer images ({}).",
509 "ignoring update command"
510 );
511 }
512 }
513 }
514 ResourceUpdate::DeleteImage(img) => {
515 self.images.remove(&img);
516 }
517 ResourceUpdate::AddFont(ref font) => match font {
518 &AddFont::Raw(key, ref bytes, index) => {
519 self.fonts
520 .insert(key, CachedFont::Raw(Some(bytes.clone()), index, None));
521 }
522 &AddFont::Native(key, ref handle) => {
523 self.fonts.insert(key, CachedFont::Native(handle.clone(), None));
524 }
525 },
526 ResourceUpdate::DeleteFont(_) => {}
527 ResourceUpdate::AddFontInstance(ref instance) => {
528 self.font_instances.insert(
529 instance.key,
530 CachedFontInstance {
531 font_key: instance.font_key,
532 glyph_size: instance.glyph_size,
533 },
534 );
535 }
536 ResourceUpdate::DeleteFontInstance(_) => {}
537 }
538 }
539 }
540
541
542
543 fn path_for_image(&mut self, key: ImageKey) -> Option<PathBuf> {
544 let data = match self.images.get_mut(&key) {
545 Some(data) => data,
546 None => return None,
547 };
548
549 if data.path.is_some() {
550 return data.path.clone();
551 }
552 let mut bytes = data.bytes.take().unwrap();
553 let (path_file, path) = self.rsrc_gen.next_rsrc_paths(
554 "img",
555 "png",
556 );
557
558 assert!(data.stride > 0);
559 let (color_type, bpp) = match data.format {
560 ImageFormat::BGRA8 => (ColorType::RGBA(8), 4),
561 ImageFormat::R8 => (ColorType::Gray(8), 1),
562 _ => {
563 println!(
564 "Failed to write image with format {:?}, dimensions {}x{}, stride {}",
565 data.format,
566 data.width,
567 data.height,
568 data.stride
569 );
570 return None;
571 }
572 };
573
574 if data.stride == data.width * bpp {
575 if data.format == ImageFormat::BGRA8 {
576 unpremultiply(bytes.as_mut_slice());
577 }
578 save_buffer(&path_file, &bytes, data.width, data.height, color_type).unwrap();
579 } else {
580 // takes a buffer with a stride and copies it into a new buffer that has stride == width
581 assert!(data.stride > data.width * bpp);
582 let mut tmp: Vec<_> = bytes[..]
583 .chunks(data.stride as usize)
584 .flat_map(|chunk| {
585 chunk[.. (data.width * bpp) as usize].iter().cloned()
586 })
587 .collect();
588 if data.format == ImageFormat::BGRA8 {
589 unpremultiply(tmp.as_mut_slice());
590 }
591
592 save_buffer(&path_file, &tmp, data.width, data.height, color_type).unwrap();
593 }
594
595 data.path = Some(path.clone());
596 Some(path)
597 }
598
599 fn make_complex_clip_node(&mut self, complex_clip: &ComplexClipRegion) -> Yaml {
600 let mut t = new_table();
601 rect_node(&mut t, "rect", &complex_clip.rect);
602 yaml_node(
603 &mut t,
604 "radius",
605 maybe_radius_yaml(&complex_clip.radii).unwrap(),
606 );
607 enum_node(&mut t, "clip-mode", complex_clip.mode);
608 Yaml::Hash(t)
609 }
610
611 fn make_complex_clips_node(
612 &mut self,
613 complex_clip_count: usize,
614 complex_clips: ItemRange<ComplexClipRegion>,
615 list: &BuiltDisplayList,
616 ) -> Option<Yaml> {
617 if complex_clip_count == 0 {
618 return None;
619 }
620
621 let complex_items = list.get(complex_clips)
622 .map(|ccx| if ccx.radii.is_zero() {
623 rect_yaml(&ccx.rect)
624 } else {
625 self.make_complex_clip_node(&ccx)
626 })
627 .collect();
628 Some(Yaml::Array(complex_items))
629 }
630
631 fn make_clip_mask_image_node(&mut self, image_mask: &Option<ImageMask>) -> Option<Yaml> {
632 let mask = match image_mask {
633 &Some(ref mask) => mask,
634 &None => return None,
635 };
636
637 let mut mask_table = new_table();
638 if let Some(path) = self.path_for_image(mask.image) {
639 path_node(&mut mask_table, "image", &path);
640 }
641 rect_node(&mut mask_table, "rect", &mask.rect);
642 bool_node(&mut mask_table, "repeat", mask.repeat);
643 Some(Yaml::Hash(mask_table))
644 }
645
646 fn write_display_list_items(
647 &mut self,
648 list: &mut Vec<Yaml>,
649 display_list: &BuiltDisplayList,
650 scene: &Scene,
651 list_iterator: &mut BuiltDisplayListIter,
652 clip_id_mapper: &mut ClipIdMapper,
653 ) {
654 // continue_traversal is a big borrowck hack
655 let mut continue_traversal = None;
656 loop {
657 if let Some(traversal) = continue_traversal.take() {
658 *list_iterator = traversal;
659 }
660 let base = match list_iterator.next() {
661 Some(base) => base,
662 None => break,
663 };
664
665 let mut v = new_table();
666 rect_node(&mut v, "bounds", &base.rect());
667
668 rect_node(&mut v, "clip-rect", base.local_clip().clip_rect());
669 if let &LocalClip::RoundedRect(_, ref region) = base.local_clip() {
670 yaml_node(&mut v, "complex-clip", self.make_complex_clip_node(region));
671 }
672
673 let clip_and_scroll_yaml = match clip_id_mapper.map_info(&base.clip_and_scroll()) {
674 (scroll_id, Some(clip_id)) => {
675 Yaml::Array(vec![Yaml::Integer(scroll_id), Yaml::Integer(clip_id)])
676 }
677 (scroll_id, None) => Yaml::Integer(scroll_id),
678 };
679 yaml_node(&mut v, "clip-and-scroll", clip_and_scroll_yaml);
680 bool_node(&mut v, "backface-visible", base.is_backface_visible());
681
682 match *base.item() {
683 Rectangle(item) => {
684 str_node(&mut v, "type", "rect");
685 color_node(&mut v, "color", item.color);
686 }
687 ClearRectangle => {
688 str_node(&mut v, "type", "clear-rect");;
689 }
690 Line(item) => {
691 str_node(&mut v, "type", "line");
692 if let LineStyle::Wavy = item.style {
693 f32_node(&mut v, "thickness", item.wavy_line_thickness);
694 }
695 str_node(&mut v, "orientation", item.orientation.as_str());
696 color_node(&mut v, "color", item.color);
697 str_node(&mut v, "style", item.style.as_str());
698 }
699 Text(item) => {
700 let gi = display_list.get(base.glyphs());
701 let mut indices: Vec<u32> = vec![];
702 let mut offsets: Vec<f32> = vec![];
703 for g in gi {
704 indices.push(g.index);
705 offsets.push(g.point.x);
706 offsets.push(g.point.y);
707 }
708 u32_vec_node(&mut v, "glyphs", &indices);
709 f32_vec_node(&mut v, "offsets", &offsets);
710
711 let instance = self.font_instances.entry(item.font_key).or_insert_with(|| {
712 println!("Warning: font instance key not found in font instances table!");
713 CachedFontInstance {
714 font_key: FontKey::new(IdNamespace(0), 0),
715 glyph_size: Au::from_px(16),
716 }
717 });
718
719 f32_node(
720 &mut v,
721 "size",
722 instance.glyph_size.to_f32_px() * 12.0 / 16.0,
723 );
724 color_node(&mut v, "color", item.color);
725
726 let entry = self.fonts.entry(instance.font_key).or_insert_with(|| {
727 println!("Warning: font key not found in fonts table!");
728 CachedFont::Raw(Some(vec![]), 0, None)
729 });
730
731 match entry {
732 &mut CachedFont::Native(ref handle, ref mut path_opt) => {
733 native_font_handle_to_yaml(&mut self.rsrc_gen, handle, &mut v, path_opt);
734 }
735 &mut CachedFont::Raw(ref mut bytes_opt, index, ref mut path_opt) => {
736 if let Some(bytes) = bytes_opt.take() {
737 let (path_file, path) = self.rsrc_gen.next_rsrc_paths(
738 "font",
739 "ttf",
740 );
741 let mut file = fs::File::create(&path_file).unwrap();
742 file.write_all(&bytes).unwrap();
743 *path_opt = Some(path);
744 }
745
746 path_node(&mut v, "font", path_opt.as_ref().unwrap());
747 if index != 0 {
748 u32_node(&mut v, "font-index", index);
749 }
750 }
751 }
752 }
753 Image(item) => {
754 if let Some(path) = self.path_for_image(item.image_key) {
755 path_node(&mut v, "image", &path);
756 }
757 if let Some(&CachedImage {
758 tiling: Some(tile_size),
759 ..
760 }) = self.images.get(&item.image_key)
761 {
762 u32_node(&mut v, "tile-size", tile_size as u32);
763 }
764 size_node(&mut v, "stretch-size", &item.stretch_size);
765 size_node(&mut v, "tile-spacing", &item.tile_spacing);
766 match item.image_rendering {
767 ImageRendering::Auto => (),
768 ImageRendering::CrispEdges => str_node(&mut v, "rendering", "crisp-edges"),
769 ImageRendering::Pixelated => str_node(&mut v, "rendering", "pixelated"),
770 };
771 match item.alpha_type {
772 AlphaType::PremultipliedAlpha => str_node(&mut v, "alpha-type", "premultiplied-alpha"),
773 AlphaType::Alpha => str_node(&mut v, "alpha-type", "alpha"),
774 };
775 }
776 YuvImage(_) => {
777 str_node(&mut v, "type", "yuv-image");
778 // TODO
779 println!("TODO YAML YuvImage");
780 }
781 Border(item) => {
782 str_node(&mut v, "type", "border");
783 match item.details {
784 BorderDetails::Normal(ref details) => {
785 let trbl =
786 vec![&details.top, &details.right, &details.bottom, &details.left];
787 let widths: Vec<f32> = vec![
788 item.widths.top,
789 item.widths.right,
790 item.widths.bottom,
791 item.widths.left,
792 ];
793 let colors: Vec<String> =
794 trbl.iter().map(|x| color_to_string(x.color)).collect();
795 let styles: Vec<String> = trbl.iter()
796 .map(|x| {
797 match x.style {
798 BorderStyle::None => "none",
799 BorderStyle::Solid => "solid",
800 BorderStyle::Double => "double",
801 BorderStyle::Dotted => "dotted",
802 BorderStyle::Dashed => "dashed",
803 BorderStyle::Hidden => "hidden",
804 BorderStyle::Ridge => "ridge",
805 BorderStyle::Inset => "inset",
806 BorderStyle::Outset => "outset",
807 BorderStyle::Groove => "groove",
808 }.to_owned()
809 })
810 .collect();
811 yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
812 str_node(&mut v, "border-type", "normal");
813 yaml_node(&mut v, "color", string_vec_yaml(&colors, true));
814 yaml_node(&mut v, "style", string_vec_yaml(&styles, true));
815 if let Some(radius_node) = maybe_radius_yaml(&details.radius) {
816 yaml_node(&mut v, "radius", radius_node);
817 }
818 }
819 BorderDetails::Image(ref details) => {
820 let widths: Vec<f32> = vec![
821 item.widths.top,
822 item.widths.right,
823 item.widths.bottom,
824 item.widths.left,
825 ];
826 let outset: Vec<f32> = vec![
827 details.outset.top,
828 details.outset.right,
829 details.outset.bottom,
830 details.outset.left,
831 ];
832 yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
833 str_node(&mut v, "border-type", "image");
834 if let Some(path) = self.path_for_image(details.image_key) {
835 path_node(&mut v, "image", &path);
836 }
837 u32_node(&mut v, "image-width", details.patch.width);
838 u32_node(&mut v, "image-height", details.patch.height);
839 let slice: Vec<u32> = vec![
840 details.patch.slice.top,
841 details.patch.slice.right,
842 details.patch.slice.bottom,
843 details.patch.slice.left,
844 ];
845 yaml_node(&mut v, "slice", u32_vec_yaml(&slice, true));
846 yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
847 match details.repeat_horizontal {
848 RepeatMode::Stretch => {
849 str_node(&mut v, "repeat-horizontal", "stretch")
850 }
851 RepeatMode::Repeat => {
852 str_node(&mut v, "repeat-horizontal", "repeat")
853 }
854 RepeatMode::Round => str_node(&mut v, "repeat-horizontal", "round"),
855 RepeatMode::Space => str_node(&mut v, "repeat-horizontal", "space"),
856 };
857 match details.repeat_vertical {
858 RepeatMode::Stretch => {
859 str_node(&mut v, "repeat-vertical", "stretch")
860 }
861 RepeatMode::Repeat => str_node(&mut v, "repeat-vertical", "repeat"),
862 RepeatMode::Round => str_node(&mut v, "repeat-vertical", "round"),
863 RepeatMode::Space => str_node(&mut v, "repeat-vertical", "space"),
864 };
865 }
866 BorderDetails::Gradient(ref details) => {
867 let widths: Vec<f32> = vec![
868 item.widths.top,
869 item.widths.right,
870 item.widths.bottom,
871 item.widths.left,
872 ];
873 let outset: Vec<f32> = vec![
874 details.outset.top,
875 details.outset.right,
876 details.outset.bottom,
877 details.outset.left,
878 ];
879 yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
880 str_node(&mut v, "border-type", "gradient");
881 point_node(&mut v, "start", &details.gradient.start_point);
882 point_node(&mut v, "end", &details.gradient.end_point);
883 let mut stops = vec![];
884 for stop in display_list.get(base.gradient_stops()) {
885 stops.push(Yaml::Real(stop.offset.to_string()));
886 stops.push(Yaml::String(color_to_string(stop.color)));
887 }
888 yaml_node(&mut v, "stops", Yaml::Array(stops));
889 bool_node(
890 &mut v,
891 "repeat",
892 details.gradient.extend_mode == ExtendMode::Repeat,
893 );
894 yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
895 }
896 BorderDetails::RadialGradient(ref details) => {
897 let widths: Vec<f32> = vec![
898 item.widths.top,
899 item.widths.right,
900 item.widths.bottom,
901 item.widths.left,
902 ];
903 let outset: Vec<f32> = vec![
904 details.outset.top,
905 details.outset.right,
906 details.outset.bottom,
907 details.outset.left,
908 ];
909 yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
910 str_node(&mut v, "border-type", "radial-gradient");
911 point_node(&mut v, "start-center", &details.gradient.start_center);
912 f32_node(&mut v, "start-radius", details.gradient.start_radius);
913 point_node(&mut v, "end-center", &details.gradient.end_center);
914 f32_node(&mut v, "end-radius", details.gradient.end_radius);
915 f32_node(&mut v, "ratio-xy", details.gradient.ratio_xy);
916 let mut stops = vec![];
917 for stop in display_list.get(base.gradient_stops()) {
918 stops.push(Yaml::Real(stop.offset.to_string()));
919 stops.push(Yaml::String(color_to_string(stop.color)));
920 }
921 yaml_node(&mut v, "stops", Yaml::Array(stops));
922 bool_node(
923 &mut v,
924 "repeat",
925 details.gradient.extend_mode == ExtendMode::Repeat,
926 );
927 yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
928 }
929 }
930 }
931 BoxShadow(item) => {
932 str_node(&mut v, "type", "box-shadow");
933 rect_node(&mut v, "box-bounds", &item.box_bounds);
934 vector_node(&mut v, "offset", &item.offset);
935 color_node(&mut v, "color", item.color);
936 f32_node(&mut v, "blur-radius", item.blur_radius);
937 f32_node(&mut v, "spread-radius", item.spread_radius);
938 if let Some(radius_node) = maybe_radius_yaml(&item.border_radius) {
939 yaml_node(&mut v, "border-radius", radius_node);
940 }
941 let clip_mode = match item.clip_mode {
942 BoxShadowClipMode::Outset => "outset",
943 BoxShadowClipMode::Inset => "inset",
944 };
945 str_node(&mut v, "clip-mode", clip_mode);
946 }
947 Gradient(item) => {
948 str_node(&mut v, "type", "gradient");
949 point_node(&mut v, "start", &item.gradient.start_point);
950 point_node(&mut v, "end", &item.gradient.end_point);
951 size_node(&mut v, "tile-size", &item.tile_size);
952 size_node(&mut v, "tile-spacing", &item.tile_spacing);
953 let mut stops = vec![];
954 for stop in display_list.get(base.gradient_stops()) {
955 stops.push(Yaml::Real(stop.offset.to_string()));
956 stops.push(Yaml::String(color_to_string(stop.color)));
957 }
958 yaml_node(&mut v, "stops", Yaml::Array(stops));
959 bool_node(
960 &mut v,
961 "repeat",
962 item.gradient.extend_mode == ExtendMode::Repeat,
963 );
964 }
965 RadialGradient(item) => {
966 str_node(&mut v, "type", "radial-gradient");
967 point_node(&mut v, "start-center", &item.gradient.start_center);
968 f32_node(&mut v, "start-radius", item.gradient.start_radius);
969 point_node(&mut v, "end-center", &item.gradient.end_center);
970 f32_node(&mut v, "end-radius", item.gradient.end_radius);
971 f32_node(&mut v, "ratio-xy", item.gradient.ratio_xy);
972 size_node(&mut v, "tile-size", &item.tile_size);
973 size_node(&mut v, "tile-spacing", &item.tile_spacing);
974 let mut stops = vec![];
975 for stop in display_list.get(base.gradient_stops()) {
976 stops.push(Yaml::Real(stop.offset.to_string()));
977 stops.push(Yaml::String(color_to_string(stop.color)));
978 }
979 yaml_node(&mut v, "stops", Yaml::Array(stops));
980 bool_node(
981 &mut v,
982 "repeat",
983 item.gradient.extend_mode == ExtendMode::Repeat,
984 );
985 }
986 Iframe(item) => {
987 str_node(&mut v, "type", "iframe");
988 u32_vec_node(&mut v, "id", &[item.pipeline_id.0, item.pipeline_id.1]);
989 }
990 PushStackingContext(item) => {
991 str_node(&mut v, "type", "stacking-context");
992 let filters = display_list.get(base.filters());
993 write_sc(&mut v, &item.stacking_context, &scene.properties, filters);
994
995 let mut sub_iter = base.sub_iter();
996 self.write_display_list(&mut v, display_list, scene, &mut sub_iter, clip_id_mapper);
997 continue_traversal = Some(sub_iter);
998 }
999 Clip(item) => {
1000 str_node(&mut v, "type", "clip");
1001 usize_node(&mut v, "id", clip_id_mapper.add_id(item.id));
1002 size_node(&mut v, "content-size", &base.rect().size);
1003
1004 let (complex_clips, complex_clip_count) = base.complex_clip();
1005 if let Some(complex) = self.make_complex_clips_node(
1006 complex_clip_count,
1007 complex_clips,
1008 display_list,
1009 ) {
1010 yaml_node(&mut v, "complex", complex);
1011 }
1012
1013 if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
1014 yaml_node(&mut v, "image-mask", mask_yaml);
1015 }
1016 }
1017 ClipChain(item) => {
1018 str_node(&mut v, "type", "clip-chain");
1019
1020 let id = ClipId::ClipChain(item.id);
1021 u32_node(&mut v, "id", clip_id_mapper.add_id(id) as u32);
1022
1023 let clip_ids: Vec<u32> = display_list.get(base.clip_chain_items()).map(|clip_id| {
1024 clip_id_mapper.map_id(&clip_id) as u32
1025 }).collect();
1026 u32_vec_node(&mut v, "clips", &clip_ids);
1027
1028 if let Some(parent) = item.parent {
1029 let parent = ClipId::ClipChain(parent);
1030 u32_node(&mut v, "parent", clip_id_mapper.map_id(&parent) as u32);
1031 }
1032 }
1033 ScrollFrame(item) => {
1034 str_node(&mut v, "type", "scroll-frame");
1035 usize_node(&mut v, "id", clip_id_mapper.add_id(item.scroll_frame_id));
1036 size_node(&mut v, "content-size", &base.rect().size);
1037 rect_node(&mut v, "bounds", &base.local_clip().clip_rect());
1038
1039 let (complex_clips, complex_clip_count) = base.complex_clip();
1040 if let Some(complex) = self.make_complex_clips_node(
1041 complex_clip_count,
1042 complex_clips,
1043 display_list,
1044 ) {
1045 yaml_node(&mut v, "complex", complex);
1046 }
1047
1048 if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
1049 yaml_node(&mut v, "image-mask", mask_yaml);
1050 }
1051 }
1052 StickyFrame(item) => {
1053 str_node(&mut v, "type", "sticky-frame");
1054 usize_node(&mut v, "id", clip_id_mapper.add_id(item.id));
1055 rect_node(&mut v, "bounds", &base.local_clip().clip_rect());
1056
1057 if let Some(margin) = item.margins.top {
1058 f32_node(&mut v, "margin-top", margin);
1059 }
1060 if let Some(margin) = item.margins.bottom {
1061 f32_node(&mut v, "margin-bottom", margin);
1062 }
1063 if let Some(margin) = item.margins.left {
1064 f32_node(&mut v, "margin-left", margin);
1065 }
1066 if let Some(margin) = item.margins.right {
1067 f32_node(&mut v, "margin-right", margin);
1068 }
1069
1070 let horizontal = vec![
1071 Yaml::Real(item.horizontal_offset_bounds.min.to_string()),
1072 Yaml::Real(item.horizontal_offset_bounds.max.to_string()),
1073 ];
1074 let vertical = vec![
1075 Yaml::Real(item.vertical_offset_bounds.min.to_string()),
1076 Yaml::Real(item.vertical_offset_bounds.max.to_string()),
1077 ];
1078
1079 yaml_node(&mut v, "horizontal-offset-bounds", Yaml::Array(horizontal));
1080 yaml_node(&mut v, "vertical-offset-bounds", Yaml::Array(vertical));
1081
1082 let applied = vec![
1083 Yaml::Real(item.previously_applied_offset.x.to_string()),
1084 Yaml::Real(item.previously_applied_offset.y.to_string()),
1085 ];
1086 yaml_node(&mut v, "previously-applied-offset", Yaml::Array(applied));
1087 }
1088
1089 PopStackingContext => return,
1090 SetGradientStops => panic!("dummy item yielded?"),
1091 PushShadow(shadow) => {
1092 str_node(&mut v, "type", "shadow");
1093 vector_node(&mut v, "offset", &shadow.offset);
1094 color_node(&mut v, "color", shadow.color);
1095 f32_node(&mut v, "blur-radius", shadow.blur_radius);
1096 }
1097 PopAllShadows => {
1098 str_node(&mut v, "type", "pop-all-shadows");
1099 }
1100 }
1101 if !v.is_empty() {
1102 list.push(Yaml::Hash(v));
1103 }
1104 }
1105 }
1106
1107 fn write_display_list(
1108 &mut self,
1109 parent: &mut Table,
1110 display_list: &BuiltDisplayList,
1111 scene: &Scene,
1112 list_iterator: &mut BuiltDisplayListIter,
1113 clip_id_mapper: &mut ClipIdMapper,
1114 ) {
1115 let mut list = vec![];
1116 self.write_display_list_items(&mut list, display_list, scene, list_iterator, clip_id_mapper);
1117 parent.insert(Yaml::String("items".to_owned()), Yaml::Array(list));
1118 }
1119 }
1120
1121 impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver {
1122 fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
1123 match *msg {
1124 ApiMsg::UpdateResources(ref updates) => {
1125 self.frame_writer.update_resources(updates);
1126 }
1127 ApiMsg::UpdateDocument(_, ref txn) => {
1128 self.frame_writer.update_resources(&txn.resource_updates);
1129 for doc_msg in &txn.scene_ops {
1130 match *doc_msg {
1131 SceneMsg::SetDisplayList {
1132 ref epoch,
1133 ref pipeline_id,
1134 ref background,
1135 ref viewport_size,
1136 ref list_descriptor,
1137 ..
1138 } => {
1139 self.frame_writer.begin_write_display_list(
1140 &mut self.scene,
1141 epoch,
1142 pipeline_id,
1143 background,
1144 viewport_size,
1145 list_descriptor,
1146 );
1147 }
1148 SceneMsg::SetRootPipeline(ref pipeline_id) => {
1149 self.scene.set_root_pipeline_id(pipeline_id.clone());
1150 }
1151 SceneMsg::RemovePipeline(ref pipeline_id) => {
1152 self.scene.remove_pipeline(pipeline_id);
1153 }
1154 _ => {}
1155 }
1156 }
1157 for doc_msg in &txn.frame_ops {
1158 match *doc_msg {
1159 FrameMsg::UpdateDynamicProperties(ref properties) => {
1160 self.scene.properties.set_properties(properties);
1161 }
1162 _ => {}
1163 }
1164 }
1165 }
1166 _ => {}
1167 }
1168 }
1169
1170 fn write_payload(&mut self, _frame: u32, data: &[u8]) {
1171 if self.frame_writer.dl_descriptor.is_some() {
1172 self.frame_writer
1173 .finish_write_display_list(&mut self.scene, data);
1174 }
1175 }
1176 }
1177
1178 /// This structure allows mapping both `Clip` and `ClipExternalId`
1179 /// `ClipIds` onto one set of numeric ids. This prevents ids
1180 /// from clashing in the yaml output.
1181 struct ClipIdMapper {
1182 hash_map: HashMap<ClipId, usize>,
1183 current_clip_id: usize,
1184 }
1185
1186 impl ClipIdMapper {
1187 fn new() -> ClipIdMapper {
1188 ClipIdMapper {
1189 hash_map: HashMap::new(),
1190 current_clip_id: 1,
1191 }
1192 }
1193
1194 fn add_id(&mut self, id: ClipId) -> usize {
1195 self.hash_map.insert(id, self.current_clip_id);
1196 self.current_clip_id += 1;
1197 self.current_clip_id - 1
1198 }
1199
1200 fn map_id(&self, id: &ClipId) -> usize {
1201 if id.is_root_scroll_node() {
1202 return 0;
1203 }
1204 *self.hash_map.get(id).unwrap()
1205 }
1206
1207 fn map_info(&self, info: &ClipAndScrollInfo) -> (i64, Option<i64>) {
1208 (
1209 self.map_id(&info.scroll_node_id) as i64,
1210 info.clip_node_id.map(|ref id| self.map_id(id) as i64),
1211 )
1212 }
1213 }
1214