use skia_safe::Color as SkColor;
use skia_safe::ColorSpace as SkColorSpace;
use skia_safe::{Color4f, named_primaries, named_transfer_fn};
use crate::native::error::NativeError;
pub(crate) fn linear_srgb_color_space() -> SkColorSpace {
SkColorSpace::new_srgb_linear()
}
pub(crate) fn rgba_linear_to_skia_color(color: RgbaLinear) -> SkColor {
let (r, g, b, a) = if color.a > 0.0 {
(
color.r / color.a,
color.g / color.a,
color.b / color.a,
color.a,
)
} else {
(0.0, 0.0, 0.0, 0.0)
};
let alpha_byte = (a.clamp(0.0, 1.0) * 255.0).round() as u8;
SkColor::from_argb(
alpha_byte,
linear_to_srgb_byte(r),
linear_to_srgb_byte(g),
linear_to_srgb_byte(b),
)
}
pub(crate) fn rgba_linear_to_unpremul_color4f(color: RgbaLinear) -> Color4f {
if color.a > 0.0 {
Color4f {
r: color.r / color.a,
g: color.g / color.a,
b: color.b / color.a,
a: color.a,
}
} else {
Color4f::new(0.0, 0.0, 0.0, 0.0)
}
}
fn linear_to_srgb_byte(v: f32) -> u8 {
let v = v.clamp(0.0, 1.0);
let s = if v <= 0.003_130_8 {
12.92 * v
} else {
1.055 * v.powf(1.0 / 2.4) - 0.055
};
(s * 255.0).round() as u8
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LinearColorSpace {
Srgb,
DisplayP3,
Rec2020,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OutputColorSpace {
Srgb,
DisplayP3,
Rec2020,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RgbaLinear {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl RgbaLinear {
pub fn new_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
pub fn opaque(r: f32, g: f32, b: f32) -> Self {
Self { r, g, b, a: 1.0 }
}
pub fn with_opacity(self, opacity: f32) -> Self {
let clamped = opacity.clamp(0.0, 1.0);
Self {
r: self.r * clamped,
g: self.g * clamped,
b: self.b * clamped,
a: self.a * clamped,
}
}
}
impl LinearColorSpace {
pub(crate) fn to_skia_color_space(self) -> Result<SkColorSpace, NativeError> {
match self {
Self::Srgb => Ok(SkColorSpace::new_srgb_linear()),
Self::DisplayP3 => SkColorSpace::new_cicp(
named_primaries::CicpId::SMPTE_EG_432_1,
named_transfer_fn::CicpId::Linear,
)
.ok_or(NativeError::UnsupportedColorSpace { color_space: self }),
Self::Rec2020 => SkColorSpace::new_cicp(
named_primaries::CicpId::Rec2020,
named_transfer_fn::CicpId::Linear,
)
.ok_or(NativeError::UnsupportedColorSpace { color_space: self }),
}
}
}
impl OutputColorSpace {
pub(crate) fn to_skia_color_space(self) -> Result<SkColorSpace, NativeError> {
match self {
Self::Srgb => Ok(SkColorSpace::new_srgb()),
Self::DisplayP3 => SkColorSpace::new_cicp(
named_primaries::CicpId::SMPTE_EG_432_1,
named_transfer_fn::CicpId::IEC61966_2_1,
)
.ok_or(NativeError::UnsupportedOutputColorSpace { color_space: self }),
Self::Rec2020 => SkColorSpace::new_cicp(
named_primaries::CicpId::Rec2020,
named_transfer_fn::CicpId::Rec709,
)
.ok_or(NativeError::UnsupportedOutputColorSpace { color_space: self }),
}
}
}