svg-filters: add basic graph and stuffs

This commit is contained in:
Schrottkatze 2024-03-15 19:52:40 +01:00
parent 69f0baf425
commit 56848a1b05
Signed by: schrottkatze
SSH key fingerprint: SHA256:hXb3t1vINBFCiDCmhRABHX5ocdbLiKyCdKI4HK2Rbbc
25 changed files with 340 additions and 69 deletions

View file

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

View file

@ -2,6 +2,7 @@
name = "app"
version = "0.1.0"
edition = "2021"
default-run = "app"
# 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;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
pub use types::nodes::Node;
pub use types::Edge;
pub use types::Filter;

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 petgraph::graphmap::DiGraphMap;
use petgraph::{
adj::EdgeIndex, data::Build, graph::DiGraph, graphmap::DiGraphMap, prelude::NodeIndex,
};
pub mod length;
pub mod nodes;
use crate::types::nodes::primitives::color_matrix::{ColorMatrix, ColorMatrixType};
use self::{
length::{Coordinate, Length},
nodes::Node,
};
#[derive(Debug)]
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
Unnamed(u8),
Unnamed,
}

View file

@ -1,7 +1,10 @@
#[derive(Default, Debug)]
pub struct Length(f32, Unit);
pub type Coordinate = Length;
#[derive(Default, Debug)]
pub enum Unit {
#[default]
None,
Em,
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};
pub mod primitives;
pub mod standard_input;
pub mod primitives;
#[derive(Debug)]
pub enum Node {
/// We represent those as Nodes because they're essentially magical nodes
StandardInput(StandardInput),
Primitive(Primitive),
StdInput(StandardInput),
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};
mod blend;
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;
use self::blend::BlendMode;
pub struct Primitive {
primitive: FePrimitive,
common_attrs: CommonAttrs,
}
use super::Node;
struct CommonAttrs {
x: Coordinate,
y: Coordinate,
width: Length,
height: Length,
}
pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
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
enum FePrimitive {
#[derive(Debug)]
pub enum FePrimitive {
// 2 inputs
Blend(blend::Blend),
ColorMatrix(color_matrix::ColorMatrix),

View file

@ -1,10 +1,26 @@
/// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement)
pub struct Blend {
#[derive(Debug)]
pub(in crate::types::nodes) struct Blend {
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
enum BlendMode {
#[derive(Debug)]
pub enum BlendMode {
Normal,
Multiply,
Screen,

View file

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

View file

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

View file

@ -1,9 +1,23 @@
/// [feComposite](https://www.w3.org/TR/SVG11/filters.html#feCompositeElement)
#[derive(Debug)]
pub struct Composite {
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,
In,
Out,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,19 @@
/// [feGaussianBlur](https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement)
#[derive(Debug)]
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
#[derive(Debug)]
pub struct Image;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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