svg-filters & basic parser #15

Merged
schrottkatze merged 67 commits from schrottkatze/iowo:svg-filters into main 2024-07-08 18:29:05 +00:00
25 changed files with 340 additions and 69 deletions
Showing only changes of commit 56848a1b05 - Show all commits

View file

@ -3,7 +3,8 @@ members = [
"crates/app", "crates/app",
"crates/eval", "crates/eval",
"crates/ir", "crates/ir",
"crates/lang", "crates/svg-filters", "crates/lang",
"crates/svg-filters",
] ]
resolver = "2" resolver = "2"

View file

@ -2,6 +2,7 @@
name = "app" name = "app"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
default-run = "app"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,16 +1,5 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
pub mod types; pub mod types;
#[cfg(test)] pub use types::nodes::Node;
mod tests { pub use types::Edge;
use super::*; pub use types::Filter;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View file

@ -0,0 +1,54 @@
use svg_filters::{types::nodes::primitives::color_matrix::ColorMatrixType, Edge, Filter, Node};
fn main() {
let mut filter = Filter::new();
// <filter id="chromabb" >
// <feColorMatrix values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceGraphic" />
// <feOffset dx="25" dy="0" />
// <feGaussianBlur stdDeviation="5 0" result="red" />
// <feColorMatrix values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="grn" in="SourceGraphic" />
// <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" in="SourceGraphic" />
// <feOffset dx="-25" dy="0" />
// <feGaussianBlur stdDeviation="5 0" result="blu"/>
// <feComposite in="red" in2="blu" operator="arithmetic" k2="1" k3="1" result="rb" />
// <feComposite in="rb" in2="grn" operator="arithmetic" k2="1" k3="1" />
// </filter>
let chan_r = filter.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.,
]))));
let chan_g = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([
0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
]))));
let chan_b = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,
]))));
let offset_r = filter.add_node(Node::offset(25., 0.));
let offset_b = filter.add_node(Node::offset(-25., 0.));
let blur_r = filter.add_node(Node::gaussian_blur_xy(5, 0));
let blur_b = filter.add_node(Node::gaussian_blur_xy(5, 0));
let composite_rb = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.));
let composite_final = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.));
filter.graph.extend_with_edges(&[
(filter.source_graphic(), chan_r, Edge::new()),
(filter.source_graphic(), chan_b, Edge::new()),
(filter.source_graphic(), chan_g, Edge::new()),
(chan_r, offset_r, Edge::new()),
(offset_r, blur_r, Edge::new()),
(chan_b, offset_b, Edge::new()),
(offset_b, blur_b, Edge::new()),
(blur_r, composite_rb, Edge::in_idx(0)),
(blur_b, composite_rb, Edge::in_idx(1)),
(composite_rb, composite_final, Edge::in_idx(0)),
(chan_g, composite_final, Edge::in_idx(1)),
]);
println!("{filter:#?}")
}

View file

@ -1,20 +1,80 @@
use std::collections::HashMap; use std::collections::HashMap;
use petgraph::graphmap::DiGraphMap; use petgraph::{
adj::EdgeIndex, data::Build, graph::DiGraph, graphmap::DiGraphMap, prelude::NodeIndex,
};
pub mod length; pub mod length;
pub mod nodes; pub mod nodes;
use crate::types::nodes::primitives::color_matrix::{ColorMatrix, ColorMatrixType};
use self::{ use self::{
length::{Coordinate, Length}, length::{Coordinate, Length},
nodes::Node, nodes::Node,
}; };
#[derive(Debug)]
pub struct Filter<'a> { pub struct Filter<'a> {
graph: DiGraphMap<Node, &'a str>, pub graph: DiGraph<Node, Edge<'a>>,
source_graphic_idx: NodeIndex,
} }
pub enum Edge<'a> {
Named(&'a str, u8), impl Filter<'_> {
pub fn new() -> Self {
let mut graph = DiGraph::new();
let source_graphic_idx = graph.add_node(Node::StdInput(
nodes::standard_input::StandardInput::SourceGraphic,
));
Self {
graph,
source_graphic_idx,
}
}
pub fn add_node(&mut self, node: Node) -> NodeIndex {
self.graph.add_node(node)
}
pub fn source_graphic(&self) -> NodeIndex {
self.source_graphic_idx
}
}
impl Default for Filter<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Edge<'a> {
edge_type: EdgeType<'a>,
/// the index of the `in` attribute on the target element
/// if None, just `in`
in_idx: Option<u8>,
}
impl Edge<'_> {
pub fn new() -> Self {
Self {
edge_type: EdgeType::Unnamed,
in_idx: None,
}
}
pub fn in_idx(idx: u8) -> Self {
Self {
edge_type: EdgeType::Unnamed,
in_idx: Some(idx),
}
}
}
#[derive(Debug, Clone)]
pub enum EdgeType<'a> {
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(u8), Unnamed,
} }

View file

@ -1,7 +1,10 @@
#[derive(Default, Debug)]
pub struct Length(f32, Unit); pub struct Length(f32, Unit);
pub type Coordinate = Length; pub type Coordinate = Length;
#[derive(Default, Debug)]
pub enum Unit { pub enum Unit {
#[default]
None, None,
Em, Em,
Ex, Ex,

View file

@ -1,13 +1,113 @@
use self::{primitives::Primitive, standard_input::StandardInput}; use self::{
primitives::{
blend::{Blend, BlendMode},
color_matrix::{ColorMatrix, ColorMatrixType},
composite::{Composite, CompositeOperator},
gaussian_blur::GaussianBlur,
offset::Offset,
FePrimitive,
},
standard_input::StandardInput,
};
use super::length::{Coordinate, Length}; use super::length::{Coordinate, Length};
pub mod primitives;
pub mod standard_input; pub mod standard_input;
pub mod primitives; #[derive(Debug)]
pub enum Node { pub enum Node {
/// We represent those as Nodes because they're essentially magical nodes StdInput(StandardInput),
StandardInput(StandardInput), Primitive {
Primitive(Primitive), primitive: FePrimitive,
common_attrs: CommonAttrs,
},
}
impl Default for Node {
fn default() -> Self {
Self::StdInput(StandardInput::SourceGraphic)
}
}
#[derive(Default, Debug)]
struct CommonAttrs {
x: Coordinate,
y: Coordinate,
width: Length,
height: Length,
}
impl Node {
pub fn simple(el: FePrimitive) -> Node {
Node::Primitive {
primitive: el,
common_attrs: CommonAttrs::default(),
}
}
pub fn input_count(&self) -> u8 {
match self {
Node::Primitive {
primitive:
FePrimitive::ColorMatrix(_)
| FePrimitive::ComponentTransfer(_)
| FePrimitive::ConvolveMatrix(_)
| FePrimitive::DiffuseLighting(_)
| FePrimitive::GaussianBlur(_)
| FePrimitive::Morphology(_)
| FePrimitive::Offset(_)
| FePrimitive::SpecularLighting(_)
| FePrimitive::Tile(_),
..
} => 1,
Node::Primitive {
primitive:
FePrimitive::Composite(_) | FePrimitive::Blend(_) | FePrimitive::DisplacementMap(_),
..
} => 2,
Node::StdInput(_)
| Node::Primitive {
primitive:
FePrimitive::Flood(_) | FePrimitive::Image(_) | FePrimitive::Turbulence(_),
..
} => 0,
Node::Primitive {
primitive: FePrimitive::Merge(_),
..
} => todo!(),
}
}
pub fn blend(mode: BlendMode) -> Self {
Self::simple(FePrimitive::Blend(Blend::new(mode)))
}
pub fn color_matrix(cm_type: ColorMatrixType) -> Self {
Self::simple(FePrimitive::ColorMatrix(ColorMatrix::new(cm_type)))
}
pub fn composite(op: CompositeOperator) -> Self {
Self::simple(FePrimitive::Composite(Composite::new(op)))
}
pub fn composite_arithmetic(k1: f32, k2: f32, k3: f32, k4: f32) -> Self {
Self::simple(FePrimitive::Composite(Composite::arithmetic(
k1, k2, k3, k4,
)))
}
pub fn gaussian_blur(v: u16) -> Self {
Self::simple(FePrimitive::GaussianBlur(GaussianBlur::single(v)))
}
pub fn gaussian_blur_xy(x: u16, y: u16) -> Self {
Self::simple(FePrimitive::GaussianBlur(GaussianBlur::with_xy(x, y)))
}
pub fn offset(dx: f32, dy: f32) -> Self {
Self::simple(FePrimitive::Offset(Offset::new(dx, dy)))
}
} }

View file

@ -1,36 +1,29 @@
use crate::types::length::{Coordinate, Length}; use crate::types::length::{Coordinate, Length};
mod blend; use self::blend::BlendMode;
mod color_matrix;
mod component_transfer;
mod composite;
mod convolve_matrix;
mod diffuse_lighting;
mod displacement_map;
mod flood;
mod gaussian_blur;
mod image;
mod merge;
mod morphology;
mod offset;
mod specular_lighting;
mod tile;
mod turbulence;
pub struct Primitive { use super::Node;
primitive: FePrimitive,
common_attrs: CommonAttrs,
}
struct CommonAttrs { pub mod blend;
x: Coordinate, pub mod color_matrix;
y: Coordinate, pub mod component_transfer;
width: Length, pub mod composite;
height: Length, pub mod convolve_matrix;
} pub mod diffuse_lighting;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod specular_lighting;
pub mod tile;
pub mod turbulence;
/// svg filter effects primitives /// svg filter effects primitives
enum FePrimitive { #[derive(Debug)]
pub enum FePrimitive {
// 2 inputs // 2 inputs
Blend(blend::Blend), Blend(blend::Blend),
ColorMatrix(color_matrix::ColorMatrix), ColorMatrix(color_matrix::ColorMatrix),

View file

@ -1,10 +1,26 @@
/// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement) /// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement)
pub struct Blend { #[derive(Debug)]
pub(in crate::types::nodes) struct Blend {
mode: BlendMode, mode: BlendMode,
} }
impl Blend {
pub fn new(mode: BlendMode) -> Self {
Self { mode }
}
}
impl Default for Blend {
fn default() -> Self {
Self {
mode: BlendMode::Normal,
}
}
}
/// as according to https://drafts.fxtf.org/compositing-1/#blending /// as according to https://drafts.fxtf.org/compositing-1/#blending
enum BlendMode { #[derive(Debug)]
pub enum BlendMode {
Normal, Normal,
Multiply, Multiply,
Screen, Screen,

View file

@ -1,19 +1,19 @@
/// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement) /// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement)
#[derive(Debug)]
pub struct ColorMatrix { pub struct ColorMatrix {
cm_type: ColorMatrixType, cm_type: ColorMatrixType,
} }
enum ColorMatrixType { impl ColorMatrix {
Matrix { pub fn new(cm_type: ColorMatrixType) -> Self {
values: [f32; 20], Self { cm_type }
}, }
Saturate { }
// is the values attribute anyway tho
value: f32, #[derive(Debug)]
}, pub enum ColorMatrixType {
HueRotate { Matrix(Box<[f32; 20]>),
// also the values attribute Saturate(f32),
degrees: f32, HueRotate(f32),
},
LuminanceToAlpha, LuminanceToAlpha,
} }

View file

@ -1,4 +1,5 @@
/// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) /// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement)
#[derive(Debug)]
pub struct ComponentTransfer { pub struct ComponentTransfer {
func_r: TransferFunction, func_r: TransferFunction,
func_g: TransferFunction, func_g: TransferFunction,
@ -7,6 +8,7 @@ pub struct ComponentTransfer {
} }
/// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements)
#[derive(Debug)]
enum TransferFunction { enum TransferFunction {
Identity, Identity,
Table { Table {

View file

@ -1,9 +1,23 @@
/// [feComposite](https://www.w3.org/TR/SVG11/filters.html#feCompositeElement) /// [feComposite](https://www.w3.org/TR/SVG11/filters.html#feCompositeElement)
#[derive(Debug)]
pub struct Composite { pub struct Composite {
operator: CompositeOperator, operator: CompositeOperator,
} }
enum CompositeOperator { impl Composite {
pub fn new(op: CompositeOperator) -> Self {
Self { operator: op }
}
pub fn arithmetic(k1: f32, k2: f32, k3: f32, k4: f32) -> Self {
Self {
operator: CompositeOperator::Arithmetic { k1, k2, k3, k4 },
}
}
}
#[derive(Debug)]
pub enum CompositeOperator {
Over, Over,
In, In,
Out, Out,

View file

@ -1,3 +1,4 @@
#[derive(Debug)]
pub struct ConvolveMatrix { pub struct ConvolveMatrix {
order: (u16, u16), order: (u16, u16),
// must be checked to be `order.0 * order.1` // must be checked to be `order.0 * order.1`
@ -11,6 +12,7 @@ pub struct ConvolveMatrix {
preserve_alpha: bool, preserve_alpha: bool,
} }
#[derive(Debug)]
enum EdgeMode { enum EdgeMode {
None, None,
Duplicate, Duplicate,

View file

@ -1,2 +1,3 @@
// TODO // TODO
#[derive(Debug)]
pub struct DiffuseLighting; pub struct DiffuseLighting;

View file

@ -1,10 +1,12 @@
/// [feDisplacementMap](https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement) /// [feDisplacementMap](https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement)
#[derive(Debug)]
pub struct DisplacementMap { pub struct DisplacementMap {
scale: f32, scale: f32,
x_channel_selector: Channel, x_channel_selector: Channel,
y_channel_selector: Channel, y_channel_selector: Channel,
} }
#[derive(Debug)]
enum Channel { enum Channel {
A, A,
R, R,

View file

@ -1,6 +1,7 @@
use csscolorparser::Color; use csscolorparser::Color;
/// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) /// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement)
#[derive(Debug)]
pub struct Flood { pub struct Flood {
flood_color: Color, flood_color: Color,
flood_opacity: f32, flood_opacity: f32,

View file

@ -1,4 +1,19 @@
/// [feGaussianBlur](https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement) /// [feGaussianBlur](https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement)
#[derive(Debug)]
pub struct GaussianBlur { pub struct GaussianBlur {
std_deviation: (f32, f32), std_deviation: (u16, u16),
}
impl GaussianBlur {
pub fn single(v: u16) -> Self {
Self {
std_deviation: (v, v),
}
}
pub fn with_xy(x: u16, y: u16) -> Self {
Self {
std_deviation: (x, y),
}
}
} }

View file

@ -1,2 +1,3 @@
// TODO // TODO
#[derive(Debug)]
pub struct Image; pub struct Image;

View file

@ -1,2 +1,3 @@
// TODO // TODO
#[derive(Debug)]
pub struct Merge; pub struct Merge;

View file

@ -1,9 +1,11 @@
/// [feMorphology](https://www.w3.org/TR/SVG11/filters.html#feMorphologyElement) /// [feMorphology](https://www.w3.org/TR/SVG11/filters.html#feMorphologyElement)
#[derive(Debug)]
pub struct Morphology { pub struct Morphology {
operator: Operator, operator: Operator,
radius: (f32, f32), radius: (f32, f32),
} }
#[derive(Debug)]
enum Operator { enum Operator {
Erode, Erode,
Dilate, Dilate,

View file

@ -1,5 +1,12 @@
/// [feOffset](https://www.w3.org/TR/SVG11/filters.html#feOffsetElement) /// [feOffset](https://www.w3.org/TR/SVG11/filters.html#feOffsetElement)
#[derive(Debug)]
pub struct Offset { pub struct Offset {
dx: f32, dx: f32,
dy: f32, dy: f32,
} }
impl Offset {
pub fn new(dx: f32, dy: f32) -> Self {
Self { dx, dy }
}
}

View file

@ -1,2 +1,3 @@
// TODO // TODO
#[derive(Debug)]
pub struct SpecularLighting; pub struct SpecularLighting;

View file

@ -1,2 +1,3 @@
/// [feTile](https://www.w3.org/TR/SVG11/filters.html#feTileElement) /// [feTile](https://www.w3.org/TR/SVG11/filters.html#feTileElement)
#[derive(Debug)]
pub struct Tile; pub struct Tile;

View file

@ -1,4 +1,5 @@
/// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement) /// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement)
#[derive(Debug)]
pub struct Turbulence { pub struct Turbulence {
base_frequency: (f32, f32), base_frequency: (f32, f32),
num_octaves: (u16), num_octaves: (u16),
@ -8,10 +9,12 @@ pub struct Turbulence {
noise_type: NoiseType, noise_type: NoiseType,
} }
#[derive(Debug)]
enum StitchTiles { enum StitchTiles {
Stitch, Stitch,
NoStitch, NoStitch,
} }
#[derive(Debug)]
enum NoiseType { enum NoiseType {
Turbulence, Turbulence,
FractalNoise, FractalNoise,

View file

@ -1,5 +1,6 @@
/// [svg filter effect standard input](https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute) /// [svg filter effect standard input](https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute)
/// technically not a node, but for implementation simplicity... yeah /// technically not a node, but for implementation simplicity... yeah
#[derive(Debug)]
pub enum StandardInput { pub enum StandardInput {
SourceGraphic, SourceGraphic,
SourceAlpha, SourceAlpha,