yaml_helper.rs (50328B)
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 use euclid::{Angle, Size2D}; 6 use crate::parse_function::parse_function; 7 use std::f32; 8 use std::str::FromStr; 9 use webrender::api::*; 10 use webrender::api::units::*; 11 use yaml_rust::{Yaml, YamlLoader}; 12 use log::Level; 13 14 pub trait YamlHelper { 15 fn as_f32(&self) -> Option<f32>; 16 fn as_force_f32(&self) -> Option<f32>; 17 fn as_vec_f32(&self) -> Option<Vec<f32>>; 18 fn as_vec_u32(&self) -> Option<Vec<u32>>; 19 fn as_vec_u64(&self) -> Option<Vec<u64>>; 20 fn as_pipeline_id(&self) -> Option<PipelineId>; 21 fn as_rect(&self) -> Option<LayoutRect>; 22 fn as_size(&self) -> Option<LayoutSize>; 23 fn as_point(&self) -> Option<LayoutPoint>; 24 fn as_vector(&self) -> Option<LayoutVector2D>; 25 fn as_matrix4d(&self) -> Option<LayoutTransform>; 26 fn as_transform(&self, transform_origin: &LayoutPoint) -> Option<LayoutTransform>; 27 fn as_colorf(&self) -> Option<ColorF>; 28 fn as_vec_colorf(&self) -> Option<Vec<ColorF>>; 29 fn as_pt_to_f32(&self) -> Option<f32>; 30 fn as_vec_string(&self) -> Option<Vec<String>>; 31 fn as_border_radius_component(&self) -> LayoutSize; 32 fn as_border_radius(&self) -> Option<BorderRadius>; 33 fn as_transform_style(&self) -> Option<TransformStyle>; 34 fn as_raster_space(&self) -> Option<RasterSpace>; 35 fn as_clip_mode(&self) -> Option<ClipMode>; 36 fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference>; 37 fn as_mix_blend_mode(&self) -> Option<MixBlendMode>; 38 fn as_filter_op(&self) -> Option<FilterOp>; 39 fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>>; 40 fn as_filter_data(&self) -> Option<FilterData>; 41 fn as_vec_filter_data(&self) -> Option<Vec<FilterData>>; 42 fn as_complex_clip_region(&self) -> ComplexClipRegion; 43 fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds; 44 fn as_gradient(&self, dl: &mut DisplayListBuilder) -> Gradient; 45 fn as_radial_gradient(&self, dl: &mut DisplayListBuilder) -> RadialGradient; 46 fn as_conic_gradient(&self, dl: &mut DisplayListBuilder) -> ConicGradient; 47 fn as_complex_clip_regions(&self) -> Vec<ComplexClipRegion>; 48 fn as_rotation(&self) -> Option<Rotation>; 49 } 50 51 fn string_to_color(color: &str) -> Option<ColorF> { 52 match color { 53 "red" => Some(ColorF::new(1.0, 0.0, 0.0, 1.0)), 54 "green" => Some(ColorF::new(0.0, 1.0, 0.0, 1.0)), 55 "blue" => Some(ColorF::new(0.0, 0.0, 1.0, 1.0)), 56 "white" => Some(ColorF::new(1.0, 1.0, 1.0, 1.0)), 57 "black" => Some(ColorF::new(0.0, 0.0, 0.0, 1.0)), 58 "yellow" => Some(ColorF::new(1.0, 1.0, 0.0, 1.0)), 59 "cyan" => Some(ColorF::new(0.0, 1.0, 1.0, 1.0)), 60 "magenta" => Some(ColorF::new(1.0, 0.0, 1.0, 1.0)), 61 "transparent" => Some(ColorF::new(1.0, 1.0, 1.0, 0.0)), 62 s => { 63 let items: Vec<f32> = s.split_whitespace() 64 .map(|s| f32::from_str(s).unwrap()) 65 .collect(); 66 if items.len() == 3 { 67 Some(ColorF::new( 68 items[0] / 255.0, 69 items[1] / 255.0, 70 items[2] / 255.0, 71 1.0, 72 )) 73 } else if items.len() == 4 { 74 Some(ColorF::new( 75 items[0] / 255.0, 76 items[1] / 255.0, 77 items[2] / 255.0, 78 items[3], 79 )) 80 } else { 81 None 82 } 83 } 84 } 85 } 86 87 pub trait StringEnum: Sized { 88 fn from_str(_: &str) -> Option<Self>; 89 } 90 91 macro_rules! define_string_enum { 92 ($T:ident, [ $( $y:ident = $x:expr ),* ]) => { 93 impl StringEnum for $T { 94 fn from_str(text: &str) -> Option<$T> { 95 match text { 96 $( $x => Some($T::$y), )* 97 _ => { 98 println!("Unrecognized {} value '{}'", stringify!($T), text); 99 None 100 } 101 } 102 } 103 } 104 } 105 } 106 107 define_string_enum!(TransformStyle, [Flat = "flat", Preserve3D = "preserve-3d"]); 108 109 define_string_enum!( 110 MixBlendMode, 111 [ 112 Normal = "normal", 113 Multiply = "multiply", 114 Screen = "screen", 115 Overlay = "overlay", 116 Darken = "darken", 117 Lighten = "lighten", 118 ColorDodge = "color-dodge", 119 ColorBurn = "color-burn", 120 HardLight = "hard-light", 121 SoftLight = "soft-light", 122 Difference = "difference", 123 Exclusion = "exclusion", 124 Hue = "hue", 125 Saturation = "saturation", 126 Color = "color", 127 Luminosity = "luminosity", 128 PlusLighter = "plus-lighter" 129 ] 130 ); 131 132 define_string_enum!( 133 LineOrientation, 134 [Horizontal = "horizontal", Vertical = "vertical"] 135 ); 136 137 define_string_enum!( 138 LineStyle, 139 [ 140 Solid = "solid", 141 Dotted = "dotted", 142 Dashed = "dashed", 143 Wavy = "wavy" 144 ] 145 ); 146 147 define_string_enum!(ClipMode, [Clip = "clip", ClipOut = "clip-out"]); 148 149 define_string_enum!( 150 ComponentTransferFuncType, 151 [ 152 Identity = "Identity", 153 Table = "Table", 154 Discrete = "Discrete", 155 Linear = "Linear", 156 Gamma = "Gamma" 157 ] 158 ); 159 160 define_string_enum!( 161 ColorSpace, 162 [ 163 Srgb = "srgb", 164 LinearRgb = "linear-rgb" 165 ] 166 ); 167 168 // Rotate around `axis` by `degrees` angle 169 fn make_rotation( 170 origin: &LayoutPoint, 171 degrees: f32, 172 axis_x: f32, 173 axis_y: f32, 174 axis_z: f32, 175 ) -> LayoutTransform { 176 let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0); 177 let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0); 178 179 let theta = 2.0f32 * f32::consts::PI - degrees.to_radians(); 180 let transform = 181 LayoutTransform::identity().pre_rotate(axis_x, axis_y, axis_z, Angle::radians(theta)); 182 183 pre_transform.then(&transform).then(&post_transform) 184 } 185 186 pub fn make_perspective( 187 origin: LayoutPoint, 188 perspective: f32, 189 ) -> LayoutTransform { 190 let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0); 191 let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0); 192 let transform = LayoutTransform::perspective(perspective); 193 pre_transform.then(&transform).then(&post_transform) 194 } 195 196 // Create a skew matrix, specified in degrees. 197 fn make_skew( 198 skew_x: f32, 199 skew_y: f32, 200 ) -> LayoutTransform { 201 let alpha = Angle::radians(skew_x.to_radians()); 202 let beta = Angle::radians(skew_y.to_radians()); 203 LayoutTransform::skew(alpha, beta) 204 } 205 206 impl YamlHelper for Yaml { 207 fn as_f32(&self) -> Option<f32> { 208 match *self { 209 Yaml::Integer(iv) => Some(iv as f32), 210 Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(), 211 _ => None, 212 } 213 } 214 215 fn as_force_f32(&self) -> Option<f32> { 216 match *self { 217 Yaml::Integer(iv) => Some(iv as f32), 218 Yaml::String(ref sv) | Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(), 219 _ => None, 220 } 221 } 222 223 fn as_vec_f32(&self) -> Option<Vec<f32>> { 224 match *self { 225 Yaml::String(ref s) | Yaml::Real(ref s) => s.split_whitespace() 226 .map(f32::from_str) 227 .collect::<Result<Vec<_>, _>>() 228 .ok(), 229 Yaml::Array(ref v) => v.iter() 230 .map(|v| match *v { 231 Yaml::Integer(k) => Ok(k as f32), 232 Yaml::String(ref k) | Yaml::Real(ref k) => f32::from_str(k).map_err(|_| false), 233 _ => Err(false), 234 }) 235 .collect::<Result<Vec<_>, _>>() 236 .ok(), 237 Yaml::Integer(k) => Some(vec![k as f32]), 238 _ => None, 239 } 240 } 241 242 fn as_vec_u32(&self) -> Option<Vec<u32>> { 243 self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u32).collect()) 244 } 245 246 fn as_vec_u64(&self) -> Option<Vec<u64>> { 247 self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u64).collect()) 248 } 249 250 fn as_pipeline_id(&self) -> Option<PipelineId> { 251 if let Some(v) = self.as_vec() { 252 let a = v.get(0).and_then(|v| v.as_i64()).map(|v| v as u32); 253 let b = v.get(1).and_then(|v| v.as_i64()).map(|v| v as u32); 254 match (a, b) { 255 (Some(a), Some(b)) if v.len() == 2 => Some(PipelineId(a, b)), 256 _ => None, 257 } 258 } else { 259 None 260 } 261 } 262 263 fn as_pt_to_f32(&self) -> Option<f32> { 264 self.as_force_f32().map(|fv| fv * 16. / 12.) 265 } 266 267 fn as_rect(&self) -> Option<LayoutRect> { 268 self.as_vec_f32().and_then(|v| match v.as_slice() { 269 &[x, y, width, height] => Some(LayoutRect::from_origin_and_size( 270 LayoutPoint::new(x, y), 271 LayoutSize::new(width, height), 272 )), 273 _ => None, 274 }) 275 } 276 277 fn as_size(&self) -> Option<LayoutSize> { 278 if self.is_badvalue() { 279 return None; 280 } 281 282 if let Some(nums) = self.as_vec_f32() { 283 if nums.len() == 2 { 284 return Some(LayoutSize::new(nums[0], nums[1])); 285 } 286 } 287 288 None 289 } 290 291 fn as_point(&self) -> Option<LayoutPoint> { 292 if self.is_badvalue() { 293 return None; 294 } 295 296 if let Some(nums) = self.as_vec_f32() { 297 if nums.len() == 2 { 298 return Some(LayoutPoint::new(nums[0], nums[1])); 299 } 300 } 301 302 None 303 } 304 305 fn as_vector(&self) -> Option<LayoutVector2D> { 306 self.as_point().map(|p| p.to_vector()) 307 } 308 309 fn as_matrix4d(&self) -> Option<LayoutTransform> { 310 if let Some(nums) = self.as_vec_f32() { 311 assert_eq!(nums.len(), 16, "expected 16 floats, got '{:?}'", self); 312 Some(LayoutTransform::new( 313 nums[0], nums[1], nums[2], nums[3], 314 nums[4], nums[5], nums[6], nums[7], 315 nums[8], nums[9], nums[10], nums[11], 316 nums[12], nums[13], nums[14], nums[15], 317 )) 318 } else { 319 None 320 } 321 } 322 323 fn as_transform(&self, transform_origin: &LayoutPoint) -> Option<LayoutTransform> { 324 if let Some(transform) = self.as_matrix4d() { 325 return Some(transform); 326 } 327 328 match *self { 329 Yaml::String(ref string) => { 330 let mut slice = string.as_str(); 331 let mut transform = LayoutTransform::identity(); 332 while !slice.is_empty() { 333 let (function, ref args, reminder) = parse_function(slice); 334 slice = reminder; 335 let mx = match function { 336 "identity" => { 337 LayoutTransform::identity() 338 } 339 "translate" if args.len() >= 2 => { 340 let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(0.); 341 LayoutTransform::translation( 342 args[0].parse().unwrap(), 343 args[1].parse().unwrap(), 344 z, 345 ) 346 } 347 "rotate" | "rotate-z" if args.len() == 1 => { 348 make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 0.0, 1.0) 349 } 350 "rotate-x" if args.len() == 1 => { 351 make_rotation(transform_origin, args[0].parse().unwrap(), 1.0, 0.0, 0.0) 352 } 353 "rotate-y" if args.len() == 1 => { 354 make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 1.0, 0.0) 355 } 356 "scale" if !args.is_empty() => { 357 let x = args[0].parse().unwrap(); 358 // Default to uniform X/Y scale if Y unspecified. 359 let y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(x); 360 // Default to no Z scale if unspecified. 361 let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(1.0); 362 LayoutTransform::scale(x, y, z) 363 } 364 "scale-x" if args.len() == 1 => { 365 LayoutTransform::scale(args[0].parse().unwrap(), 1.0, 1.0) 366 } 367 "scale-y" if args.len() == 1 => { 368 LayoutTransform::scale(1.0, args[0].parse().unwrap(), 1.0) 369 } 370 "scale-z" if args.len() == 1 => { 371 LayoutTransform::scale(1.0, 1.0, args[0].parse().unwrap()) 372 } 373 "skew" if !args.is_empty() => { 374 // Default to no Y skew if unspecified. 375 let skew_y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(0.0); 376 make_skew(args[0].parse().unwrap(), skew_y) 377 } 378 "skew-x" if args.len() == 1 => { 379 make_skew(args[0].parse().unwrap(), 0.0) 380 } 381 "skew-y" if args.len() == 1 => { 382 make_skew(0.0, args[0].parse().unwrap()) 383 } 384 "perspective" if args.len() == 1 => { 385 LayoutTransform::perspective(args[0].parse().unwrap()) 386 } 387 _ => { 388 println!("unknown function {}", function); 389 break; 390 } 391 }; 392 transform = transform.then(&mx); 393 } 394 Some(transform) 395 } 396 Yaml::Array(ref array) => { 397 let transform = array.iter().fold( 398 LayoutTransform::identity(), 399 |u, yaml| if let Some(transform) = yaml.as_transform(transform_origin) { 400 transform.then(&u) 401 } else { 402 u 403 }, 404 ); 405 Some(transform) 406 } 407 Yaml::BadValue => None, 408 _ => { 409 println!("unknown transform {:?}", self); 410 None 411 } 412 } 413 } 414 415 /// Inputs for r, g, b channels are floats or ints in the range [0, 255]. 416 /// If included, the alpha channel is in the range [0, 1]. 417 /// This matches CSS-style, but requires conversion for `ColorF`. 418 fn as_colorf(&self) -> Option<ColorF> { 419 if let Some(nums) = self.as_vec_f32() { 420 assert!(nums.iter().take(3).all(|x| (0.0 ..= 255.0).contains(x)), 421 "r, g, b values should be in the 0-255 range, got {:?}", nums); 422 423 let color: ColorF = match *nums.as_slice() { 424 [r, g, b] => ColorF { r, g, b, a: 1.0 }, 425 [r, g, b, a] => ColorF { r, g, b, a }, 426 _ => panic!("color expected a color name, or 3-4 floats; got '{:?}'", self), 427 }.scale_rgb(1.0 / 255.0); 428 429 assert!((0.0 ..= 1.0).contains(&color.a), 430 "alpha value should be in the 0-1 range, got {:?}", 431 color.a); 432 433 Some(color) 434 } else if let Some(s) = self.as_str() { 435 string_to_color(s) 436 } else { 437 None 438 } 439 } 440 441 fn as_vec_colorf(&self) -> Option<Vec<ColorF>> { 442 if let Some(v) = self.as_vec() { 443 Some(v.iter().map(|v| v.as_colorf().unwrap()).collect()) 444 } else { self.as_colorf().map(|color| vec![color]) } 445 } 446 447 fn as_vec_string(&self) -> Option<Vec<String>> { 448 if let Some(v) = self.as_vec() { 449 Some(v.iter().map(|v| v.as_str().unwrap().to_owned()).collect()) 450 } else { self.as_str().map(|s| vec![s.to_owned()]) } 451 } 452 453 fn as_border_radius_component(&self) -> LayoutSize { 454 if let Yaml::Integer(integer) = *self { 455 return LayoutSize::new(integer as f32, integer as f32); 456 } 457 self.as_size().unwrap_or_else(Size2D::zero) 458 } 459 460 fn as_border_radius(&self) -> Option<BorderRadius> { 461 if let Some(size) = self.as_size() { 462 return Some(BorderRadius::uniform_size(size)); 463 } 464 465 match *self { 466 Yaml::BadValue => None, 467 Yaml::String(ref s) | Yaml::Real(ref s) => { 468 let fv = f32::from_str(s).unwrap(); 469 Some(BorderRadius::uniform(fv)) 470 } 471 Yaml::Integer(v) => Some(BorderRadius::uniform(v as f32)), 472 Yaml::Array(ref array) if array.len() == 4 => { 473 let top_left = array[0].as_border_radius_component(); 474 let top_right = array[1].as_border_radius_component(); 475 let bottom_left = array[2].as_border_radius_component(); 476 let bottom_right = array[3].as_border_radius_component(); 477 Some(BorderRadius { 478 top_left, 479 top_right, 480 bottom_left, 481 bottom_right, 482 }) 483 } 484 Yaml::Hash(_) => { 485 let top_left = self["top-left"].as_border_radius_component(); 486 let top_right = self["top-right"].as_border_radius_component(); 487 let bottom_left = self["bottom-left"].as_border_radius_component(); 488 let bottom_right = self["bottom-right"].as_border_radius_component(); 489 Some(BorderRadius { 490 top_left, 491 top_right, 492 bottom_left, 493 bottom_right, 494 }) 495 } 496 _ => { 497 panic!("Invalid border radius specified: {:?}", self); 498 } 499 } 500 } 501 502 fn as_transform_style(&self) -> Option<TransformStyle> { 503 self.as_str().and_then(StringEnum::from_str) 504 } 505 506 fn as_raster_space(&self) -> Option<RasterSpace> { 507 self.as_str().map(|s| { 508 match parse_function(s) { 509 ("screen", _, _) => { 510 RasterSpace::Screen 511 } 512 ("local", ref args, _) if args.len() == 1 => { 513 RasterSpace::Local(args[0].parse().unwrap()) 514 } 515 f => { 516 panic!("error parsing raster space {:?}", f); 517 } 518 } 519 }) 520 } 521 522 fn as_mix_blend_mode(&self) -> Option<MixBlendMode> { 523 self.as_str().and_then(StringEnum::from_str) 524 } 525 526 fn as_clip_mode(&self) -> Option<ClipMode> { 527 self.as_str().and_then(StringEnum::from_str) 528 } 529 530 fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference> { 531 match self.as_i64() { 532 Some(n) => Some(FilterOpGraphPictureReference{ 533 buffer_id: FilterOpGraphPictureBufferId::BufferId(n as i16), 534 }), 535 None => None, 536 } 537 } 538 539 fn as_filter_op(&self) -> Option<FilterOp> { 540 if let Some(filter_op) = self["svgfe"].as_str() { 541 let subregion = self["subregion"].as_rect().unwrap_or( 542 LayoutRect::new( 543 LayoutPoint::new(0.0, 0.0), 544 LayoutPoint::new(1024.0, 1024.0), 545 )); 546 547 let node = FilterOpGraphNode { 548 linear: self["linear"].as_bool().unwrap_or(true), 549 subregion, 550 input: self["in"].as_graph_picture_reference().unwrap_or( 551 FilterOpGraphPictureReference{ 552 buffer_id: FilterOpGraphPictureBufferId::None, 553 }), 554 input2: self["in2"].as_graph_picture_reference().unwrap_or( 555 FilterOpGraphPictureReference{ 556 buffer_id: FilterOpGraphPictureBufferId::None, 557 }), 558 }; 559 let debug_print_input = |input: FilterOpGraphPictureReference| -> String { 560 match input.buffer_id { 561 FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId{}", id), 562 FilterOpGraphPictureBufferId::None => "None".into(), 563 } 564 }; 565 log!(Level::Debug, "svgfe parsed: {} linear: {} in: {} in2: {} subregion: [{}, {}, {}, {}]", 566 filter_op, node.linear, 567 debug_print_input(node.input), debug_print_input(node.input2), 568 node.subregion.min.x, node.subregion.min.y, node.subregion.max.x, node.subregion.max.y, 569 ); 570 return match filter_op { 571 "identity" => Some(FilterOp::SVGFEIdentity{node}), 572 "opacity" => { 573 let value = self["value"].as_f32().unwrap(); 574 Some(FilterOp::SVGFEOpacity{node, valuebinding: value.into(), value}) 575 }, 576 "toalpha" => Some(FilterOp::SVGFEToAlpha{node}), 577 "blendcolor" => Some(FilterOp::SVGFEBlendColor{node}), 578 "blendcolorburn" => Some(FilterOp::SVGFEBlendColorBurn{node}), 579 "blendcolordodge" => Some(FilterOp::SVGFEBlendColorDodge{node}), 580 "blenddarken" => Some(FilterOp::SVGFEBlendDarken{node}), 581 "blenddifference" => Some(FilterOp::SVGFEBlendDifference{node}), 582 "blendexclusion" => Some(FilterOp::SVGFEBlendExclusion{node}), 583 "blendhardlight" => Some(FilterOp::SVGFEBlendHardLight{node}), 584 "blendhue" => Some(FilterOp::SVGFEBlendHue{node}), 585 "blendlighten" => Some(FilterOp::SVGFEBlendLighten{node}), 586 "blendluminosity" => Some(FilterOp::SVGFEBlendLuminosity{node}), 587 "blendmultiply" => Some(FilterOp::SVGFEBlendMultiply{node}), 588 "blendnormal" => Some(FilterOp::SVGFEBlendNormal{node}), 589 "blendoverlay" => Some(FilterOp::SVGFEBlendOverlay{node}), 590 "blendsaturation" => Some(FilterOp::SVGFEBlendSaturation{node}), 591 "blendscreen" => Some(FilterOp::SVGFEBlendScreen{node}), 592 "blendsoftlight" => Some(FilterOp::SVGFEBlendSoftLight{node}), 593 "colormatrix" => { 594 let m: Vec<f32> = self["matrix"].as_vec_f32().unwrap(); 595 let mut matrix: [f32; 20] = [0.0; 20]; 596 matrix.clone_from_slice(&m); 597 Some(FilterOp::SVGFEColorMatrix{node, values: matrix}) 598 } 599 "componenttransfer" => Some(FilterOp::SVGFEComponentTransfer{node}), 600 "compositearithmetic" => { 601 let k: Vec<f32> = self["k"].as_vec_f32().unwrap(); 602 Some(FilterOp::SVGFECompositeArithmetic{ 603 node, 604 k1: k[0], 605 k2: k[1], 606 k3: k[2], 607 k4: k[3], 608 }) 609 } 610 "compositeatop" => Some(FilterOp::SVGFECompositeATop{node}), 611 "compositein" => Some(FilterOp::SVGFECompositeIn{node}), 612 "compositelighter" => Some(FilterOp::SVGFECompositeLighter{node}), 613 "compositeout" => Some(FilterOp::SVGFECompositeOut{node}), 614 "compositeover" => Some(FilterOp::SVGFECompositeOver{node}), 615 "compositexor" => Some(FilterOp::SVGFECompositeXOR{node}), 616 "convolvematrixedgemodeduplicate" => { 617 let order_x = self["order_x"].as_i64().unwrap() as i32; 618 let order_y = self["order_y"].as_i64().unwrap() as i32; 619 let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap(); 620 let mut kernel: [f32; 25] = [0.0; 25]; 621 kernel.clone_from_slice(&m); 622 let divisor = self["divisor"].as_f32().unwrap(); 623 let bias = self["bias"].as_f32().unwrap(); 624 let target_x = self["target_x"].as_i64().unwrap() as i32; 625 let target_y = self["target_y"].as_i64().unwrap() as i32; 626 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 627 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 628 let preserve_alpha = match self["preserve_alpha"].as_bool() { 629 Some(true) => 1, 630 Some(false) => 0, 631 _ => 1, 632 }; 633 Some(FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate{ 634 node, order_x, order_y, kernel, divisor, bias, 635 target_x, target_y, kernel_unit_length_x, 636 kernel_unit_length_y, preserve_alpha}) 637 }, 638 "convolvematrixedgemodenone" => { 639 let order_x = self["order_x"].as_i64().unwrap() as i32; 640 let order_y = self["order_y"].as_i64().unwrap() as i32; 641 let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap(); 642 let mut kernel: [f32; 25] = [0.0; 25]; 643 kernel.clone_from_slice(&m); 644 let divisor = self["divisor"].as_f32().unwrap(); 645 let bias = self["bias"].as_f32().unwrap(); 646 let target_x = self["target_x"].as_i64().unwrap() as i32; 647 let target_y = self["target_y"].as_i64().unwrap() as i32; 648 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 649 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 650 let preserve_alpha = match self["preserve_alpha"].as_bool() { 651 Some(true) => 1, 652 Some(false) => 0, 653 _ => 1, 654 }; 655 Some(FilterOp::SVGFEConvolveMatrixEdgeModeNone{ 656 node, order_x, order_y, kernel, divisor, bias, 657 target_x, target_y, kernel_unit_length_x, 658 kernel_unit_length_y, preserve_alpha}) 659 }, 660 "convolvematrixedgemodewrap" => { 661 let order_x = self["order_x"].as_i64().unwrap() as i32; 662 let order_y = self["order_y"].as_i64().unwrap() as i32; 663 let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap(); 664 let mut kernel: [f32; 25] = [0.0; 25]; 665 kernel.clone_from_slice(&m); 666 let divisor = self["divisor"].as_f32().unwrap(); 667 let bias = self["bias"].as_f32().unwrap(); 668 let target_x = self["target_x"].as_i64().unwrap() as i32; 669 let target_y = self["target_y"].as_i64().unwrap() as i32; 670 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 671 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 672 let preserve_alpha = match self["preserve_alpha"].as_bool() { 673 Some(true) => 1, 674 Some(false) => 0, 675 _ => 1, 676 }; 677 Some(FilterOp::SVGFEConvolveMatrixEdgeModeWrap{ 678 node, order_x, order_y, kernel, divisor, bias, 679 target_x, target_y, kernel_unit_length_x, 680 kernel_unit_length_y, preserve_alpha}) 681 }, 682 "diffuselightingdistant" => { 683 let surface_scale = self["surface_scale"].as_f32().unwrap(); 684 let diffuse_constant = self["diffuse_constant"].as_f32().unwrap(); 685 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 686 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 687 let azimuth = self["azimuth"].as_f32().unwrap(); 688 let elevation = self["elevation"].as_f32().unwrap(); 689 Some(FilterOp::SVGFEDiffuseLightingDistant{ 690 node, surface_scale, diffuse_constant, 691 kernel_unit_length_x, kernel_unit_length_y, 692 azimuth, elevation}) 693 }, 694 "diffuselightingpoint" => { 695 let surface_scale = self["surface_scale"].as_f32().unwrap(); 696 let diffuse_constant = self["diffuse_constant"].as_f32().unwrap(); 697 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 698 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 699 let x = self["x"].as_f32().unwrap(); 700 let y = self["y"].as_f32().unwrap(); 701 let z = self["z"].as_f32().unwrap(); 702 Some(FilterOp::SVGFEDiffuseLightingPoint{ 703 node, surface_scale, diffuse_constant, 704 kernel_unit_length_x, kernel_unit_length_y, x, y, z}) 705 }, 706 "diffuselightingspot" => { 707 let surface_scale = self["surface_scale"].as_f32().unwrap(); 708 let diffuse_constant = self["diffuse_constant"].as_f32().unwrap(); 709 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 710 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 711 let x = self["x"].as_f32().unwrap(); 712 let y = self["y"].as_f32().unwrap(); 713 let z = self["z"].as_f32().unwrap(); 714 let points_at_x = self["points_at_x"].as_f32().unwrap(); 715 let points_at_y = self["points_at_y"].as_f32().unwrap(); 716 let points_at_z = self["points_at_z"].as_f32().unwrap(); 717 let cone_exponent = self["cone_exponent"].as_f32().unwrap(); 718 let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap(); 719 Some(FilterOp::SVGFEDiffuseLightingSpot{ 720 node, surface_scale, diffuse_constant, 721 kernel_unit_length_x, kernel_unit_length_y, x, y, z, 722 points_at_x, points_at_y, points_at_z, cone_exponent, 723 limiting_cone_angle}) 724 }, 725 "displacementmap" => { 726 let scale = self["scale"].as_f32().unwrap(); 727 let x_channel_selector = self["x_channel_selector"].as_i64().unwrap() as u32; 728 let y_channel_selector = self["y_channel_selector"].as_i64().unwrap() as u32; 729 Some(FilterOp::SVGFEDisplacementMap{node, scale, x_channel_selector, y_channel_selector}) 730 }, 731 "dropshadow" => { 732 let color = self["color"].as_colorf().unwrap(); 733 let dx = self["dx"].as_f32().unwrap(); 734 let dy = self["dy"].as_f32().unwrap(); 735 let std_deviation_x = self["std_deviation_x"].as_f32().unwrap(); 736 let std_deviation_y = self["std_deviation_y"].as_f32().unwrap(); 737 Some(FilterOp::SVGFEDropShadow{node, color, dx, dy, std_deviation_x, std_deviation_y}) 738 }, 739 "flood" => Some(FilterOp::SVGFEFlood{node, color: self["color"].as_colorf().unwrap()}), 740 "gaussianblur" => { 741 let std_deviation_x = self["std_deviation_x"].as_f32().unwrap(); 742 let std_deviation_y = self["std_deviation_y"].as_f32().unwrap(); 743 Some(FilterOp::SVGFEGaussianBlur{node, std_deviation_x, std_deviation_y}) 744 }, 745 "image" => { 746 let sampling_filter = match self["sampling_filter"].as_str() { 747 Some("GOOD") => 0, 748 Some("LINEAR") => 1, 749 Some("POINT") => 2, 750 _ => 0, 751 }; 752 let m: Vec<f32> = self["matrix"].as_vec_f32().unwrap(); 753 let mut matrix: [f32; 6] = [0.0; 6]; 754 matrix.clone_from_slice(&m); 755 Some(FilterOp::SVGFEImage{node, sampling_filter, matrix}) 756 }, 757 "morphologydilate" => { 758 let radius_x = self["radius_x"].as_f32().unwrap(); 759 let radius_y = self["radius_y"].as_f32().unwrap(); 760 Some(FilterOp::SVGFEMorphologyDilate{node, radius_x, radius_y}) 761 }, 762 "morphologyerode" => { 763 let radius_x = self["radius_x"].as_f32().unwrap(); 764 let radius_y = self["radius_y"].as_f32().unwrap(); 765 Some(FilterOp::SVGFEMorphologyErode{node, radius_x, radius_y}) 766 }, 767 "offset" => { 768 let offset = self["offset"].as_vec_f32().unwrap(); 769 Some(FilterOp::SVGFEOffset{node, offset_x: offset[0], offset_y: offset[1]}) 770 }, 771 "SourceAlpha" => Some(FilterOp::SVGFESourceAlpha{node}), 772 "SourceGraphic" => Some(FilterOp::SVGFESourceGraphic{node}), 773 "sourcealpha" => Some(FilterOp::SVGFESourceAlpha{node}), 774 "sourcegraphic" => Some(FilterOp::SVGFESourceGraphic{node}), 775 "specularlightingdistant" => { 776 let surface_scale = self["surface_scale"].as_f32().unwrap(); 777 let specular_constant = self["specular_constant"].as_f32().unwrap(); 778 let specular_exponent = self["specular_exponent"].as_f32().unwrap(); 779 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 780 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 781 let azimuth = self["azimuth"].as_f32().unwrap(); 782 let elevation = self["elevation"].as_f32().unwrap(); 783 Some(FilterOp::SVGFESpecularLightingDistant{ 784 node, surface_scale, specular_constant, 785 specular_exponent, kernel_unit_length_x, 786 kernel_unit_length_y, azimuth, elevation}) 787 }, 788 "specularlightingpoint" => { 789 let surface_scale = self["surface_scale"].as_f32().unwrap(); 790 let specular_constant = self["specular_constant"].as_f32().unwrap(); 791 let specular_exponent = self["specular_exponent"].as_f32().unwrap(); 792 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 793 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 794 let x = self["x"].as_f32().unwrap(); 795 let y = self["y"].as_f32().unwrap(); 796 let z = self["z"].as_f32().unwrap(); 797 Some(FilterOp::SVGFESpecularLightingPoint{ 798 node, surface_scale, specular_constant, 799 specular_exponent, kernel_unit_length_x, 800 kernel_unit_length_y, x, y, z}) 801 }, 802 "specularlightingspot" => { 803 let surface_scale = self["surface_scale"].as_f32().unwrap(); 804 let specular_constant = self["specular_constant"].as_f32().unwrap(); 805 let specular_exponent = self["specular_exponent"].as_f32().unwrap(); 806 let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); 807 let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); 808 let x = self["x"].as_f32().unwrap(); 809 let y = self["y"].as_f32().unwrap(); 810 let z = self["z"].as_f32().unwrap(); 811 let points_at_x = self["points_at_x"].as_f32().unwrap(); 812 let points_at_y = self["points_at_y"].as_f32().unwrap(); 813 let points_at_z = self["points_at_z"].as_f32().unwrap(); 814 let cone_exponent = self["cone_exponent"].as_f32().unwrap(); 815 let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap(); 816 Some(FilterOp::SVGFESpecularLightingSpot{ 817 node, surface_scale, specular_constant, 818 specular_exponent, kernel_unit_length_x, 819 kernel_unit_length_y, x, y, z, points_at_x, 820 points_at_y, points_at_z, limiting_cone_angle, 821 cone_exponent}) 822 }, 823 "tile" => Some(FilterOp::SVGFETile{node}), 824 "turbulencewithfractalnoisewithnostitching" => { 825 let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); 826 let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); 827 let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; 828 let seed = self["seed"].as_i64().unwrap() as u32; 829 Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ 830 node, base_frequency_x, base_frequency_y, num_octaves, seed}) 831 }, 832 "turbulencewithfractalnoisewithstitching" => { 833 let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); 834 let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); 835 let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; 836 let seed = self["seed"].as_i64().unwrap() as u32; 837 Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching{ 838 node, base_frequency_x, base_frequency_y, num_octaves, seed}) 839 }, 840 "turbulencewithturbulencenoisewithnostitching" => { 841 let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); 842 let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); 843 let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; 844 let seed = self["seed"].as_i64().unwrap() as u32; 845 Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{ 846 node, base_frequency_x, base_frequency_y, num_octaves, seed}) 847 }, 848 "turbulencewithturbulencenoisewithstitching" => { 849 let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); 850 let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); 851 let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; 852 let seed = self["seed"].as_i64().unwrap() as u32; 853 Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{ 854 node, base_frequency_x, base_frequency_y, num_octaves, seed}) 855 }, 856 _ => None, 857 } 858 } 859 if let Some(s) = self.as_str() { 860 match parse_function(s) { 861 ("identity", _, _) => { 862 Some(FilterOp::Identity) 863 } 864 ("component-transfer", _, _) => { 865 Some(FilterOp::ComponentTransfer) 866 } 867 ("blur", ref args, _) if args.len() == 2 => { 868 Some(FilterOp::Blur(args[0].parse().unwrap(), args[1].parse().unwrap())) 869 } 870 ("brightness", ref args, _) if args.len() == 1 => { 871 Some(FilterOp::Brightness(args[0].parse().unwrap())) 872 } 873 ("contrast", ref args, _) if args.len() == 1 => { 874 Some(FilterOp::Contrast(args[0].parse().unwrap())) 875 } 876 ("grayscale", ref args, _) if args.len() == 1 => { 877 Some(FilterOp::Grayscale(args[0].parse().unwrap())) 878 } 879 ("hue-rotate", ref args, _) if args.len() == 1 => { 880 Some(FilterOp::HueRotate(args[0].parse().unwrap())) 881 } 882 ("invert", ref args, _) if args.len() == 1 => { 883 Some(FilterOp::Invert(args[0].parse().unwrap())) 884 } 885 ("opacity", ref args, _) if args.len() == 1 => { 886 let amount: f32 = args[0].parse().unwrap(); 887 Some(FilterOp::Opacity(amount.into(), amount)) 888 } 889 ("saturate", ref args, _) if args.len() == 1 => { 890 Some(FilterOp::Saturate(args[0].parse().unwrap())) 891 } 892 ("sepia", ref args, _) if args.len() == 1 => { 893 Some(FilterOp::Sepia(args[0].parse().unwrap())) 894 } 895 ("srgb-to-linear", _, _) => Some(FilterOp::SrgbToLinear), 896 ("linear-to-srgb", _, _) => Some(FilterOp::LinearToSrgb), 897 ("drop-shadow", ref args, _) if args.len() == 3 => { 898 let str = format!("---\noffset: {}\nblur-radius: {}\ncolor: {}\n", args[0], args[1], args[2]); 899 let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse drop-shadow"); 900 let yaml = yaml_doc.pop().unwrap(); 901 Some(FilterOp::DropShadow(Shadow { 902 offset: yaml["offset"].as_vector().unwrap(), 903 blur_radius: yaml["blur-radius"].as_f32().unwrap(), 904 color: yaml["color"].as_colorf().unwrap() 905 })) 906 } 907 ("color-matrix", ref args, _) if args.len() == 20 => { 908 let m: Vec<f32> = args.iter().map(|f| f.parse().unwrap()).collect(); 909 let mut matrix: [f32; 20] = [0.0; 20]; 910 matrix.clone_from_slice(&m); 911 Some(FilterOp::ColorMatrix(matrix)) 912 } 913 ("flood", ref args, _) if args.len() == 1 => { 914 let str = format!("---\ncolor: {}\n", args[0]); 915 let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse flood"); 916 let yaml = yaml_doc.pop().unwrap(); 917 Some(FilterOp::Flood(yaml["color"].as_colorf().unwrap())) 918 } 919 (_, _, _) => None, 920 } 921 } else { 922 None 923 } 924 } 925 926 fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>> { 927 if let Some(v) = self.as_vec() { 928 Some(v.iter().map(|x| x.as_filter_op().unwrap()).collect()) 929 } else { 930 self.as_filter_op().map(|op| vec![op]) 931 } 932 } 933 934 fn as_filter_data(&self) -> Option<FilterData> { 935 // Parse an array with five entries. First entry is an array of func types (4). 936 // The remaining entries are arrays of floats. 937 if let Yaml::Array(ref array) = *self { 938 if array.len() != 5 { 939 panic!("Invalid filter data specified, base array doesn't have five entries: {:?}", self); 940 } 941 if let Some(func_types_p) = array[0].as_vec_string() { 942 if func_types_p.len() != 4 { 943 panic!("Invalid filter data specified, func type array doesn't have five entries: {:?}", self); 944 } 945 let func_types: Vec<ComponentTransferFuncType> = 946 func_types_p.into_iter().map(|x| 947 StringEnum::from_str(&x).unwrap_or_else(|| 948 panic!("Invalid filter data specified, invalid func type name: {:?}", self)) 949 ).collect(); 950 if let Some(r_values_p) = array[1].as_vec_f32() { 951 if let Some(g_values_p) = array[2].as_vec_f32() { 952 if let Some(b_values_p) = array[3].as_vec_f32() { 953 if let Some(a_values_p) = array[4].as_vec_f32() { 954 let filter_data = FilterData { 955 func_r_type: func_types[0], 956 r_values: r_values_p, 957 func_g_type: func_types[1], 958 g_values: g_values_p, 959 func_b_type: func_types[2], 960 b_values: b_values_p, 961 func_a_type: func_types[3], 962 a_values: a_values_p, 963 }; 964 return Some(filter_data) 965 } 966 } 967 } 968 } 969 } 970 } 971 None 972 } 973 974 fn as_vec_filter_data(&self) -> Option<Vec<FilterData>> { 975 if let Some(v) = self.as_vec() { 976 Some(v.iter().map(|x| x.as_filter_data().unwrap()).collect()) 977 } else { 978 self.as_filter_data().map(|data| vec![data]) 979 } 980 } 981 982 fn as_complex_clip_region(&self) -> ComplexClipRegion { 983 let rect = self["rect"] 984 .as_rect() 985 .expect("Complex clip entry must have rect"); 986 let radius = self["radius"] 987 .as_border_radius() 988 .unwrap_or_else(BorderRadius::zero); 989 let mode = self["clip-mode"] 990 .as_clip_mode() 991 .unwrap_or(ClipMode::Clip); 992 ComplexClipRegion::new(rect, radius, mode) 993 } 994 995 fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds { 996 match *self { 997 Yaml::Array(ref array) => StickyOffsetBounds::new( 998 array[0].as_f32().unwrap_or(0.0), 999 array[1].as_f32().unwrap_or(0.0), 1000 ), 1001 _ => StickyOffsetBounds::new(0.0, 0.0), 1002 } 1003 } 1004 1005 fn as_gradient(&self, dl: &mut DisplayListBuilder) -> Gradient { 1006 let start = self["start"].as_point().expect("gradient must have start"); 1007 let end = self["end"].as_point().expect("gradient must have end"); 1008 let stops = self["stops"] 1009 .as_vec() 1010 .expect("gradient must have stops") 1011 .chunks(2) 1012 .map(|chunk| { 1013 GradientStop { 1014 offset: chunk[0] 1015 .as_force_f32() 1016 .expect("gradient stop offset is not f32"), 1017 color: chunk[1] 1018 .as_colorf() 1019 .expect("gradient stop color is not color"), 1020 } 1021 }) 1022 .collect::<Vec<_>>(); 1023 let extend_mode = if self["repeat"].as_bool().unwrap_or(false) { 1024 ExtendMode::Repeat 1025 } else { 1026 ExtendMode::Clamp 1027 }; 1028 1029 dl.create_gradient(start, end, stops, extend_mode) 1030 } 1031 1032 fn as_radial_gradient(&self, dl: &mut DisplayListBuilder) -> RadialGradient { 1033 let center = self["center"].as_point().expect("radial gradient must have center"); 1034 let radius = self["radius"].as_size().expect("radial gradient must have a radius"); 1035 let stops = self["stops"] 1036 .as_vec() 1037 .expect("radial gradient must have stops") 1038 .chunks(2) 1039 .map(|chunk| { 1040 GradientStop { 1041 offset: chunk[0] 1042 .as_force_f32() 1043 .expect("gradient stop offset is not f32"), 1044 color: chunk[1] 1045 .as_colorf() 1046 .expect("gradient stop color is not color"), 1047 } 1048 }) 1049 .collect::<Vec<_>>(); 1050 let extend_mode = if self["repeat"].as_bool().unwrap_or(false) { 1051 ExtendMode::Repeat 1052 } else { 1053 ExtendMode::Clamp 1054 }; 1055 1056 dl.create_radial_gradient(center, radius, stops, extend_mode) 1057 } 1058 1059 fn as_conic_gradient(&self, dl: &mut DisplayListBuilder) -> ConicGradient { 1060 let center = self["center"].as_point().expect("conic gradient must have center"); 1061 let angle = self["angle"].as_force_f32().expect("conic gradient must have an angle"); 1062 let stops = self["stops"] 1063 .as_vec() 1064 .expect("conic gradient must have stops") 1065 .chunks(2) 1066 .map(|chunk| { 1067 GradientStop { 1068 offset: chunk[0] 1069 .as_force_f32() 1070 .expect("gradient stop offset is not f32"), 1071 color: chunk[1] 1072 .as_colorf() 1073 .expect("gradient stop color is not color"), 1074 } 1075 }) 1076 .collect::<Vec<_>>(); 1077 let extend_mode = if self["repeat"].as_bool().unwrap_or(false) { 1078 ExtendMode::Repeat 1079 } else { 1080 ExtendMode::Clamp 1081 }; 1082 1083 dl.create_conic_gradient(center, angle, stops, extend_mode) 1084 } 1085 1086 fn as_complex_clip_regions(&self) -> Vec<ComplexClipRegion> { 1087 match *self { 1088 Yaml::Array(ref array) => array 1089 .iter() 1090 .map(Yaml::as_complex_clip_region) 1091 .collect(), 1092 Yaml::BadValue => vec![], 1093 _ => { 1094 println!("Unable to parse complex clip region {:?}", self); 1095 vec![] 1096 } 1097 } 1098 } 1099 1100 fn as_rotation(&self) -> Option<Rotation> { 1101 match *self { 1102 Yaml::Integer(0) => Some(Rotation::Degree0), 1103 Yaml::Integer(90) => Some(Rotation::Degree90), 1104 Yaml::Integer(180) => Some(Rotation::Degree180), 1105 Yaml::Integer(270) => Some(Rotation::Degree270), 1106 Yaml::BadValue => None, 1107 _ => { 1108 println!("Unable to parse rotation {:?}", self); 1109 None 1110 } 1111 } 1112 } 1113 }