|
use dyn_any::DynAny; |
|
use std::hash::Hash; |
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, DynAny, specta::Type, serde::Serialize, serde::Deserialize)] |
|
#[serde(default)] |
|
pub struct AlphaBlending { |
|
pub blend_mode: BlendMode, |
|
pub opacity: f32, |
|
pub fill: f32, |
|
pub clip: bool, |
|
} |
|
impl Default for AlphaBlending { |
|
fn default() -> Self { |
|
Self::new() |
|
} |
|
} |
|
impl Hash for AlphaBlending { |
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
|
self.opacity.to_bits().hash(state); |
|
self.fill.to_bits().hash(state); |
|
self.blend_mode.hash(state); |
|
self.clip.hash(state); |
|
} |
|
} |
|
impl std::fmt::Display for AlphaBlending { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
let round = |x: f32| (x * 1e3).round() / 1e3; |
|
write!( |
|
f, |
|
"Blend Mode: {} β Opacity: {}% β Fill: {}% β Clip: {}", |
|
self.blend_mode, |
|
round(self.opacity * 100.), |
|
round(self.fill * 100.), |
|
if self.clip { "Yes" } else { "No" } |
|
) |
|
} |
|
} |
|
|
|
impl AlphaBlending { |
|
pub const fn new() -> Self { |
|
Self { |
|
opacity: 1., |
|
fill: 1., |
|
blend_mode: BlendMode::Normal, |
|
clip: false, |
|
} |
|
} |
|
|
|
pub fn lerp(&self, other: &Self, t: f32) -> Self { |
|
let lerp = |a: f32, b: f32, t: f32| a + (b - a) * t; |
|
|
|
AlphaBlending { |
|
opacity: lerp(self.opacity, other.opacity, t), |
|
fill: lerp(self.fill, other.fill, t), |
|
blend_mode: if t < 0.5 { self.blend_mode } else { other.blend_mode }, |
|
clip: if t < 0.5 { self.clip } else { other.clip }, |
|
} |
|
} |
|
} |
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, Hash, specta::Type)] |
|
#[repr(i32)] |
|
pub enum BlendMode { |
|
|
|
#[default] |
|
Normal, |
|
|
|
|
|
Darken, |
|
Multiply, |
|
ColorBurn, |
|
LinearBurn, |
|
DarkerColor, |
|
|
|
|
|
Lighten, |
|
Screen, |
|
ColorDodge, |
|
LinearDodge, |
|
LighterColor, |
|
|
|
|
|
Overlay, |
|
SoftLight, |
|
HardLight, |
|
VividLight, |
|
LinearLight, |
|
PinLight, |
|
HardMix, |
|
|
|
|
|
Difference, |
|
Exclusion, |
|
Subtract, |
|
Divide, |
|
|
|
|
|
Hue, |
|
Saturation, |
|
Color, |
|
Luminosity, |
|
|
|
|
|
Erase, |
|
Restore, |
|
MultiplyAlpha, |
|
} |
|
|
|
impl BlendMode { |
|
|
|
pub fn list() -> [&'static [BlendMode]; 6] { |
|
use BlendMode::*; |
|
[ |
|
|
|
&[Normal], |
|
|
|
&[Darken, Multiply, ColorBurn, LinearBurn, DarkerColor], |
|
|
|
&[Lighten, Screen, ColorDodge, LinearDodge, LighterColor], |
|
|
|
&[Overlay, SoftLight, HardLight, VividLight, LinearLight, PinLight, HardMix], |
|
|
|
&[Difference, Exclusion, Subtract, Divide], |
|
|
|
&[Hue, Saturation, Color, Luminosity], |
|
] |
|
} |
|
|
|
|
|
pub fn list_svg_subset() -> [&'static [BlendMode]; 6] { |
|
use BlendMode::*; |
|
[ |
|
|
|
&[Normal], |
|
|
|
&[Darken, Multiply, ColorBurn], |
|
|
|
&[Lighten, Screen, ColorDodge], |
|
|
|
&[Overlay, SoftLight, HardLight], |
|
|
|
&[Difference, Exclusion], |
|
|
|
&[Hue, Saturation, Color, Luminosity], |
|
] |
|
} |
|
|
|
pub fn index_in_list(&self) -> Option<usize> { |
|
Self::list().iter().flat_map(|x| x.iter()).position(|&blend_mode| blend_mode == *self) |
|
} |
|
|
|
pub fn index_in_list_svg_subset(&self) -> Option<usize> { |
|
Self::list_svg_subset().iter().flat_map(|x| x.iter()).position(|&blend_mode| blend_mode == *self) |
|
} |
|
|
|
|
|
|
|
pub fn to_svg_style_name(&self) -> Option<&'static str> { |
|
match self { |
|
|
|
BlendMode::Normal => Some("normal"), |
|
|
|
BlendMode::Darken => Some("darken"), |
|
BlendMode::Multiply => Some("multiply"), |
|
BlendMode::ColorBurn => Some("color-burn"), |
|
|
|
BlendMode::Lighten => Some("lighten"), |
|
BlendMode::Screen => Some("screen"), |
|
BlendMode::ColorDodge => Some("color-dodge"), |
|
|
|
BlendMode::Overlay => Some("overlay"), |
|
BlendMode::SoftLight => Some("soft-light"), |
|
BlendMode::HardLight => Some("hard-light"), |
|
|
|
BlendMode::Difference => Some("difference"), |
|
BlendMode::Exclusion => Some("exclusion"), |
|
|
|
BlendMode::Hue => Some("hue"), |
|
BlendMode::Saturation => Some("saturation"), |
|
BlendMode::Color => Some("color"), |
|
BlendMode::Luminosity => Some("luminosity"), |
|
_ => None, |
|
} |
|
} |
|
|
|
|
|
pub fn render(&self) -> String { |
|
format!( |
|
r#" mix-blend-mode: {};"#, |
|
self.to_svg_style_name().unwrap_or_else(|| { |
|
warn!("Unsupported blend mode {self:?}"); |
|
"normal" |
|
}) |
|
) |
|
} |
|
} |
|
|
|
impl std::fmt::Display for BlendMode { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
match self { |
|
|
|
BlendMode::Normal => write!(f, "Normal"), |
|
|
|
BlendMode::Darken => write!(f, "Darken"), |
|
BlendMode::Multiply => write!(f, "Multiply"), |
|
BlendMode::ColorBurn => write!(f, "Color Burn"), |
|
BlendMode::LinearBurn => write!(f, "Linear Burn"), |
|
BlendMode::DarkerColor => write!(f, "Darker Color"), |
|
|
|
BlendMode::Lighten => write!(f, "Lighten"), |
|
BlendMode::Screen => write!(f, "Screen"), |
|
BlendMode::ColorDodge => write!(f, "Color Dodge"), |
|
BlendMode::LinearDodge => write!(f, "Linear Dodge"), |
|
BlendMode::LighterColor => write!(f, "Lighter Color"), |
|
|
|
BlendMode::Overlay => write!(f, "Overlay"), |
|
BlendMode::SoftLight => write!(f, "Soft Light"), |
|
BlendMode::HardLight => write!(f, "Hard Light"), |
|
BlendMode::VividLight => write!(f, "Vivid Light"), |
|
BlendMode::LinearLight => write!(f, "Linear Light"), |
|
BlendMode::PinLight => write!(f, "Pin Light"), |
|
BlendMode::HardMix => write!(f, "Hard Mix"), |
|
|
|
BlendMode::Difference => write!(f, "Difference"), |
|
BlendMode::Exclusion => write!(f, "Exclusion"), |
|
BlendMode::Subtract => write!(f, "Subtract"), |
|
BlendMode::Divide => write!(f, "Divide"), |
|
|
|
BlendMode::Hue => write!(f, "Hue"), |
|
BlendMode::Saturation => write!(f, "Saturation"), |
|
BlendMode::Color => write!(f, "Color"), |
|
BlendMode::Luminosity => write!(f, "Luminosity"), |
|
|
|
BlendMode::Erase => write!(f, "Erase"), |
|
BlendMode::Restore => write!(f, "Restore"), |
|
BlendMode::MultiplyAlpha => write!(f, "Multiply Alpha"), |
|
} |
|
} |
|
} |
|
|