use image::Rgba;
use ruisa_path::{NormalizedF32, Scalar};
pub type AlphaU8 = u8;
pub const ALPHA_U8_TRANSPARENT: AlphaU8 = 0x00;
pub const ALPHA_U8_OPAQUE: AlphaU8 = 0xFF;
pub const ALPHA_TRANSPARENT: NormalizedF32 = NormalizedF32::ZERO;
pub const ALPHA_OPAQUE: NormalizedF32 = NormalizedF32::ONE;
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq)]
pub struct ColorU8{
pub color: Rgba<u8>
}
impl ColorU8 {
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
ColorU8{
color:Rgba::from([r,g,b,a])
}
}
pub const fn red(self) -> u8 {
self.color.0[0]
}
pub const fn green(self) -> u8 {
self.color.0[1]
}
pub const fn blue(self) -> u8 {
self.color.0[2]
}
pub const fn alpha(self) -> u8 {
self.color.0[3]
}
pub fn is_opaque(&self) -> bool {
self.alpha() == ALPHA_U8_OPAQUE
}
pub fn premultiply(&self) -> PremultipliedColorU8 {
let a = self.alpha();
if a != ALPHA_U8_OPAQUE {
PremultipliedColorU8::from_rgba_unchecked(
premultiply_u8(self.red(), a),
premultiply_u8(self.green(), a),
premultiply_u8(self.blue(), a),
a,
)
} else {
PremultipliedColorU8::from_rgba_unchecked(self.red(), self.green(), self.blue(), a)
}
}
}
impl core::fmt::Debug for ColorU8 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ColorU8")
.field("r", &self.red())
.field("g", &self.green())
.field("b", &self.blue())
.field("a", &self.alpha())
.finish()
}
}
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq)]
pub struct PremultipliedColorU8(u32);
unsafe impl bytemuck::Zeroable for PremultipliedColorU8 {}
unsafe impl bytemuck::Pod for PremultipliedColorU8 {}
impl PremultipliedColorU8 {
pub const TRANSPARENT: Self = PremultipliedColorU8::from_rgba_unchecked(0, 0, 0, 0);
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Option<Self> {
if r <= a && g <= a && b <= a {
Some(PremultipliedColorU8(pack_rgba(r, g, b, a)))
} else {
None
}
}
pub(crate) const fn from_rgba_unchecked(r: u8, g: u8, b: u8, a: u8) -> Self {
PremultipliedColorU8(pack_rgba(r, g, b, a))
}
pub const fn red(self) -> u8 {
self.0.to_ne_bytes()[0]
}
pub const fn green(self) -> u8 {
self.0.to_ne_bytes()[1]
}
pub const fn blue(self) -> u8 {
self.0.to_ne_bytes()[2]
}
pub const fn alpha(self) -> u8 {
self.0.to_ne_bytes()[3]
}
pub fn to_rgba(self) -> Rgba<u8> {
Rgba::from(self.0.to_ne_bytes())
}
pub const fn byte(self, index: usize) -> u8 {
self.0.to_ne_bytes()[index]
}
pub fn is_opaque(&self) -> bool {
self.alpha() == ALPHA_U8_OPAQUE
}
pub const fn get(self) -> u32 {
self.0
}
pub fn demultiply(&self) -> ColorU8 {
let alpha = self.alpha();
if alpha == ALPHA_U8_OPAQUE {
ColorU8::from_rgba(self.red(), self.green(), self.blue(), alpha)
} else {
let a = alpha as f64 / 255.0;
ColorU8::from_rgba(
(self.red() as f64 / a + 0.5) as u8,
(self.green() as f64 / a + 0.5) as u8,
(self.blue() as f64 / a + 0.5) as u8,
alpha,
)
}
}
}
impl core::fmt::Debug for PremultipliedColorU8 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PremultipliedColorU8")
.field("r", &self.red())
.field("g", &self.green())
.field("b", &self.blue())
.field("a", &self.alpha())
.finish()
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Color {
r: NormalizedF32,
g: NormalizedF32,
b: NormalizedF32,
a: NormalizedF32,
}
const NV_ZERO: NormalizedF32 = NormalizedF32::ZERO;
const NV_ONE: NormalizedF32 = NormalizedF32::ONE;
impl Color {
pub const TRANSPARENT: Color = Color {
r: NV_ZERO,
g: NV_ZERO,
b: NV_ZERO,
a: NV_ZERO,
};
pub const BLACK: Color = Color {
r: NV_ZERO,
g: NV_ZERO,
b: NV_ZERO,
a: NV_ONE,
};
pub const WHITE: Color = Color {
r: NV_ONE,
g: NV_ONE,
b: NV_ONE,
a: NV_ONE,
};
pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Option<Self> {
Some(Color {
r: NormalizedF32::new(r)?,
g: NormalizedF32::new(g)?,
b: NormalizedF32::new(b)?,
a: NormalizedF32::new(a)?,
})
}
pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
Color {
r: NormalizedF32::new_u8(r),
g: NormalizedF32::new_u8(g),
b: NormalizedF32::new_u8(b),
a: NormalizedF32::new_u8(a),
}
}
pub fn red(&self) -> f32 {
self.r.get()
}
pub fn green(&self) -> f32 {
self.g.get()
}
pub fn blue(&self) -> f32 {
self.b.get()
}
pub fn alpha(&self) -> f32 {
self.a.get()
}
pub fn set_red(&mut self, c: f32) {
self.r = NormalizedF32::new_clamped(c);
}
pub fn set_green(&mut self, c: f32) {
self.g = NormalizedF32::new_clamped(c);
}
pub fn set_blue(&mut self, c: f32) {
self.b = NormalizedF32::new_clamped(c);
}
pub fn set_alpha(&mut self, c: f32) {
self.a = NormalizedF32::new_clamped(c);
}
pub fn apply_opacity(&mut self, opacity: f32) {
self.a = NormalizedF32::new_clamped(self.a.get() * opacity.bound(0.0, 1.0));
}
pub fn is_opaque(&self) -> bool {
self.a == ALPHA_OPAQUE
}
pub fn premultiply(&self) -> ColorF32 {
if self.is_opaque() {
ColorF32 {
r: self.r,
g: self.g,
b: self.b,
a: self.a,
}
} else {
ColorF32 {
r: NormalizedF32::new_clamped(self.r.get() * self.a.get()),
g: NormalizedF32::new_clamped(self.g.get() * self.a.get()),
b: NormalizedF32::new_clamped(self.b.get() * self.a.get()),
a: self.a,
}
}
}
pub fn to_color_u8(&self) -> ColorU8 {
let c = color_f32_to_u8(self.r, self.g, self.b, self.a);
ColorU8::from_rgba(c[0], c[1], c[2], c[3])
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct ColorF32 {
r: NormalizedF32,
g: NormalizedF32,
b: NormalizedF32,
a: NormalizedF32,
}
impl ColorF32 {
pub fn red(&self) -> f32 {
self.r.get()
}
pub fn green(&self) -> f32 {
self.g.get()
}
pub fn blue(&self) -> f32 {
self.b.get()
}
pub fn alpha(&self) -> f32 {
self.a.get()
}
pub fn demultiply(&self) -> Color {
let a = self.a.get();
if a == 0.0 {
Color::TRANSPARENT
} else {
Color {
r: NormalizedF32::new_clamped(self.r.get() / a),
g: NormalizedF32::new_clamped(self.g.get() / a),
b: NormalizedF32::new_clamped(self.b.get() / a),
a: self.a,
}
}
}
pub fn to_color_u8(&self) -> PremultipliedColorU8 {
let c = color_f32_to_u8(self.r, self.g, self.b, self.a);
PremultipliedColorU8::from_rgba_unchecked(c[0], c[1], c[2], c[3])
}
}
pub fn premultiply_u8(c: u8, a: u8) -> u8 {
let prod = u32::from(c) * u32::from(a) + 128;
((prod + (prod >> 8)) >> 8) as u8
}
const fn pack_rgba(r: u8, g: u8, b: u8, a: u8) -> u32 {
u32::from_ne_bytes([r, g, b, a])
}
fn color_f32_to_u8(
r: NormalizedF32,
g: NormalizedF32,
b: NormalizedF32,
a: NormalizedF32,
) -> [u8; 4] {
[
(r.get() * 255.0 + 0.5) as u8,
(g.get() * 255.0 + 0.5) as u8,
(b.get() * 255.0 + 0.5) as u8,
(a.get() * 255.0 + 0.5) as u8,
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn premultiply_u8() {
assert_eq!(
ColorU8::from_rgba(10, 20, 30, 40).premultiply(),
PremultipliedColorU8::from_rgba_unchecked(2, 3, 5, 40)
);
}
#[test]
fn premultiply_u8_opaque() {
assert_eq!(
ColorU8::from_rgba(10, 20, 30, 255).premultiply(),
PremultipliedColorU8::from_rgba_unchecked(10, 20, 30, 255)
);
}
#[test]
fn demultiply_u8_1() {
assert_eq!(
PremultipliedColorU8::from_rgba_unchecked(2, 3, 5, 40).demultiply(),
ColorU8::from_rgba(13, 19, 32, 40)
);
}
#[test]
fn demultiply_u8_2() {
assert_eq!(
PremultipliedColorU8::from_rgba_unchecked(10, 20, 30, 255).demultiply(),
ColorU8::from_rgba(10, 20, 30, 255)
);
}
#[test]
fn demultiply_u8_3() {
assert_eq!(
PremultipliedColorU8::from_rgba_unchecked(153, 99, 54, 180).demultiply(),
ColorU8::from_rgba(217, 140, 77, 180)
);
}
}