| 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"), |
| } |
| } |
| } |
|
|