Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  yaml_helper.rs   Sprache: unbekannt

 
/* 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<f32>;
    fn as_force_f32(&self) -> Option<f32>;
    fn as_vec_f32(&self) -> Option<Vec<f32>>;
    fn as_vec_u32(&self) -> Option<Vec<u32>>;
    fn as_vec_u64(&self) -> Option<Vec<u64>>;
    fn as_pipeline_id(&self) -> Option<PipelineId>;
    fn as_rect(&self) -> Option<LayoutRect>;
    fn as_size(&self) -> Option<LayoutSize>;
    fn as_point(&self) -> Option<LayoutPoint>;
    fn as_vector(&self) -> Option<LayoutVector2D>;
    fn as_matrix4d(&self) -> Option<LayoutTransform>;
    fn as_transform(&self, transform_origin: &LayoutPoint) -> Option<LayoutTransform>;
    fn as_colorf(&self) -> Option<ColorF>;
    fn as_vec_colorf(&self) -> Option<Vec<ColorF>>;
    fn as_pt_to_f32(&self) -> Option<f32>;
    fn as_vec_string(&self) -> Option<Vec<String>>;
    fn as_border_radius_component(&self) -> LayoutSize;
    fn as_border_radius(&self) -> Option<BorderRadius>;
    fn as_transform_style(&self) -> Option<TransformStyle>;
    fn as_raster_space(&self) -> Option<RasterSpace>;
    fn as_clip_mode(&self) -> Option<ClipMode>;
    fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference>;
    fn as_mix_blend_mode(&self) -> Option<MixBlendMode>;
    fn as_filter_op(&self) -> Option<FilterOp>;
    fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>>;
    fn as_filter_data(&self) -> Option<FilterData>;
    fn as_vec_filter_data(&self) -> Option<Vec<FilterData>>;
    fn as_filter_input(&self) -> Option<FilterPrimitiveInput>;
    fn as_filter_primitive(&self) -> Option<FilterPrimitive>;
    fn as_vec_filter_primitive(&self) -> Option<Vec<FilterPrimitive>>;
    fn as_color_space(&self) -> Option<ColorSpace>;
    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<ComplexClipRegion>;
    fn as_rotation(&self) -> Option<Rotation>;
}

fn string_to_color(color: &str) -> Option<ColorF> {
    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<f32> = 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<Self>;
}

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
                    }
                }
            }
        }
    }
}

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<f32> {
        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<f32> {
        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<Vec<f32>> {
        match *self {
            Yaml::String(ref s) | Yaml::Real(ref s) => s.split_whitespace()
                .map(f32::from_str)
                .collect::<Result<Vec<_>, _>>()
                .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::<Result<Vec<_>, _>>()
                .ok(),
            Yaml::Integer(k) => Some(vec![k as f32]),
            _ => None,
        }
    }

    fn as_vec_u32(&self) -> Option<Vec<u32>> {
        self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u32).collect())
    }

    fn as_vec_u64(&self) -> Option<Vec<u64>> {
        self.as_vec().map(|v| v.iter().map(|v| v.as_i64().unwrap() as u64).collect())
    }

    fn as_pipeline_id(&self) -> Option<PipelineId> {
        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_pt_to_f32(&self) -> Option<f32> {
        self.as_force_f32().map(|fv| fv * 16. / 12.)
    }

    fn as_rect(&self) -> Option<LayoutRect> {
        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<LayoutSize> {
        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<LayoutPoint> {
        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<LayoutVector2D> {
        self.as_point().map(|p| p.to_vector())
    }

    fn as_matrix4d(&self) -> Option<LayoutTransform> {
        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<LayoutTransform> {
        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<ColorF> {
        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<Vec<ColorF>> {
        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<Vec<String>> {
        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<BorderRadius> {
        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<TransformStyle> {
        self.as_str().and_then(StringEnum::from_str)
    }

    fn as_raster_space(&self) -> Option<RasterSpace> {
        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<MixBlendMode> {
        self.as_str().and_then(StringEnum::from_str)
    }

    fn as_clip_mode(&self) -> Option<ClipMode> {
        self.as_str().and_then(StringEnum::from_str)
    }

    fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference> {
        match self.as_i64() {
            Some(n) => Some(FilterOpGraphPictureReference{
                buffer_id: FilterOpGraphPictureBufferId::BufferId(n as i16),
            }),
            None => None,
        }
    }

    fn as_filter_op(&self) -> Option<FilterOp> {
        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<f32> = 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<f32> = 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<f32> = 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<f32> = 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<f32> = 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<f32> = 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<f32> = 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<Vec<FilterOp>> {
        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<FilterData> {
        // 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<ComponentTransferFuncType> =
                    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<FilterPrimitiveInput> {
        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<Vec<FilterData>> {
        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<FilterPrimitive> {
        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<f32> = 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<Vec<FilterPrimitive>> {
        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<ColorSpace> {
        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::<Vec<_>>();
        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::<Vec<_>>();
        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::<Vec<_>>();
        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<ComplexClipRegion> {
        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<Rotation> {
        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
            }
        }
    }
}

[ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge