Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions vello_common/src/filter/color_matrix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2026 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! The color matrix filter.

/// Matrix-based color transformation filter.
///
/// The matrix is stored as four rows of five values. Each row computes one output
/// channel (`R`, `G`, `B`, `A`) from the four straight-alpha input channels plus a
/// constant offset.
#[derive(Clone, Copy, Debug)]
pub struct ColorMatrix {
/// The 4x5 color transformation matrix in row-major order.
pub matrix: [f32; 20],
}

impl ColorMatrix {
/// Create a new color matrix filter.
pub fn new(matrix: [f32; 20]) -> Self {
Self { matrix }
}
}
46 changes: 33 additions & 13 deletions vello_common/src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! represent a special representation of each filter to be used as the basis for rendering in
//! `vello_gpu` and `vello_cpu`.

use crate::filter::color_matrix::ColorMatrix;
use crate::filter::drop_shadow::{DropShadow, transform_shadow_params};
use crate::filter::flood::Flood;
use crate::filter::gaussian_blur::{GaussianBlur, transform_blur_params};
Expand All @@ -17,7 +18,9 @@ use crate::kurbo::{Affine, Rect, Vec2};
use crate::math::snap_up;
use crate::tile::Tile;
use crate::util::RectExt;
use smallvec::SmallVec;

pub mod color_matrix;
pub mod drop_shadow;
pub mod flood;
pub mod gaussian_blur;
Expand All @@ -34,17 +37,34 @@ pub enum PreparedFilter {
Offset(Offset),
/// A drop shadow filter.
DropShadow(DropShadow),
/// A color matrix filter.
ColorMatrix(ColorMatrix),
}

/// The prepared primitives of a filter, in the order in which they need to be applied.
///
/// Each primitive takes the output of the previous one as its input; the first one operates on
/// the rendered layer. An empty chain leaves the layer unchanged.
pub type PreparedFilterChain = SmallVec<[PreparedFilter; 1]>;

impl PreparedFilter {
/// Build a new prepared filter for the given transform.
pub fn new(filter: &Filter, transform: &Affine) -> Self {
// Multi-primitive filter graphs are not yet implemented.
if filter.graph.primitives.len() != 1 {
unimplemented!("Multi-primitive filter graphs are not yet supported");
}
/// Prepare all primitives of a filter for rendering with the given transform.
///
/// Primitives that are not implemented yet are skipped, i.e. they act as identity.
pub fn chain(filter: &Filter, transform: &Affine) -> PreparedFilterChain {
filter
.graph
.primitives
.iter()
.filter_map(|primitive| Self::from_primitive(primitive, transform))
.collect()
}

match &filter.graph.primitives[0] {
/// Prepare a single filter primitive for rendering with the given transform.
///
/// Returns `None` for primitives that are not implemented yet.
pub fn from_primitive(primitive: &FilterPrimitive, transform: &Affine) -> Option<Self> {
let prepared = match primitive {
FilterPrimitive::Flood { color } => {
let flood = Flood::new(*color);
Self::Flood(flood)
Expand Down Expand Up @@ -96,12 +116,12 @@ impl PreparedFilter {

Self::Offset(offset)
}
_ => {
// Other primitives like Blend, ColorMatrix, ComponentTransfer, etc.
// are not yet implemented
unimplemented!("Other filter primitives not yet implemented");
}
}
FilterPrimitive::ColorMatrix { matrix } => Self::ColorMatrix(ColorMatrix::new(*matrix)),
// Other primitives like Blend, ComponentTransfer, etc. are not yet implemented.
_ => return None,
};

Some(prepared)
}
}

Expand Down
Loading
Loading