Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/wr/wrench/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 54 kB image not shown  

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.22 Sekunden  (vorverarbeitet)  ]