use ribir_types::Point;
pub use ribir_types::{Color, LightnessTone};
use serde::{Deserialize, Serialize};
use crate::SpreadMethod;
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
pub struct ColorFilterMatrix {
pub matrix: [f32; 16],
pub base_color: Option<Color>,
}
#[inline]
fn dot(
row: usize, col: usize, m1: &[f32], m2: &[f32], m1_row_cnt: usize, m2_row_cnt: usize,
) -> f32 {
let (mut r, mut c, mut s) = (row * m1_row_cnt, col, 0.);
for _ in 0..m1_row_cnt {
s += m1[r] * m2[c];
r += 1;
c += m2_row_cnt;
}
s
}
impl ColorFilterMatrix {
#[inline]
pub fn new(matrix: [f32; 16]) -> Self { Self { matrix, base_color: None } }
pub fn only_alpha(alpha: f32) -> Self {
Self::new([
1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., alpha, ])
}
pub fn with_base(self, color: Color) -> Self { Self { base_color: Some(color), ..self } }
pub fn chains(&mut self, next: &ColorFilterMatrix) -> ColorFilterMatrix {
let mut matrix = [0.; 16];
for (i, item) in matrix.iter_mut().enumerate() {
*item = dot(i / 4, i % 4, &self.matrix, &next.matrix, 4, 4);
}
let mut base_color = next.base_color;
if let Some(c) = self.base_color.as_ref() {
let f = c.into_f32_components();
let base = base_color
.map(|c| c.into_f32_components())
.unwrap_or([0.; 4]);
base_color = Some(Color::from_f32_rgba(
(base[0] + dot(0, 0, &next.matrix, &f, 4, 1)).clamp(0., 1.),
(base[1] + dot(1, 0, &next.matrix, &f, 4, 1)).clamp(0., 1.),
(base[2] + dot(2, 0, &next.matrix, &f, 4, 1)).clamp(0., 1.),
(base[3] + dot(3, 0, &next.matrix, &f, 4, 1)).clamp(0., 1.),
));
}
Self { matrix, base_color }
}
pub fn apply_to(&self, color: &Color) -> Color {
let c = color.into_f32_components();
let base = self
.base_color
.as_ref()
.map(|c| c.into_f32_components())
.unwrap_or([0.; 4]);
Color::from_f32_rgba(
(base[0] + dot(0, 0, &self.matrix, &c, 4, 1)).clamp(0., 1.),
(base[1] + dot(1, 0, &self.matrix, &c, 4, 1)).clamp(0., 1.),
(base[2] + dot(2, 0, &self.matrix, &c, 4, 1)).clamp(0., 1.),
(base[3] + dot(3, 0, &self.matrix, &c, 4, 1)).clamp(0., 1.),
)
}
pub fn apply_alpha(&mut self, alpha: f32) {
self.matrix[12] *= alpha;
self.matrix[13] *= alpha;
self.matrix[14] *= alpha;
self.matrix[15] *= alpha;
if let Some(color) = self.base_color.as_mut() {
*color = color.apply_alpha(alpha);
}
}
pub fn is_transparent(&self) -> bool {
self
.base_color
.as_ref()
.map(|c| c.alpha == 0)
.unwrap_or(true)
&& self.matrix[12] == 0.
&& self.matrix[13] == 0.
&& self.matrix[14] == 0.
&& self.matrix[15] == 0.
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct GradientStop {
pub color: Color,
pub offset: f32,
}
impl GradientStop {
#[inline]
pub fn new(color: Color, offset: f32) -> Self { Self { color, offset } }
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct RadialGradient {
pub start_center: Point,
pub start_radius: f32,
pub end_center: Point,
pub end_radius: f32,
pub stops: Vec<GradientStop>,
pub spread_method: SpreadMethod,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct LinearGradient {
pub start: Point,
pub end: Point,
pub stops: Vec<GradientStop>,
pub spread_method: SpreadMethod,
}