/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use euclid::{Angle, Size2D}; use crate::parse_function::parse_function; use std::f32; use std::str::FromStr; use webrender::api::*; use webrender::api::units::*; use yaml_rust::{Yaml, YamlLoader}; use log::Level; pub trait YamlHelper { fn as_f32(&self) -> Option; fn as_force_f32(&self) -> Option; fn as_vec_f32(&self) -> Option>; fn as_vec_u32(&self) -> Option>; fn as_vec_u64(&self) -> Option>; fn as_pipeline_id(&self) -> Option; fn as_rect(&self) -> Option; fn as_size(&self) -> Option; fn as_point(&self) -> Option; fn as_vector(&self) -> Option; fn as_matrix4d(&self) -> Option; fn as_transform(&self, transform_origin: &LayoutPoint) -> Option; fn as_colorf(&self) -> Option; fn as_vec_colorf(&self) -> Option>; fn as_px_to_f32(&self) -> Option; fn as_pt_to_f32(&self) -> Option; fn as_vec_string(&self) -> Option>; fn as_border_radius_component(&self) -> LayoutSize; fn as_border_radius(&self) -> Option; fn as_transform_style(&self) -> Option; fn as_raster_space(&self) -> Option; fn as_clip_mode(&self) -> Option; fn as_graph_picture_reference(&self) -> Option; fn as_mix_blend_mode(&self) -> Option; fn as_filter_op(&self) -> Option; fn as_vec_filter_op(&self) -> Option>; fn as_filter_data(&self) -> Option; fn as_vec_filter_data(&self) -> Option>; fn as_filter_input(&self) -> Option; fn as_filter_primitive(&self) -> Option; fn as_vec_filter_primitive(&self) -> Option>; fn as_color_space(&self) -> Option; fn as_complex_clip_region(&self) -> ComplexClipRegion; fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds; fn as_gradient(&self, dl: &mut DisplayListBuilder) -> Gradient; fn as_radial_gradient(&self, dl: &mut DisplayListBuilder) -> RadialGradient; fn as_conic_gradient(&self, dl: &mut DisplayListBuilder) -> ConicGradient; fn as_complex_clip_regions(&self) -> Vec; fn as_rotation(&self) -> Option; } fn string_to_color(color: &str) -> Option { match color { "red" => Some(ColorF::new(1.0, 0.0, 0.0, 1.0)), "green" => Some(ColorF::new(0.0, 1.0, 0.0, 1.0)), "blue" => Some(ColorF::new(0.0, 0.0, 1.0, 1.0)), "white" => Some(ColorF::new(1.0, 1.0, 1.0, 1.0)), "black" => Some(ColorF::new(0.0, 0.0, 0.0, 1.0)), "yellow" => Some(ColorF::new(1.0, 1.0, 0.0, 1.0)), "cyan" => Some(ColorF::new(0.0, 1.0, 1.0, 1.0)), "magenta" => Some(ColorF::new(1.0, 0.0, 1.0, 1.0)), "transparent" => Some(ColorF::new(1.0, 1.0, 1.0, 0.0)), s => { let items: Vec = s.split_whitespace() .map(|s| f32::from_str(s).unwrap()) .collect(); if items.len() == 3 { Some(ColorF::new( items[0] / 255.0, items[1] / 255.0, items[2] / 255.0, 1.0, )) } else if items.len() == 4 { Some(ColorF::new( items[0] / 255.0, items[1] / 255.0, items[2] / 255.0, items[3], )) } else { None } } } } pub trait StringEnum: Sized { fn from_str(_: &str) -> Option; fn as_str(&self) -> &'static str; } macro_rules! define_string_enum { ($T:ident, [ $( $y:ident = $x:expr ),* ]) => { impl StringEnum for $T { fn from_str(text: &str) -> Option<$T> { match text { $( $x => Some($T::$y), )* _ => { println!("Unrecognized {} value '{}'", stringify!($T), text); None } } } fn as_str(&self) -> &'static str { match *self { $( $T::$y => $x, )* } } } } } define_string_enum!(TransformStyle, [Flat = "flat", Preserve3D = "preserve-3d"]); define_string_enum!( MixBlendMode, [ Normal = "normal", Multiply = "multiply", Screen = "screen", Overlay = "overlay", Darken = "darken", Lighten = "lighten", ColorDodge = "color-dodge", ColorBurn = "color-burn", HardLight = "hard-light", SoftLight = "soft-light", Difference = "difference", Exclusion = "exclusion", Hue = "hue", Saturation = "saturation", Color = "color", Luminosity = "luminosity", PlusLighter = "plus-lighter" ] ); define_string_enum!( LineOrientation, [Horizontal = "horizontal", Vertical = "vertical"] ); define_string_enum!( LineStyle, [ Solid = "solid", Dotted = "dotted", Dashed = "dashed", Wavy = "wavy" ] ); define_string_enum!(ClipMode, [Clip = "clip", ClipOut = "clip-out"]); define_string_enum!( ComponentTransferFuncType, [ Identity = "Identity", Table = "Table", Discrete = "Discrete", Linear = "Linear", Gamma = "Gamma" ] ); define_string_enum!( ColorSpace, [ Srgb = "srgb", LinearRgb = "linear-rgb" ] ); // Rotate around `axis` by `degrees` angle fn make_rotation( origin: &LayoutPoint, degrees: f32, axis_x: f32, axis_y: f32, axis_z: f32, ) -> LayoutTransform { let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0); let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0); let theta = 2.0f32 * f32::consts::PI - degrees.to_radians(); let transform = LayoutTransform::identity().pre_rotate(axis_x, axis_y, axis_z, Angle::radians(theta)); pre_transform.then(&transform).then(&post_transform) } pub fn make_perspective( origin: LayoutPoint, perspective: f32, ) -> LayoutTransform { let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0); let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0); let transform = LayoutTransform::perspective(perspective); pre_transform.then(&transform).then(&post_transform) } // Create a skew matrix, specified in degrees. fn make_skew( skew_x: f32, skew_y: f32, ) -> LayoutTransform { let alpha = Angle::radians(skew_x.to_radians()); let beta = Angle::radians(skew_y.to_radians()); LayoutTransform::skew(alpha, beta) } impl YamlHelper for Yaml { fn as_f32(&self) -> Option { match *self { Yaml::Integer(iv) => Some(iv as f32), Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(), _ => None, } } fn as_force_f32(&self) -> Option { match *self { Yaml::Integer(iv) => Some(iv as f32), Yaml::String(ref sv) | Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(), _ => None, } } fn as_vec_f32(&self) -> Option> { match *self { Yaml::String(ref s) | Yaml::Real(ref s) => s.split_whitespace() .map(f32::from_str) .collect::, _>>() .ok(), Yaml::Array(ref v) => v.iter() .map(|v| match *v { Yaml::Integer(k) => Ok(k as f32), Yaml::String(ref k) | Yaml::Real(ref k) => f32::from_str(k).map_err(|_| false), _ => Err(false), }) .collect::, _>>() .ok(), Yaml::Integer(k) => Some(vec![k as f32]), _ => None, } } fn as_vec_u32(&self) -> Option> { self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u32).collect()) } fn as_vec_u64(&self) -> Option> { self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u64).collect()) } fn as_pipeline_id(&self) -> Option { if let Some(v) = self.as_vec() { let a = v.get(0).and_then(|v| v.as_i64()).map(|v| v as u32); let b = v.get(1).and_then(|v| v.as_i64()).map(|v| v as u32); match (a, b) { (Some(a), Some(b)) if v.len() == 2 => Some(PipelineId(a, b)), _ => None, } } else { None } } fn as_px_to_f32(&self) -> Option { self.as_force_f32() } fn as_pt_to_f32(&self) -> Option { self.as_force_f32().map(|fv| fv * 16. / 12.) } fn as_rect(&self) -> Option { self.as_vec_f32().and_then(|v| match v.as_slice() { &[x, y, width, height] => Some(LayoutRect::from_origin_and_size( LayoutPoint::new(x, y), LayoutSize::new(width, height), )), _ => None, }) } fn as_size(&self) -> Option { if self.is_badvalue() { return None; } if let Some(nums) = self.as_vec_f32() { if nums.len() == 2 { return Some(LayoutSize::new(nums[0], nums[1])); } } None } fn as_point(&self) -> Option { if self.is_badvalue() { return None; } if let Some(nums) = self.as_vec_f32() { if nums.len() == 2 { return Some(LayoutPoint::new(nums[0], nums[1])); } } None } fn as_vector(&self) -> Option { self.as_point().map(|p| p.to_vector()) } fn as_matrix4d(&self) -> Option { if let Some(nums) = self.as_vec_f32() { assert_eq!(nums.len(), 16, "expected 16 floats, got '{:?}'", self); Some(LayoutTransform::new( nums[0], nums[1], nums[2], nums[3], nums[4], nums[5], nums[6], nums[7], nums[8], nums[9], nums[10], nums[11], nums[12], nums[13], nums[14], nums[15], )) } else { None } } fn as_transform(&self, transform_origin: &LayoutPoint) -> Option { if let Some(transform) = self.as_matrix4d() { return Some(transform); } match *self { Yaml::String(ref string) => { let mut slice = string.as_str(); let mut transform = LayoutTransform::identity(); while !slice.is_empty() { let (function, ref args, reminder) = parse_function(slice); slice = reminder; let mx = match function { "translate" if args.len() >= 2 => { let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(0.); LayoutTransform::translation( args[0].parse().unwrap(), args[1].parse().unwrap(), z, ) } "rotate" | "rotate-z" if args.len() == 1 => { make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 0.0, 1.0) } "rotate-x" if args.len() == 1 => { make_rotation(transform_origin, args[0].parse().unwrap(), 1.0, 0.0, 0.0) } "rotate-y" if args.len() == 1 => { make_rotation(transform_origin, args[0].parse().unwrap(), 0.0, 1.0, 0.0) } "scale" if !args.is_empty() => { let x = args[0].parse().unwrap(); // Default to uniform X/Y scale if Y unspecified. let y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(x); // Default to no Z scale if unspecified. let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(1.0); LayoutTransform::scale(x, y, z) } "scale-x" if args.len() == 1 => { LayoutTransform::scale(args[0].parse().unwrap(), 1.0, 1.0) } "scale-y" if args.len() == 1 => { LayoutTransform::scale(1.0, args[0].parse().unwrap(), 1.0) } "scale-z" if args.len() == 1 => { LayoutTransform::scale(1.0, 1.0, args[0].parse().unwrap()) } "skew" if !args.is_empty() => { // Default to no Y skew if unspecified. let skew_y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(0.0); make_skew(args[0].parse().unwrap(), skew_y) } "skew-x" if args.len() == 1 => { make_skew(args[0].parse().unwrap(), 0.0) } "skew-y" if args.len() == 1 => { make_skew(0.0, args[0].parse().unwrap()) } "perspective" if args.len() == 1 => { LayoutTransform::perspective(args[0].parse().unwrap()) } _ => { println!("unknown function {}", function); break; } }; transform = transform.then(&mx); } Some(transform) } Yaml::Array(ref array) => { let transform = array.iter().fold( LayoutTransform::identity(), |u, yaml| if let Some(transform) = yaml.as_transform(transform_origin) { transform.then(&u) } else { u }, ); Some(transform) } Yaml::BadValue => None, _ => { println!("unknown transform {:?}", self); None } } } /// Inputs for r, g, b channels are floats or ints in the range [0, 255]. /// If included, the alpha channel is in the range [0, 1]. /// This matches CSS-style, but requires conversion for `ColorF`. fn as_colorf(&self) -> Option { if let Some(nums) = self.as_vec_f32() { assert!(nums.iter().take(3).all(|x| (0.0 ..= 255.0).contains(x)), "r, g, b values should be in the 0-255 range, got {:?}", nums); let color: ColorF = match *nums.as_slice() { [r, g, b] => ColorF { r, g, b, a: 1.0 }, [r, g, b, a] => ColorF { r, g, b, a }, _ => panic!("color expected a color name, or 3-4 floats; got '{:?}'", self), }.scale_rgb(1.0 / 255.0); assert!((0.0 ..= 1.0).contains(&color.a), "alpha value should be in the 0-1 range, got {:?}", color.a); Some(color) } else if let Some(s) = self.as_str() { string_to_color(s) } else { None } } fn as_vec_colorf(&self) -> Option> { if let Some(v) = self.as_vec() { Some(v.iter().map(|v| v.as_colorf().unwrap()).collect()) } else { self.as_colorf().map(|color| vec![color]) } } fn as_vec_string(&self) -> Option> { if let Some(v) = self.as_vec() { Some(v.iter().map(|v| v.as_str().unwrap().to_owned()).collect()) } else { self.as_str().map(|s| vec![s.to_owned()]) } } fn as_border_radius_component(&self) -> LayoutSize { if let Yaml::Integer(integer) = *self { return LayoutSize::new(integer as f32, integer as f32); } self.as_size().unwrap_or_else(Size2D::zero) } fn as_border_radius(&self) -> Option { if let Some(size) = self.as_size() { return Some(BorderRadius::uniform_size(size)); } match *self { Yaml::BadValue => None, Yaml::String(ref s) | Yaml::Real(ref s) => { let fv = f32::from_str(s).unwrap(); Some(BorderRadius::uniform(fv)) } Yaml::Integer(v) => Some(BorderRadius::uniform(v as f32)), Yaml::Array(ref array) if array.len() == 4 => { let top_left = array[0].as_border_radius_component(); let top_right = array[1].as_border_radius_component(); let bottom_left = array[2].as_border_radius_component(); let bottom_right = array[3].as_border_radius_component(); Some(BorderRadius { top_left, top_right, bottom_left, bottom_right, }) } Yaml::Hash(_) => { let top_left = self["top-left"].as_border_radius_component(); let top_right = self["top-right"].as_border_radius_component(); let bottom_left = self["bottom-left"].as_border_radius_component(); let bottom_right = self["bottom-right"].as_border_radius_component(); Some(BorderRadius { top_left, top_right, bottom_left, bottom_right, }) } _ => { panic!("Invalid border radius specified: {:?}", self); } } } fn as_transform_style(&self) -> Option { self.as_str().and_then(StringEnum::from_str) } fn as_raster_space(&self) -> Option { self.as_str().map(|s| { match parse_function(s) { ("screen", _, _) => { RasterSpace::Screen } ("local", ref args, _) if args.len() == 1 => { RasterSpace::Local(args[0].parse().unwrap()) } f => { panic!("error parsing raster space {:?}", f); } } }) } fn as_mix_blend_mode(&self) -> Option { self.as_str().and_then(StringEnum::from_str) } fn as_clip_mode(&self) -> Option { self.as_str().and_then(StringEnum::from_str) } fn as_graph_picture_reference(&self) -> Option { match self.as_i64() { Some(n) => Some(FilterOpGraphPictureReference{ buffer_id: FilterOpGraphPictureBufferId::BufferId(n as i16), }), None => None, } } fn as_filter_op(&self) -> Option { if let Some(filter_op) = self["svgfe"].as_str() { let subregion = self["subregion"].as_rect().unwrap_or( LayoutRect::new( LayoutPoint::new(0.0, 0.0), LayoutPoint::new(1024.0, 1024.0), )); let node = FilterOpGraphNode { linear: self["linear"].as_bool().unwrap_or(true), subregion, input: self["in"].as_graph_picture_reference().unwrap_or( FilterOpGraphPictureReference{ buffer_id: FilterOpGraphPictureBufferId::None, }), input2: self["in2"].as_graph_picture_reference().unwrap_or( FilterOpGraphPictureReference{ buffer_id: FilterOpGraphPictureBufferId::None, }), }; let debug_print_input = |input: FilterOpGraphPictureReference| -> String { match input.buffer_id { FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId{}", id), FilterOpGraphPictureBufferId::None => "None".into(), } }; log!(Level::Debug, "svgfe parsed: {} linear: {} in: {} in2: {} subregion: [{}, {}, {}, {}]", filter_op, node.linear, debug_print_input(node.input), debug_print_input(node.input2), node.subregion.min.x, node.subregion.min.y, node.subregion.max.x, node.subregion.max.y, ); return match filter_op { "identity" => Some(FilterOp::SVGFEIdentity{node}), "opacity" => { let value = self["value"].as_f32().unwrap(); Some(FilterOp::SVGFEOpacity{node, valuebinding: value.into(), value}) }, "toalpha" => Some(FilterOp::SVGFEToAlpha{node}), "blendcolor" => Some(FilterOp::SVGFEBlendColor{node}), "blendcolorburn" => Some(FilterOp::SVGFEBlendColorBurn{node}), "blendcolordodge" => Some(FilterOp::SVGFEBlendColorDodge{node}), "blenddarken" => Some(FilterOp::SVGFEBlendDarken{node}), "blenddifference" => Some(FilterOp::SVGFEBlendDifference{node}), "blendexclusion" => Some(FilterOp::SVGFEBlendExclusion{node}), "blendhardlight" => Some(FilterOp::SVGFEBlendHardLight{node}), "blendhue" => Some(FilterOp::SVGFEBlendHue{node}), "blendlighten" => Some(FilterOp::SVGFEBlendLighten{node}), "blendluminosity" => Some(FilterOp::SVGFEBlendLuminosity{node}), "blendmultiply" => Some(FilterOp::SVGFEBlendMultiply{node}), "blendnormal" => Some(FilterOp::SVGFEBlendNormal{node}), "blendoverlay" => Some(FilterOp::SVGFEBlendOverlay{node}), "blendsaturation" => Some(FilterOp::SVGFEBlendSaturation{node}), "blendscreen" => Some(FilterOp::SVGFEBlendScreen{node}), "blendsoftlight" => Some(FilterOp::SVGFEBlendSoftLight{node}), "colormatrix" => { let m: Vec = self["matrix"].as_vec_f32().unwrap(); let mut matrix: [f32; 20] = [0.0; 20]; matrix.clone_from_slice(&m); Some(FilterOp::SVGFEColorMatrix{node, values: matrix}) } "componenttransfer" => Some(FilterOp::SVGFEComponentTransfer{node}), "compositearithmetic" => { let k: Vec = self["k"].as_vec_f32().unwrap(); Some(FilterOp::SVGFECompositeArithmetic{ node, k1: k[0], k2: k[1], k3: k[2], k4: k[3], }) } "compositeatop" => Some(FilterOp::SVGFECompositeATop{node}), "compositein" => Some(FilterOp::SVGFECompositeIn{node}), "compositelighter" => Some(FilterOp::SVGFECompositeLighter{node}), "compositeout" => Some(FilterOp::SVGFECompositeOut{node}), "compositeover" => Some(FilterOp::SVGFECompositeOver{node}), "compositexor" => Some(FilterOp::SVGFECompositeXOR{node}), "convolvematrixedgemodeduplicate" => { let order_x = self["order_x"].as_i64().unwrap() as i32; let order_y = self["order_y"].as_i64().unwrap() as i32; let m: Vec = self["kernel"].as_vec_f32().unwrap(); let mut kernel: [f32; 25] = [0.0; 25]; kernel.clone_from_slice(&m); let divisor = self["divisor"].as_f32().unwrap(); let bias = self["bias"].as_f32().unwrap(); let target_x = self["target_x"].as_i64().unwrap() as i32; let target_y = self["target_y"].as_i64().unwrap() as i32; let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let preserve_alpha = match self["preserve_alpha"].as_bool() { Some(true) => 1, Some(false) => 0, _ => 1, }; Some(FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate{ node, order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha}) }, "convolvematrixedgemodenone" => { let order_x = self["order_x"].as_i64().unwrap() as i32; let order_y = self["order_y"].as_i64().unwrap() as i32; let m: Vec = self["kernel"].as_vec_f32().unwrap(); let mut kernel: [f32; 25] = [0.0; 25]; kernel.clone_from_slice(&m); let divisor = self["divisor"].as_f32().unwrap(); let bias = self["bias"].as_f32().unwrap(); let target_x = self["target_x"].as_i64().unwrap() as i32; let target_y = self["target_y"].as_i64().unwrap() as i32; let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let preserve_alpha = match self["preserve_alpha"].as_bool() { Some(true) => 1, Some(false) => 0, _ => 1, }; Some(FilterOp::SVGFEConvolveMatrixEdgeModeNone{ node, order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha}) }, "convolvematrixedgemodewrap" => { let order_x = self["order_x"].as_i64().unwrap() as i32; let order_y = self["order_y"].as_i64().unwrap() as i32; let m: Vec = self["kernel"].as_vec_f32().unwrap(); let mut kernel: [f32; 25] = [0.0; 25]; kernel.clone_from_slice(&m); let divisor = self["divisor"].as_f32().unwrap(); let bias = self["bias"].as_f32().unwrap(); let target_x = self["target_x"].as_i64().unwrap() as i32; let target_y = self["target_y"].as_i64().unwrap() as i32; let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let preserve_alpha = match self["preserve_alpha"].as_bool() { Some(true) => 1, Some(false) => 0, _ => 1, }; Some(FilterOp::SVGFEConvolveMatrixEdgeModeWrap{ node, order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha}) }, "diffuselightingdistant" => { let surface_scale = self["surface_scale"].as_f32().unwrap(); let diffuse_constant = self["diffuse_constant"].as_f32().unwrap(); let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let azimuth = self["azimuth"].as_f32().unwrap(); let elevation = self["elevation"].as_f32().unwrap(); Some(FilterOp::SVGFEDiffuseLightingDistant{ node, surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation}) }, "diffuselightingpoint" => { let surface_scale = self["surface_scale"].as_f32().unwrap(); let diffuse_constant = self["diffuse_constant"].as_f32().unwrap(); let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let x = self["x"].as_f32().unwrap(); let y = self["y"].as_f32().unwrap(); let z = self["z"].as_f32().unwrap(); Some(FilterOp::SVGFEDiffuseLightingPoint{ node, surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z}) }, "diffuselightingspot" => { let surface_scale = self["surface_scale"].as_f32().unwrap(); let diffuse_constant = self["diffuse_constant"].as_f32().unwrap(); let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let x = self["x"].as_f32().unwrap(); let y = self["y"].as_f32().unwrap(); let z = self["z"].as_f32().unwrap(); let points_at_x = self["points_at_x"].as_f32().unwrap(); let points_at_y = self["points_at_y"].as_f32().unwrap(); let points_at_z = self["points_at_z"].as_f32().unwrap(); let cone_exponent = self["cone_exponent"].as_f32().unwrap(); let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap(); Some(FilterOp::SVGFEDiffuseLightingSpot{ node, surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle}) }, "displacementmap" => { let scale = self["scale"].as_f32().unwrap(); let x_channel_selector = self["x_channel_selector"].as_i64().unwrap() as u32; let y_channel_selector = self["y_channel_selector"].as_i64().unwrap() as u32; Some(FilterOp::SVGFEDisplacementMap{node, scale, x_channel_selector, y_channel_selector}) }, "dropshadow" => { let color = self["color"].as_colorf().unwrap(); let dx = self["dx"].as_f32().unwrap(); let dy = self["dy"].as_f32().unwrap(); let std_deviation_x = self["std_deviation_x"].as_f32().unwrap(); let std_deviation_y = self["std_deviation_y"].as_f32().unwrap(); Some(FilterOp::SVGFEDropShadow{node, color, dx, dy, std_deviation_x, std_deviation_y}) }, "flood" => Some(FilterOp::SVGFEFlood{node, color: self["color"].as_colorf().unwrap()}), "gaussianblur" => { let std_deviation_x = self["std_deviation_x"].as_f32().unwrap(); let std_deviation_y = self["std_deviation_y"].as_f32().unwrap(); Some(FilterOp::SVGFEGaussianBlur{node, std_deviation_x, std_deviation_y}) }, "image" => { let sampling_filter = match self["sampling_filter"].as_str() { Some("GOOD") => 0, Some("LINEAR") => 1, Some("POINT") => 2, _ => 0, }; let m: Vec = self["matrix"].as_vec_f32().unwrap(); let mut matrix: [f32; 6] = [0.0; 6]; matrix.clone_from_slice(&m); Some(FilterOp::SVGFEImage{node, sampling_filter, matrix}) }, "morphologydilate" => { let radius_x = self["radius_x"].as_f32().unwrap(); let radius_y = self["radius_y"].as_f32().unwrap(); Some(FilterOp::SVGFEMorphologyDilate{node, radius_x, radius_y}) }, "morphologyerode" => { let radius_x = self["radius_x"].as_f32().unwrap(); let radius_y = self["radius_y"].as_f32().unwrap(); Some(FilterOp::SVGFEMorphologyErode{node, radius_x, radius_y}) }, "offset" => { let offset = self["offset"].as_vec_f32().unwrap(); Some(FilterOp::SVGFEOffset{node, offset_x: offset[0], offset_y: offset[1]}) }, "SourceAlpha" => Some(FilterOp::SVGFESourceAlpha{node}), "SourceGraphic" => Some(FilterOp::SVGFESourceGraphic{node}), "sourcealpha" => Some(FilterOp::SVGFESourceAlpha{node}), "sourcegraphic" => Some(FilterOp::SVGFESourceGraphic{node}), "specularlightingdistant" => { let surface_scale = self["surface_scale"].as_f32().unwrap(); let specular_constant = self["specular_constant"].as_f32().unwrap(); let specular_exponent = self["specular_exponent"].as_f32().unwrap(); let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let azimuth = self["azimuth"].as_f32().unwrap(); let elevation = self["elevation"].as_f32().unwrap(); Some(FilterOp::SVGFESpecularLightingDistant{ node, surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation}) }, "specularlightingpoint" => { let surface_scale = self["surface_scale"].as_f32().unwrap(); let specular_constant = self["specular_constant"].as_f32().unwrap(); let specular_exponent = self["specular_exponent"].as_f32().unwrap(); let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let x = self["x"].as_f32().unwrap(); let y = self["y"].as_f32().unwrap(); let z = self["z"].as_f32().unwrap(); Some(FilterOp::SVGFESpecularLightingPoint{ node, surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z}) }, "specularlightingspot" => { let surface_scale = self["surface_scale"].as_f32().unwrap(); let specular_constant = self["specular_constant"].as_f32().unwrap(); let specular_exponent = self["specular_exponent"].as_f32().unwrap(); let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap(); let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap(); let x = self["x"].as_f32().unwrap(); let y = self["y"].as_f32().unwrap(); let z = self["z"].as_f32().unwrap(); let points_at_x = self["points_at_x"].as_f32().unwrap(); let points_at_y = self["points_at_y"].as_f32().unwrap(); let points_at_z = self["points_at_z"].as_f32().unwrap(); let cone_exponent = self["cone_exponent"].as_f32().unwrap(); let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap(); Some(FilterOp::SVGFESpecularLightingSpot{ node, surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, limiting_cone_angle, cone_exponent}) }, "tile" => Some(FilterOp::SVGFETile{node}), "turbulencewithfractalnoisewithnostitching" => { let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; let seed = self["seed"].as_i64().unwrap() as u32; Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ node, base_frequency_x, base_frequency_y, num_octaves, seed}) }, "turbulencewithfractalnoisewithstitching" => { let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; let seed = self["seed"].as_i64().unwrap() as u32; Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching{ node, base_frequency_x, base_frequency_y, num_octaves, seed}) }, "turbulencewithturbulencenoisewithnostitching" => { let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; let seed = self["seed"].as_i64().unwrap() as u32; Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{ node, base_frequency_x, base_frequency_y, num_octaves, seed}) }, "turbulencewithturbulencenoisewithstitching" => { let base_frequency_x = self["base_frequency_x"].as_f32().unwrap(); let base_frequency_y = self["base_frequency_y"].as_f32().unwrap(); let num_octaves = self["num_octaves"].as_i64().unwrap() as u32; let seed = self["seed"].as_i64().unwrap() as u32; Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{ node, base_frequency_x, base_frequency_y, num_octaves, seed}) }, _ => None, } } if let Some(s) = self.as_str() { match parse_function(s) { ("identity", _, _) => { Some(FilterOp::Identity) } ("component-transfer", _, _) => { Some(FilterOp::ComponentTransfer) } ("blur", ref args, _) if args.len() == 2 => { Some(FilterOp::Blur(args[0].parse().unwrap(), args[1].parse().unwrap())) } ("brightness", ref args, _) if args.len() == 1 => { Some(FilterOp::Brightness(args[0].parse().unwrap())) } ("contrast", ref args, _) if args.len() == 1 => { Some(FilterOp::Contrast(args[0].parse().unwrap())) } ("grayscale", ref args, _) if args.len() == 1 => { Some(FilterOp::Grayscale(args[0].parse().unwrap())) } ("hue-rotate", ref args, _) if args.len() == 1 => { Some(FilterOp::HueRotate(args[0].parse().unwrap())) } ("invert", ref args, _) if args.len() == 1 => { Some(FilterOp::Invert(args[0].parse().unwrap())) } ("opacity", ref args, _) if args.len() == 1 => { let amount: f32 = args[0].parse().unwrap(); Some(FilterOp::Opacity(amount.into(), amount)) } ("saturate", ref args, _) if args.len() == 1 => { Some(FilterOp::Saturate(args[0].parse().unwrap())) } ("sepia", ref args, _) if args.len() == 1 => { Some(FilterOp::Sepia(args[0].parse().unwrap())) } ("srgb-to-linear", _, _) => Some(FilterOp::SrgbToLinear), ("linear-to-srgb", _, _) => Some(FilterOp::LinearToSrgb), ("drop-shadow", ref args, _) if args.len() == 3 => { let str = format!("---\noffset: {}\nblur-radius: {}\ncolor: {}\n", args[0], args[1], args[2]); let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse drop-shadow"); let yaml = yaml_doc.pop().unwrap(); Some(FilterOp::DropShadow(Shadow { offset: yaml["offset"].as_vector().unwrap(), blur_radius: yaml["blur-radius"].as_f32().unwrap(), color: yaml["color"].as_colorf().unwrap() })) } ("color-matrix", ref args, _) if args.len() == 20 => { let m: Vec = args.iter().map(|f| f.parse().unwrap()).collect(); let mut matrix: [f32; 20] = [0.0; 20]; matrix.clone_from_slice(&m); Some(FilterOp::ColorMatrix(matrix)) } ("flood", ref args, _) if args.len() == 1 => { let str = format!("---\ncolor: {}\n", args[0]); let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse flood"); let yaml = yaml_doc.pop().unwrap(); Some(FilterOp::Flood(yaml["color"].as_colorf().unwrap())) } (_, _, _) => None, } } else { None } } fn as_vec_filter_op(&self) -> Option> { if let Some(v) = self.as_vec() { Some(v.iter().map(|x| x.as_filter_op().unwrap()).collect()) } else { self.as_filter_op().map(|op| vec![op]) } } fn as_filter_data(&self) -> Option { // Parse an array with five entries. First entry is an array of func types (4). // The remaining entries are arrays of floats. if let Yaml::Array(ref array) = *self { if array.len() != 5 { panic!("Invalid filter data specified, base array doesn't have five entries: {:?}", self); } if let Some(func_types_p) = array[0].as_vec_string() { if func_types_p.len() != 4 { panic!("Invalid filter data specified, func type array doesn't have five entries: {:?}", self); } let func_types: Vec = func_types_p.into_iter().map(|x| StringEnum::from_str(&x).unwrap_or_else(|| panic!("Invalid filter data specified, invalid func type name: {:?}", self)) ).collect(); if let Some(r_values_p) = array[1].as_vec_f32() { if let Some(g_values_p) = array[2].as_vec_f32() { if let Some(b_values_p) = array[3].as_vec_f32() { if let Some(a_values_p) = array[4].as_vec_f32() { let filter_data = FilterData { func_r_type: func_types[0], r_values: r_values_p, func_g_type: func_types[1], g_values: g_values_p, func_b_type: func_types[2], b_values: b_values_p, func_a_type: func_types[3], a_values: a_values_p, }; return Some(filter_data) } } } } } } None } fn as_filter_input(&self) -> Option { if let Some(input) = self.as_str() { match input { "original" => Some(FilterPrimitiveInput::Original), "previous" => Some(FilterPrimitiveInput::Previous), _ => None, } } else if let Some(index) = self.as_i64() { if index >= 0 { Some(FilterPrimitiveInput::OutputOfPrimitiveIndex(index as usize)) } else { panic!("Filter input index cannot be negative"); } } else { panic!("Invalid filter input"); } } fn as_vec_filter_data(&self) -> Option> { if let Some(v) = self.as_vec() { Some(v.iter().map(|x| x.as_filter_data().unwrap()).collect()) } else { self.as_filter_data().map(|data| vec![data]) } } fn as_filter_primitive(&self) -> Option { if let Some(filter_type) = self["type"].as_str() { let kind = match filter_type { "identity" => { FilterPrimitiveKind::Identity(IdentityPrimitive { input: self["in"].as_filter_input().unwrap(), }) } "blend" => { FilterPrimitiveKind::Blend(BlendPrimitive { input1: self["in1"].as_filter_input().unwrap(), input2: self["in2"].as_filter_input().unwrap(), mode: self["blend-mode"].as_mix_blend_mode().unwrap(), }) } "flood" => { FilterPrimitiveKind::Flood(FloodPrimitive { color: self["color"].as_colorf().unwrap(), }) } "blur" => { FilterPrimitiveKind::Blur(BlurPrimitive { input: self["in"].as_filter_input().unwrap(), width: self["width"].as_f32().unwrap(), height: self["height"].as_f32().unwrap(), }) } "opacity" => { FilterPrimitiveKind::Opacity(OpacityPrimitive { input: self["in"].as_filter_input().unwrap(), opacity: self["opacity"].as_f32().unwrap(), }) } "color-matrix" => { let m: Vec = self["matrix"].as_vec_f32().unwrap(); let mut matrix: [f32; 20] = [0.0; 20]; matrix.clone_from_slice(&m); FilterPrimitiveKind::ColorMatrix(ColorMatrixPrimitive { input: self["in"].as_filter_input().unwrap(), matrix, }) } "drop-shadow" => { FilterPrimitiveKind::DropShadow(DropShadowPrimitive { input: self["in"].as_filter_input().unwrap(), shadow: Shadow { offset: self["offset"].as_vector().unwrap(), color: self["color"].as_colorf().unwrap(), blur_radius: self["radius"].as_f32().unwrap(), } }) } "component-transfer" => { FilterPrimitiveKind::ComponentTransfer(ComponentTransferPrimitive { input: self["in"].as_filter_input().unwrap(), }) } "offset" => { FilterPrimitiveKind::Offset(OffsetPrimitive { input: self["in"].as_filter_input().unwrap(), offset: self["offset"].as_vector().unwrap(), }) } "composite" => { let operator = match self["operator"].as_str().unwrap() { "over" => CompositeOperator::Over, "in" => CompositeOperator::In, "out" => CompositeOperator::Out, "atop" => CompositeOperator::Atop, "xor" => CompositeOperator::Xor, "lighter" => CompositeOperator::Lighter, "arithmetic" => { let k_vals = self["k-values"].as_vec_f32().unwrap(); assert!(k_vals.len() == 4, "Must be 4 k values for arithmetic composite operator"); let k_vals = [k_vals[0], k_vals[1], k_vals[2], k_vals[3]]; CompositeOperator::Arithmetic(k_vals) } _ => panic!("Invalid composite operator"), }; FilterPrimitiveKind::Composite(CompositePrimitive { input1: self["in1"].as_filter_input().unwrap(), input2: self["in2"].as_filter_input().unwrap(), operator, }) } _ => return None, }; Some(FilterPrimitive { kind, color_space: self["color-space"].as_color_space().unwrap_or(ColorSpace::LinearRgb), }) } else { None } } fn as_vec_filter_primitive(&self) -> Option> { if let Some(v) = self.as_vec() { Some(v.iter().map(|x| x.as_filter_primitive().unwrap()).collect()) } else { self.as_filter_primitive().map(|data| vec![data]) } } fn as_color_space(&self) -> Option { self.as_str().and_then(StringEnum::from_str) } fn as_complex_clip_region(&self) -> ComplexClipRegion { let rect = self["rect"] .as_rect() .expect("Complex clip entry must have rect"); let radius = self["radius"] .as_border_radius() .unwrap_or_else(BorderRadius::zero); let mode = self["clip-mode"] .as_clip_mode() .unwrap_or(ClipMode::Clip); ComplexClipRegion::new(rect, radius, mode) } fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds { match *self { Yaml::Array(ref array) => StickyOffsetBounds::new( array[0].as_f32().unwrap_or(0.0), array[1].as_f32().unwrap_or(0.0), ), _ => StickyOffsetBounds::new(0.0, 0.0), } } fn as_gradient(&self, dl: &mut DisplayListBuilder) -> Gradient { let start = self["start"].as_point().expect("gradient must have start"); let end = self["end"].as_point().expect("gradient must have end"); let stops = self["stops"] .as_vec() .expect("gradient must have stops") .chunks(2) .map(|chunk| { GradientStop { offset: chunk[0] .as_force_f32() .expect("gradient stop offset is not f32"), color: chunk[1] .as_colorf() .expect("gradient stop color is not color"), } }) .collect::>(); let extend_mode = if self["repeat"].as_bool().unwrap_or(false) { ExtendMode::Repeat } else { ExtendMode::Clamp }; dl.create_gradient(start, end, stops, extend_mode) } fn as_radial_gradient(&self, dl: &mut DisplayListBuilder) -> RadialGradient { let center = self["center"].as_point().expect("radial gradient must have center"); let radius = self["radius"].as_size().expect("radial gradient must have a radius"); let stops = self["stops"] .as_vec() .expect("radial gradient must have stops") .chunks(2) .map(|chunk| { GradientStop { offset: chunk[0] .as_force_f32() .expect("gradient stop offset is not f32"), color: chunk[1] .as_colorf() .expect("gradient stop color is not color"), } }) .collect::>(); let extend_mode = if self["repeat"].as_bool().unwrap_or(false) { ExtendMode::Repeat } else { ExtendMode::Clamp }; dl.create_radial_gradient(center, radius, stops, extend_mode) } fn as_conic_gradient(&self, dl: &mut DisplayListBuilder) -> ConicGradient { let center = self["center"].as_point().expect("conic gradient must have center"); let angle = self["angle"].as_force_f32().expect("conic gradient must have an angle"); let stops = self["stops"] .as_vec() .expect("conic gradient must have stops") .chunks(2) .map(|chunk| { GradientStop { offset: chunk[0] .as_force_f32() .expect("gradient stop offset is not f32"), color: chunk[1] .as_colorf() .expect("gradient stop color is not color"), } }) .collect::>(); let extend_mode = if self["repeat"].as_bool().unwrap_or(false) { ExtendMode::Repeat } else { ExtendMode::Clamp }; dl.create_conic_gradient(center, angle, stops, extend_mode) } fn as_complex_clip_regions(&self) -> Vec { match *self { Yaml::Array(ref array) => array .iter() .map(Yaml::as_complex_clip_region) .collect(), Yaml::BadValue => vec![], _ => { println!("Unable to parse complex clip region {:?}", self); vec![] } } } fn as_rotation(&self) -> Option { match *self { Yaml::Integer(0) => Some(Rotation::Degree0), Yaml::Integer(90) => Some(Rotation::Degree90), Yaml::Integer(180) => Some(Rotation::Degree180), Yaml::Integer(270) => Some(Rotation::Degree270), Yaml::BadValue => None, _ => { println!("Unable to parse rotation {:?}", self); None } } } }