use crate::types::{Color, Rect};
use super::BlendMode;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct NormalizedFilterRegion(Rect);
impl NormalizedFilterRegion {
pub(crate) fn new(x: f32, y: f32, width: f32, height: f32) -> Option<Self> {
(x.is_finite()
&& y.is_finite()
&& width.is_finite()
&& height.is_finite()
&& width > 0.0
&& height > 0.0)
.then_some(Self(Rect::from_xywh(x, y, width, height)))
}
pub(crate) const fn as_rect(self) -> Rect {
self.0
}
}
impl Default for NormalizedFilterRegion {
fn default() -> Self {
Self(Rect::from_xywh(-0.1, -0.1, 1.2, 1.2))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DropShadow {
pub dx: f32,
pub dy: f32,
pub blur: f32,
pub color: Color,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FilterOperation {
Grayscale(f32),
Sepia(f32),
Invert(f32),
Brightness(f32),
Contrast(f32),
Saturate(f32),
HueRotate(f32),
Opacity(f32),
Matrix([f32; 20]),
BlendWithFlood {
color: Color,
mode: BlendMode,
region: NormalizedFilterRegion,
},
Blur(f32),
Offset {
dx: f32,
dy: f32,
keep_source: bool,
region: NormalizedFilterRegion,
},
DropShadow(DropShadow),
MorphologyDilate(f32),
}
impl FilterOperation {
pub(crate) fn is_visual_identity(&self) -> bool {
const EPSILON: f32 = 1e-6;
match self {
Self::Grayscale(amount)
| Self::Sepia(amount)
| Self::Invert(amount)
| Self::Blur(amount)
| Self::MorphologyDilate(amount) => amount.abs() <= EPSILON,
Self::Brightness(amount)
| Self::Contrast(amount)
| Self::Saturate(amount)
| Self::Opacity(amount) => (*amount - 1.0).abs() <= EPSILON,
Self::HueRotate(degrees) => degrees.rem_euclid(360.0).abs() <= EPSILON,
Self::Matrix(values) => {
const IDENTITY: [f32; 20] = [
1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
];
values
.iter()
.zip(IDENTITY)
.all(|(value, identity)| (*value - identity).abs() <= EPSILON)
}
Self::BlendWithFlood { .. } | Self::Offset { .. } | Self::DropShadow(_) => false,
}
}
pub(crate) fn requires_complete_svg_region(&self) -> bool {
match self {
Self::Blur(radius) => *radius > 0.0,
Self::DropShadow(shadow) => shadow.blur > 0.0,
Self::BlendWithFlood { .. } => true,
Self::Matrix(values) => values[19] > 0.0,
_ => false,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct FilterEffects {
pub establishes_stacking_context: bool,
pub operations: Vec<FilterOperation>,
pub url_id: Option<String>,
}