mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Color system based on traits, and conversion to linear color in the graph (#1123)
* Migrate Nodes to use RasterMut + Samplable * Add Pixel trait to include serialization * Implement traits for Color and propagate new generics * Always convert to linear color when loading images
This commit is contained in:
committed by
Keavon Chambers
parent
e21c2fb67b
commit
37b892a516
@@ -10,18 +10,18 @@ use spirv_std::num_traits::float::Float;
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::Euclid;
|
||||
|
||||
#[cfg(feature = "gpu")]
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use super::{Alpha, AssociatedAlpha, Luminance, Rec709Primaries, RGB, SRGB};
|
||||
|
||||
/// Structure that represents a color.
|
||||
/// Internally alpha is stored as `f32` that ranges from `0.0` (transparent) to `1.0` (opaque).
|
||||
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
||||
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "gpu", derive(Pod, Zeroable))]
|
||||
#[cfg_attr(feature = "std", derive(specta::Type))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable)]
|
||||
pub struct Color {
|
||||
red: f32,
|
||||
green: f32,
|
||||
@@ -39,6 +39,50 @@ impl Hash for Color {
|
||||
}
|
||||
}
|
||||
|
||||
impl RGB for Color {
|
||||
type ColorChannel = f32;
|
||||
fn red(&self) -> f32 {
|
||||
self.red
|
||||
}
|
||||
fn green(&self) -> f32 {
|
||||
self.green
|
||||
}
|
||||
fn blue(&self) -> f32 {
|
||||
self.blue
|
||||
}
|
||||
}
|
||||
|
||||
impl Alpha for Color {
|
||||
type AlphaChannel = f32;
|
||||
fn alpha(&self) -> f32 {
|
||||
self.alpha
|
||||
}
|
||||
fn multiply_alpha(&self, alpha: Self::AlphaChannel) -> Self {
|
||||
Self {
|
||||
red: self.red * alpha,
|
||||
green: self.green * alpha,
|
||||
blue: self.blue * alpha,
|
||||
alpha: self.alpha * alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AssociatedAlpha for Color {
|
||||
fn to_unassociated<Out: super::UnassociatedAlpha>(&self) -> Out {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Luminance for Color {
|
||||
type LuminanceChannel = f32;
|
||||
fn luminance(&self) -> f32 {
|
||||
0.2126 * self.red + 0.7152 * self.green + 0.0722 * self.blue
|
||||
}
|
||||
}
|
||||
|
||||
impl Rec709Primaries for Color {}
|
||||
impl SRGB for Color {}
|
||||
|
||||
impl Color {
|
||||
pub const BLACK: Color = Color::from_rgbf32_unchecked(0., 0., 0.);
|
||||
pub const WHITE: Color = Color::from_rgbf32_unchecked(1., 1., 1.);
|
||||
@@ -97,8 +141,8 @@ impl Color {
|
||||
/// let color2 = Color::from_rgba8(0x72, 0x67, 0x62, 0xFF);
|
||||
/// assert!(color == color2)
|
||||
/// ```
|
||||
pub fn from_rgb8(red: u8, green: u8, blue: u8) -> Color {
|
||||
Color::from_rgba8(red, green, blue, 255)
|
||||
pub fn from_rgb8_srgb(red: u8, green: u8, blue: u8) -> Color {
|
||||
Color::from_rgba8_srgb(red, green, blue, 255)
|
||||
}
|
||||
|
||||
/// Return an SDR `Color` given RGBA channels from `0` to `255`.
|
||||
@@ -108,7 +152,7 @@ impl Color {
|
||||
/// use graphene_core::raster::color::Color;
|
||||
/// let color = Color::from_rgba8(0x72, 0x67, 0x62, 0x61);
|
||||
/// ```
|
||||
pub fn from_rgba8(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||
pub fn from_rgba8_srgb(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||
let map_range = |int_color| int_color as f32 / 255.0;
|
||||
Color {
|
||||
red: map_range(red),
|
||||
@@ -116,6 +160,7 @@ impl Color {
|
||||
blue: map_range(blue),
|
||||
alpha: map_range(alpha),
|
||||
}
|
||||
.to_linear_srgb()
|
||||
}
|
||||
|
||||
/// Create a [Color] from a hue, saturation, lightness and alpha (all between 0 and 1)
|
||||
@@ -527,7 +572,7 @@ impl Color {
|
||||
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
|
||||
let a = u8::from_str_radix(&color_str[6..8], 16).ok()?;
|
||||
|
||||
Some(Color::from_rgba8(r, g, b, a))
|
||||
Some(Color::from_rgba8_srgb(r, g, b, a))
|
||||
}
|
||||
|
||||
/// Creates a color from a 6-character RGB hex string (without a # prefix).
|
||||
@@ -544,7 +589,7 @@ impl Color {
|
||||
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
|
||||
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
|
||||
|
||||
Some(Color::from_rgb8(r, g, b))
|
||||
Some(Color::from_rgb8_srgb(r, g, b))
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two colors based on t.
|
||||
@@ -676,7 +721,7 @@ fn hsl_roundtrip() {
|
||||
(82, 84, 84),
|
||||
(255, 255, 178),
|
||||
] {
|
||||
let col = Color::from_rgb8(red, green, blue);
|
||||
let col = Color::from_rgb8_srgb(red, green, blue);
|
||||
let [hue, saturation, lightness, alpha] = col.to_hsla();
|
||||
let result = Color::from_hsla(hue, saturation, lightness, alpha);
|
||||
assert!((col.r() - result.r()) < f32::EPSILON * 100.);
|
||||
|
||||
Reference in New Issue
Block a user