/* 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<LayoutTr
ansform>;
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_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 .0 f32 * 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 {
"identity" => {
LayoutTransform::identity()
}
"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,
shape_top_left: 1 .0 ,
shape_top_right: 1 .0 ,
shape_bottom_left: 1 .0 ,
shape_bottom_right: 1 .0 ,
})
}
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,
shape_top_left: 1 .0 ,
shape_top_right: 1 .0 ,
shape_bottom_left: 1 .0 ,
shape_bottom_right: 1 .0 ,
})
}
_ => {
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_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_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
}
}
}
}
Messung V0.5 in Prozent C=97 H=100 G=98
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet am 2026-08-25)
¤
*© Formatika GbR, Deutschland