use skia_safe::{
BlurStyle as SkBlurStyle, ColorFilter as SkColorFilter,
ImageFilter as SkImageFilter, MaskFilter as SkMaskFilter, color_filters,
image_filters, luma_color_filter,
};
use crate::{
color::{
RgbaLinear, linear_srgb_color_space, rgba_linear_to_unpremul_color4f,
},
error::Error,
};
#[derive(Clone)]
pub struct ImageFilter {
pub(crate) inner: SkImageFilter,
}
impl std::fmt::Debug for ImageFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImageFilter").finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct ColorFilter {
pub(crate) inner: SkColorFilter,
}
impl std::fmt::Debug for ColorFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ColorFilter").finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum BlurStyle {
#[default]
Normal,
Solid,
Outer,
Inner,
}
impl BlurStyle {
fn to_skia(self) -> SkBlurStyle {
match self {
Self::Normal => SkBlurStyle::Normal,
Self::Solid => SkBlurStyle::Solid,
Self::Outer => SkBlurStyle::Outer,
Self::Inner => SkBlurStyle::Inner,
}
}
}
#[derive(Clone)]
pub struct MaskFilter {
pub(crate) inner: SkMaskFilter,
}
impl std::fmt::Debug for MaskFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MaskFilter").finish_non_exhaustive()
}
}
impl MaskFilter {
pub fn blur(
style: BlurStyle,
sigma: f32,
respect_ctm: bool,
) -> Result<Self, Error> {
SkMaskFilter::blur(style.to_skia(), sigma, respect_ctm)
.map(|inner| Self { inner })
.ok_or_else(|| Error::FilterCreate {
reason: format!("mask blur (style={style:?}, sigma={sigma})"),
})
}
}
impl ImageFilter {
pub fn blur(
sigma_x: f32,
sigma_y: f32,
input: Option<ImageFilter>,
) -> Result<Self, Error> {
let inner = input.map(|f| f.inner);
image_filters::blur((sigma_x, sigma_y), None, inner, None)
.map(|f| ImageFilter { inner: f })
.ok_or_else(|| Error::FilterCreate {
reason: format!("blur({sigma_x}, {sigma_y}) failed"),
})
}
pub fn drop_shadow(
dx: f32,
dy: f32,
sigma_x: f32,
sigma_y: f32,
color: RgbaLinear,
input: Option<ImageFilter>,
) -> Result<Self, Error> {
let unpremul = rgba_linear_to_unpremul_color4f(color);
let inner = input.map(|f| f.inner);
let cs = linear_srgb_color_space();
image_filters::drop_shadow(
skia_safe::Vector::new(dx, dy),
(sigma_x, sigma_y),
unpremul,
Some(cs),
inner,
None,
)
.map(|f| ImageFilter { inner: f })
.ok_or_else(|| Error::FilterCreate {
reason: format!("drop_shadow({dx}, {dy}) failed"),
})
}
pub fn color_matrix(
matrix: [f32; 20],
input: Option<ImageFilter>,
) -> Result<Self, Error> {
let cf = color_filters::matrix_row_major(&matrix, None);
let inner = input.map(|f| f.inner);
image_filters::color_filter(cf, inner, None)
.map(|f| ImageFilter { inner: f })
.ok_or_else(|| Error::FilterCreate {
reason: "color_matrix failed".to_string(),
})
}
pub fn from_color_filter(
color_filter: ColorFilter,
input: Option<ImageFilter>,
) -> Result<Self, Error> {
let inner = input.map(|f| f.inner);
image_filters::color_filter(color_filter.inner, inner, None)
.map(|f| ImageFilter { inner: f })
.ok_or_else(|| Error::FilterCreate {
reason: "from_color_filter failed".to_string(),
})
}
pub fn compose(
outer: ImageFilter,
inner: ImageFilter,
) -> Result<Self, Error> {
image_filters::compose(outer.inner, inner.inner)
.map(|f| ImageFilter { inner: f })
.ok_or_else(|| Error::FilterCreate {
reason: "image filter compose failed".to_string(),
})
}
}
impl ColorFilter {
pub fn luma() -> Self {
Self {
inner: luma_color_filter::new(),
}
}
pub fn linear_to_srgb_gamma() -> Self {
Self {
inner: color_filters::linear_to_srgb_gamma(),
}
}
pub fn srgb_to_linear_gamma() -> Self {
Self {
inner: color_filters::srgb_to_linear_gamma(),
}
}
pub fn compose(
outer: ColorFilter,
inner: ColorFilter,
) -> Result<Self, Error> {
color_filters::compose(outer.inner, inner.inner)
.map(|f| ColorFilter { inner: f })
.ok_or_else(|| Error::FilterCreate {
reason: "color filter compose failed".to_string(),
})
}
}