Compare commits

...

2 commits

5 changed files with 292 additions and 14 deletions

View file

@ -5,7 +5,44 @@ mod complex;
mod gaussian_blur; mod gaussian_blur;
mod offset; mod offset;
mod component_transfer {} mod component_transfer {
use crate::{
codegen::SvgDocument,
types::nodes::primitives::{
component_transfer::{ComponentTransfer, TransferFn},
FePrimitive,
},
Node,
};
#[test]
fn test_comp_trans_simple() {
let mut doc = SvgDocument::new();
let comptrans = doc.create_filter("comp_trans");
comptrans.add_node(Node::simple(FePrimitive::ComponentTransfer(
ComponentTransfer {
func_r: TransferFn::Table {
table_values: vec![0., 0.1, 0.4, 0.9],
},
func_g: TransferFn::Discrete {
table_values: vec![0.1, 0.3, 0.5, 0.7, 0.9],
},
func_b: TransferFn::Linear {
slope: 1.0,
intercept: 0.75,
},
func_a: TransferFn::Identity,
},
)));
assert_eq!(
doc.generate_svg(),
r#"<svg><filter id="comp_trans"><feComponentTransfer><feFuncR type="table" tableValues="0 0.1 0.4 0.9"/><feFuncG type="discrete" tableValues="0.1 0.3 0.5 0.7 0.9"/><feFuncB type="linear" slope="1" intercept="0.75"/><feFuncA type="identity"/></feComponentTransfer></filter></svg>"#
);
}
}
mod composite {} mod composite {}
mod convolve_matrix {} mod convolve_matrix {}
mod diffuse_lighting {} mod diffuse_lighting {}

View file

@ -139,10 +139,12 @@ impl FilterGraph {
} }
pub mod abstracted_inputs { pub mod abstracted_inputs {
use petgraph::prelude::NodeIndex; use petgraph::{data::Build, prelude::NodeIndex};
use crate::{ use crate::{
types::nodes::primitives::{blend::BlendMode, color_matrix::ColorMatrixType}, types::nodes::primitives::{
blend::BlendMode, color_matrix::ColorMatrixType, component_transfer::TransferFn,
},
Node, Node,
}; };
@ -211,6 +213,82 @@ pub mod abstracted_inputs {
.add_edge(self.resolve_input(in2.into()), node_idx, ()); .add_edge(self.resolve_input(in2.into()), node_idx, ());
node_idx node_idx
} }
pub fn component_transfer_rgba(
&mut self,
r#in: impl Into<NodeInput>,
r: TransferFn,
g: TransferFn,
b: TransferFn,
a: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_rgba(r, g, b, a));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
pub fn component_transfer_rgb(
&mut self,
r#in: impl Into<NodeInput>,
r: TransferFn,
g: TransferFn,
b: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_rgb(r, g, b));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
pub fn component_transfer_r(
&mut self,
r#in: impl Into<NodeInput>,
func: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_r(func));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
pub fn component_transfer_g(
&mut self,
r#in: impl Into<NodeInput>,
func: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_g(func));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
pub fn component_transfer_b(
&mut self,
r#in: impl Into<NodeInput>,
func: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_b(func));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
pub fn component_transfer_a(
&mut self,
r#in: impl Into<NodeInput>,
func: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_a(func));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
pub fn component_transfer_single(
&mut self,
r#in: impl Into<NodeInput>,
func: TransferFn,
) -> NodeIndex {
let node_idx = self.dag.add_node(Node::component_transfer_single(func));
self.dag
.add_edge(self.resolve_input(r#in.into()), node_idx, ());
node_idx
}
} }
} }

View file

@ -150,4 +150,48 @@ impl Node {
func_a: a, func_a: a,
})) }))
} }
pub fn component_transfer_rgb(r: TransferFn, g: TransferFn, b: TransferFn) -> Self {
Self::component_transfer_rgba(r, g, b, TransferFn::Identity)
}
pub fn component_transfer_r(func: TransferFn) -> Self {
Self::component_transfer_rgba(
func,
TransferFn::Identity,
TransferFn::Identity,
TransferFn::Identity,
)
}
pub fn component_transfer_g(func: TransferFn) -> Self {
Self::component_transfer_rgba(
TransferFn::Identity,
func,
TransferFn::Identity,
TransferFn::Identity,
)
}
pub fn component_transfer_b(func: TransferFn) -> Self {
Self::component_transfer_rgba(
TransferFn::Identity,
TransferFn::Identity,
func,
TransferFn::Identity,
)
}
pub fn component_transfer_a(func: TransferFn) -> Self {
Self::component_transfer_rgba(
TransferFn::Identity,
TransferFn::Identity,
TransferFn::Identity,
func,
)
}
pub fn component_transfer_single(func: TransferFn) -> Self {
Self::component_transfer_rgb(func.clone(), func.clone(), func)
}
} }

View file

@ -1,6 +1,5 @@
use std::convert::Into;
use quick_xml::{events::attributes::Attribute, ElementWriter, Writer}; use quick_xml::{events::attributes::Attribute, ElementWriter, Writer};
use std::convert::Into;
use super::CommonAttrs; use super::CommonAttrs;
@ -84,20 +83,20 @@ impl WriteElement for FePrimitive {
match self { match self {
FePrimitive::Blend(el) => el.attrs(), FePrimitive::Blend(el) => el.attrs(),
FePrimitive::ColorMatrix(el) => el.attrs(), FePrimitive::ColorMatrix(el) => el.attrs(),
FePrimitive::ComponentTransfer(_) => todo!(), FePrimitive::ComponentTransfer(el) => el.attrs(),
FePrimitive::Composite(el) => el.attrs(), FePrimitive::Composite(el) => el.attrs(),
FePrimitive::GaussianBlur(el) => el.attrs(),
FePrimitive::Offset(el) => el.attrs(),
FePrimitive::Turbulence(el) => el.attrs(),
FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::ConvolveMatrix(_) => todo!(),
FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(),
FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(),
FePrimitive::Flood(_) => todo!(), FePrimitive::Flood(_) => todo!(),
FePrimitive::GaussianBlur(el) => el.attrs(),
FePrimitive::Image(_) => todo!(), FePrimitive::Image(_) => todo!(),
FePrimitive::Merge(_) => todo!(), FePrimitive::Merge(_) => todo!(),
FePrimitive::Morphology(_) => todo!(), FePrimitive::Morphology(_) => todo!(),
FePrimitive::Offset(el) => el.attrs(),
FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::SpecularLighting(_) => todo!(),
FePrimitive::Tile(_) => todo!(), FePrimitive::Tile(_) => todo!(),
FePrimitive::Turbulence(el) => el.attrs(),
} }
} }
@ -105,20 +104,47 @@ impl WriteElement for FePrimitive {
match self { match self {
FePrimitive::Blend(el) => el.tag_name(), FePrimitive::Blend(el) => el.tag_name(),
FePrimitive::ColorMatrix(el) => el.tag_name(), FePrimitive::ColorMatrix(el) => el.tag_name(),
FePrimitive::ComponentTransfer(_) => todo!(), FePrimitive::ComponentTransfer(el) => el.tag_name(),
FePrimitive::Composite(el) => el.tag_name(), FePrimitive::Composite(el) => el.tag_name(),
FePrimitive::GaussianBlur(el) => el.tag_name(),
FePrimitive::Offset(el) => el.tag_name(),
FePrimitive::Turbulence(el) => el.tag_name(),
FePrimitive::ConvolveMatrix(_) => todo!(),
FePrimitive::DiffuseLighting(_) => todo!(),
FePrimitive::DisplacementMap(_) => todo!(),
FePrimitive::Flood(_) => todo!(),
FePrimitive::Image(_) => todo!(),
FePrimitive::Merge(_) => todo!(),
FePrimitive::Morphology(_) => todo!(),
FePrimitive::SpecularLighting(_) => todo!(),
FePrimitive::Tile(_) => todo!(),
}
}
fn element_writer<'writer, 'result>(
&self,
writer: &'writer mut Writer<&'result mut Vec<u8>>,
common: CommonAttrs,
inputs: Vec<String>,
output: Option<String>,
) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec<u8>>> {
match self {
FePrimitive::Blend(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::ColorMatrix(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::ComponentTransfer(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::Composite(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::Turbulence(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::GaussianBlur(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output),
FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::ConvolveMatrix(_) => todo!(),
FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(),
FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(),
FePrimitive::Flood(_) => todo!(), FePrimitive::Flood(_) => todo!(),
FePrimitive::GaussianBlur(el) => el.tag_name(),
FePrimitive::Image(_) => todo!(), FePrimitive::Image(_) => todo!(),
FePrimitive::Merge(_) => todo!(), FePrimitive::Merge(_) => todo!(),
FePrimitive::Morphology(_) => todo!(), FePrimitive::Morphology(_) => todo!(),
FePrimitive::Offset(el) => el.tag_name(),
FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::SpecularLighting(_) => todo!(),
FePrimitive::Tile(_) => todo!(), FePrimitive::Tile(_) => todo!(),
FePrimitive::Turbulence(el) => el.tag_name(),
} }
} }
} }

View file

@ -1,3 +1,5 @@
use quick_xml::{events::attributes::Attribute, Writer};
use super::WriteElement; use super::WriteElement;
/// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) /// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement)
@ -17,10 +19,48 @@ impl WriteElement for ComponentTransfer {
fn tag_name(&self) -> &'static str { fn tag_name(&self) -> &'static str {
"feComponentTransfer" "feComponentTransfer"
} }
fn element_writer<'writer, 'result>(
&self,
writer: &'writer mut quick_xml::Writer<&'result mut Vec<u8>>,
common: crate::types::nodes::CommonAttrs,
inputs: Vec<String>,
output: Option<String>,
) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec<u8>>> {
let inputs: Vec<_> = inputs
.into_iter()
.enumerate()
.map(|(i, edge)| {
(
match i {
0 => "in".to_owned(),
n => format!("in{}", n + 1),
}
.into_bytes(),
edge.into_bytes(),
)
})
.collect();
let mut el_writer = writer
.create_element(self.tag_name())
.with_attributes(inputs.iter().map(|(k, v)| (&k[..], &v[..])))
.with_attributes(Into::<Vec<Attribute<'_>>>::into(common));
if let Some(output) = output {
el_writer = el_writer.with_attribute(("result", output.as_str()));
}
el_writer.write_inner_content(|writer| {
self.func_r.write_self(writer, "feFuncR")?;
self.func_g.write_self(writer, "feFuncG")?;
self.func_b.write_self(writer, "feFuncB")?;
self.func_a.write_self(writer, "feFuncA")?;
Ok(())
})
}
} }
/// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements)
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum TransferFn { pub enum TransferFn {
Identity, Identity,
Table { Table {
@ -39,3 +79,56 @@ pub enum TransferFn {
offset: f32, offset: f32,
}, },
} }
impl TransferFn {
#[allow(clippy::str_to_string, reason = "inside macro call")]
fn write_self<'writer, 'result>(
&self,
target: &'writer mut Writer<&'result mut Vec<u8>>,
name: &'static str,
) -> quick_xml::Result<&'writer mut Writer<&'result mut Vec<u8>>> {
target
.create_element(name)
.with_attributes(match self {
TransferFn::Identity => gen_attrs![b"type": "identity"],
TransferFn::Table { table_values } => gen_attrs![
b"type": "table",
b"tableValues": table_values
.iter()
.map(std::string::ToString::to_string)
.reduce(|mut acc, e| {
acc.push(' ');
acc.push_str(&e);
acc
}).expect("empty tables disallowed")
],
TransferFn::Discrete { table_values } => gen_attrs![
b"type": "discrete",
b"tableValues": table_values
.iter()
.map(std::string::ToString::to_string)
.reduce(|mut acc, e| {
acc.push(' ');
acc.push_str(&e);
acc
}).expect("empty tables disallowed")
],
TransferFn::Linear { slope, intercept } => gen_attrs![
b"type": "linear",
b"slope": slope,
b"intercept": intercept
],
TransferFn::Gamma {
amplitude,
exponent,
offset,
} => gen_attrs![
b"type": "gamma",
b"amplitude": amplitude,
b"exponent": exponent,
b"offset": offset
],
})
.write_empty()
}
}