svg-filters & basic parser #15
6 changed files with 227 additions and 9 deletions
|
@ -1,6 +1,20 @@
|
||||||
use svg_filters::{types::nodes::primitives::color_matrix::ColorMatrixType, Edge, Filter, Node};
|
use svg_filters::{
|
||||||
|
types::nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput},
|
||||||
|
Edge, Filter, Node,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let mut supersimple = Filter::new();
|
||||||
|
|
||||||
|
let cm = supersimple.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([
|
||||||
|
1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
|
||||||
|
]))));
|
||||||
|
|
||||||
|
supersimple
|
||||||
|
.graph
|
||||||
|
.extend_with_edges(&[(supersimple.source_graphic(), cm)]);
|
||||||
|
dbg!(supersimple.to_svg());
|
||||||
|
|
||||||
let mut filter = Filter::new();
|
let mut filter = Filter::new();
|
||||||
|
|
||||||
// <filter id="chromabb" >
|
// <filter id="chromabb" >
|
||||||
|
@ -50,5 +64,5 @@ fn main() {
|
||||||
(chan_g, composite_final, Edge::in_idx(1)),
|
(chan_g, composite_final, Edge::in_idx(1)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
println!("{filter:#?}")
|
println!("Result: {}", filter.to_svg())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::{borrow::Cow, collections::HashMap, fmt::Debug, io::BufWriter, primitive};
|
||||||
|
|
||||||
use petgraph::{
|
use petgraph::{
|
||||||
adj::EdgeIndex, data::Build, graph::DiGraph, graphmap::DiGraphMap, prelude::NodeIndex,
|
adj::EdgeIndex,
|
||||||
|
algo::{toposort, DfsSpace},
|
||||||
|
data::{Build, DataMap},
|
||||||
|
graph::DiGraph,
|
||||||
|
graphmap::DiGraphMap,
|
||||||
|
prelude::NodeIndex,
|
||||||
|
visit::NodeIndexable,
|
||||||
};
|
};
|
||||||
|
use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter};
|
||||||
|
|
||||||
pub mod length;
|
pub mod length;
|
||||||
pub mod nodes;
|
pub mod nodes;
|
||||||
|
@ -11,7 +18,10 @@ use crate::types::nodes::primitives::color_matrix::{ColorMatrix, ColorMatrixType
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
length::{Coordinate, Length},
|
length::{Coordinate, Length},
|
||||||
nodes::Node,
|
nodes::{
|
||||||
|
primitives::{FePrimitive, WriteElement},
|
||||||
|
Node,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -40,6 +50,52 @@ impl Filter<'_> {
|
||||||
pub fn source_graphic(&self) -> NodeIndex {
|
pub fn source_graphic(&self) -> NodeIndex {
|
||||||
self.source_graphic_idx
|
self.source_graphic_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_svg(&self) -> String {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut writer = quick_xml::Writer::new(&mut result);
|
||||||
|
|
||||||
|
let mut dfs_space = DfsSpace::new(&self.graph);
|
||||||
|
let sorted = toposort(&self.graph, Some(&mut dfs_space)).expect("No cycles! Bad user!");
|
||||||
|
|
||||||
|
sorted
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|node_idx| {
|
||||||
|
let node = self
|
||||||
|
.graph
|
||||||
|
.node_weight(node_idx)
|
||||||
|
.expect("toposorting will not return invalid indices");
|
||||||
|
|
||||||
|
if let Node::Primitive {
|
||||||
|
primitive,
|
||||||
|
common_attrs,
|
||||||
|
} = node
|
||||||
|
{
|
||||||
|
Some((node_idx, primitive, common_attrs))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(
|
||||||
|
&mut writer,
|
||||||
|
|writer, (node_idx, primitive, common_attrs)| {
|
||||||
|
let el_writer = primitive.element_writer(writer);
|
||||||
|
create_input_elements(
|
||||||
|
&self.graph,
|
||||||
|
el_writer,
|
||||||
|
node_idx,
|
||||||
|
self.graph
|
||||||
|
.node_weight(node_idx)
|
||||||
|
.expect("cannot get invalid node_idx from toposort")
|
||||||
|
.input_count(),
|
||||||
|
)
|
||||||
|
.write_empty()
|
||||||
|
.expect("should write successfully")
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
String::from_utf8_lossy(&result).to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Filter<'_> {
|
impl Default for Filter<'_> {
|
||||||
|
@ -50,10 +106,10 @@ impl Default for Filter<'_> {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Edge<'a> {
|
pub struct Edge<'a> {
|
||||||
edge_type: EdgeType<'a>,
|
pub edge_type: EdgeType<'a>,
|
||||||
/// the index of the `in` attribute on the target element
|
/// the index of the `in` attribute on the target element
|
||||||
/// if None, just `in`
|
/// if None, just `in`
|
||||||
in_idx: Option<u8>,
|
pub in_idx: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edge<'_> {
|
impl Edge<'_> {
|
||||||
|
@ -72,9 +128,60 @@ impl Edge<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Edge<'_> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum EdgeType<'a> {
|
pub enum EdgeType<'a> {
|
||||||
Named(&'a str),
|
Named(&'a str),
|
||||||
/// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity
|
/// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity
|
||||||
Unnamed,
|
Unnamed,
|
||||||
}
|
}
|
||||||
|
fn create_input_elements<'a>(
|
||||||
|
g: &'a DiGraph<Node, Edge<'_>>,
|
||||||
|
mut el_writer: ElementWriter<'a, &'a mut Vec<u8>>,
|
||||||
|
node_idx: NodeIndex,
|
||||||
|
input_count: u8,
|
||||||
|
) -> ElementWriter<'a, &'a mut Vec<u8>> {
|
||||||
|
let inputs = g
|
||||||
|
.neighbors_directed(node_idx, petgraph::Direction::Incoming)
|
||||||
|
.collect::<Vec<NodeIndex>>();
|
||||||
|
|
||||||
|
if inputs.len() != input_count as usize {
|
||||||
|
// TODO: better error handling
|
||||||
|
panic!("input couns didnt match");
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(el_writer, |el_writer, (i, incoming_idx)| {
|
||||||
|
let incoming_node = g.node_weight(incoming_idx).expect("cannot fail here");
|
||||||
|
|
||||||
|
// find incoming edge and get weight
|
||||||
|
let Edge { edge_type, in_idx } = g
|
||||||
|
.edge_weight(
|
||||||
|
g.find_edge(incoming_idx, node_idx)
|
||||||
|
.expect("there should always be an edge"),
|
||||||
|
)
|
||||||
|
.expect("once again, should always exist");
|
||||||
|
|
||||||
|
let in_attr_name = match in_idx {
|
||||||
|
None | Some(0) => "in".to_owned(),
|
||||||
|
Some(n) => format!("in{}", n + 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
let v = match incoming_node {
|
||||||
|
Node::StdInput(std_in) => format!("{std_in:?}"),
|
||||||
|
Node::Primitive {
|
||||||
|
primitive,
|
||||||
|
common_attrs,
|
||||||
|
} => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
el_writer.with_attribute((in_attr_name.as_str(), v.as_str()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
use core::panic;
|
||||||
|
|
||||||
|
use quick_xml::events::attributes::Attribute;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
primitives::{
|
primitives::{
|
||||||
blend::{Blend, BlendMode},
|
blend::{Blend, BlendMode},
|
||||||
|
@ -31,7 +35,7 @@ impl Default for Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct CommonAttrs {
|
pub(crate) struct CommonAttrs {
|
||||||
x: Coordinate,
|
x: Coordinate,
|
||||||
y: Coordinate,
|
y: Coordinate,
|
||||||
width: Length,
|
width: Length,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use quick_xml::{events::attributes::Attribute, ElementWriter, Writer};
|
||||||
|
|
||||||
use crate::types::length::{Coordinate, Length};
|
use crate::types::length::{Coordinate, Length};
|
||||||
|
|
||||||
use self::blend::BlendMode;
|
use self::blend::BlendMode;
|
||||||
|
@ -21,6 +23,19 @@ pub mod specular_lighting;
|
||||||
pub mod tile;
|
pub mod tile;
|
||||||
pub mod turbulence;
|
pub mod turbulence;
|
||||||
|
|
||||||
|
pub trait WriteElement {
|
||||||
|
fn attrs(&self) -> Vec<Attribute>;
|
||||||
|
fn tag_name(&self) -> &'static str;
|
||||||
|
fn element_writer<'writer, 'result>(
|
||||||
|
&self,
|
||||||
|
writer: &'writer mut Writer<&'result mut Vec<u8>>,
|
||||||
|
) -> ElementWriter<'writer, &'result mut Vec<u8>> {
|
||||||
|
writer
|
||||||
|
.create_element(self.tag_name())
|
||||||
|
.with_attributes(self.attrs())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// svg filter effects primitives
|
/// svg filter effects primitives
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FePrimitive {
|
pub enum FePrimitive {
|
||||||
|
@ -42,3 +57,47 @@ pub enum FePrimitive {
|
||||||
Tile(tile::Tile),
|
Tile(tile::Tile),
|
||||||
Turbulence(turbulence::Turbulence),
|
Turbulence(turbulence::Turbulence),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteElement for FePrimitive {
|
||||||
|
fn attrs(&self) -> std::vec::Vec<quick_xml::events::attributes::Attribute<'_>> {
|
||||||
|
match self {
|
||||||
|
FePrimitive::Blend(_) => todo!(),
|
||||||
|
FePrimitive::ColorMatrix(cm) => cm.attrs(),
|
||||||
|
FePrimitive::ComponentTransfer(_) => todo!(),
|
||||||
|
FePrimitive::Composite(_) => todo!(),
|
||||||
|
FePrimitive::ConvolveMatrix(_) => todo!(),
|
||||||
|
FePrimitive::DiffuseLighting(_) => todo!(),
|
||||||
|
FePrimitive::DisplacementMap(_) => todo!(),
|
||||||
|
FePrimitive::Flood(_) => todo!(),
|
||||||
|
FePrimitive::GaussianBlur(_) => todo!(),
|
||||||
|
FePrimitive::Image(_) => todo!(),
|
||||||
|
FePrimitive::Merge(_) => todo!(),
|
||||||
|
FePrimitive::Morphology(_) => todo!(),
|
||||||
|
FePrimitive::Offset(_) => todo!(),
|
||||||
|
FePrimitive::SpecularLighting(_) => todo!(),
|
||||||
|
FePrimitive::Tile(_) => todo!(),
|
||||||
|
FePrimitive::Turbulence(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tag_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FePrimitive::Blend(_) => todo!(),
|
||||||
|
FePrimitive::ColorMatrix(cm) => cm.tag_name(),
|
||||||
|
FePrimitive::ComponentTransfer(_) => todo!(),
|
||||||
|
FePrimitive::Composite(_) => todo!(),
|
||||||
|
FePrimitive::ConvolveMatrix(_) => todo!(),
|
||||||
|
FePrimitive::DiffuseLighting(_) => todo!(),
|
||||||
|
FePrimitive::DisplacementMap(_) => todo!(),
|
||||||
|
FePrimitive::Flood(_) => todo!(),
|
||||||
|
FePrimitive::GaussianBlur(_) => todo!(),
|
||||||
|
FePrimitive::Image(_) => todo!(),
|
||||||
|
FePrimitive::Merge(_) => todo!(),
|
||||||
|
FePrimitive::Morphology(_) => todo!(),
|
||||||
|
FePrimitive::Offset(_) => todo!(),
|
||||||
|
FePrimitive::SpecularLighting(_) => todo!(),
|
||||||
|
FePrimitive::Tile(_) => todo!(),
|
||||||
|
FePrimitive::Turbulence(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement)
|
/// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(in crate::types::nodes) struct Blend {
|
pub struct Blend {
|
||||||
mode: BlendMode,
|
mode: BlendMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use quick_xml::{events::attributes::Attribute, name::QName, se::to_string};
|
||||||
|
|
||||||
|
use super::WriteElement;
|
||||||
|
|
||||||
/// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement)
|
/// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ColorMatrix {
|
pub struct ColorMatrix {
|
||||||
|
@ -10,6 +16,34 @@ impl ColorMatrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteElement for ColorMatrix {
|
||||||
|
fn attrs(&self) -> Vec<quick_xml::events::attributes::Attribute> {
|
||||||
|
match &self.cm_type {
|
||||||
|
ColorMatrixType::Matrix(v) => vec![Attribute {
|
||||||
|
key: QName(b"values"),
|
||||||
|
value: Cow::from(
|
||||||
|
v.iter()
|
||||||
|
.map(|v| v.to_string())
|
||||||
|
.reduce(|mut acc, e| {
|
||||||
|
acc.push(' ');
|
||||||
|
acc.push_str(&e);
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
.expect("Should be able to concatenate the thingies")
|
||||||
|
.into_bytes(),
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
ColorMatrixType::Saturate(v) => todo!(),
|
||||||
|
ColorMatrixType::HueRotate(v) => todo!(),
|
||||||
|
ColorMatrixType::LuminanceToAlpha => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tag_name(&self) -> &'static str {
|
||||||
|
"feColorMatrix"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ColorMatrixType {
|
pub enum ColorMatrixType {
|
||||||
Matrix(Box<[f32; 20]>),
|
Matrix(Box<[f32; 20]>),
|
||||||
|
|
Loading…
Reference in a new issue