mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Refactor many usages of Color to natively store linear not gamma (#2457)
This commit is contained in:
@@ -15,9 +15,10 @@ pub enum GradientType {
|
||||
}
|
||||
|
||||
// TODO: Someday we could switch this to a Box[T] to avoid over-allocation
|
||||
// TODO: Use linear not gamma colors
|
||||
/// A list of colors associated with positions (in the range 0 to 1) along a gradient.
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, DynAny, specta::Type)]
|
||||
pub struct GradientStops(pub Vec<(f64, Color)>);
|
||||
pub struct GradientStops(Vec<(f64, Color)>);
|
||||
|
||||
impl std::hash::Hash for GradientStops {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
@@ -35,8 +36,54 @@ impl Default for GradientStops {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for GradientStops {
|
||||
type Item = (f64, Color);
|
||||
type IntoIter = std::vec::IntoIter<(f64, Color)>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a GradientStops {
|
||||
type Item = &'a (f64, Color);
|
||||
type IntoIter = std::slice::Iter<'a, (f64, Color)>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<usize> for GradientStops {
|
||||
type Output = (f64, Color);
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for GradientStops {
|
||||
type Target = Vec<(f64, Color)>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for GradientStops {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl GradientStops {
|
||||
pub fn evalute(&self, t: f64) -> Color {
|
||||
pub fn new(stops: Vec<(f64, Color)>) -> Self {
|
||||
let mut stops = Self(stops);
|
||||
stops.sort();
|
||||
stops
|
||||
}
|
||||
|
||||
pub fn evaluate(&self, t: f64) -> Color {
|
||||
if self.0.is_empty() {
|
||||
return Color::BLACK;
|
||||
}
|
||||
@@ -60,9 +107,17 @@ impl GradientStops {
|
||||
Color::BLACK
|
||||
}
|
||||
|
||||
pub fn sort(&mut self) {
|
||||
self.0.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
||||
}
|
||||
|
||||
pub fn reversed(&self) -> Self {
|
||||
Self(self.0.iter().rev().map(|(position, color)| (1. - position, *color)).collect())
|
||||
}
|
||||
|
||||
pub fn map_colors<F: Fn(&Color) -> Color>(&self, f: F) -> Self {
|
||||
Self(self.0.iter().map(|(position, color)| (*position, f(color))).collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// A gradient fill.
|
||||
@@ -110,7 +165,7 @@ impl Gradient {
|
||||
Gradient {
|
||||
start,
|
||||
end,
|
||||
stops: GradientStops(vec![(0., start_color), (1., end_color)]),
|
||||
stops: GradientStops::new(vec![(0., start_color.to_gamma_srgb()), (1., end_color.to_gamma_srgb())]),
|
||||
transform,
|
||||
gradient_type,
|
||||
}
|
||||
@@ -131,7 +186,7 @@ impl Gradient {
|
||||
(position, color)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let stops = GradientStops(stops);
|
||||
let stops = GradientStops::new(stops);
|
||||
let gradient_type = if time < 0.5 { self.gradient_type } else { other.gradient_type };
|
||||
|
||||
Self {
|
||||
@@ -156,7 +211,7 @@ impl Gradient {
|
||||
if *position != 0. {
|
||||
let _ = write!(stop, r#" offset="{}""#, (position * 1_000_000.).round() / 1_000_000.);
|
||||
}
|
||||
let _ = write!(stop, r##" stop-color="#{}""##, color.rgb_hex());
|
||||
let _ = write!(stop, r##" stop-color="#{}""##, color.to_rgb_hex_srgb_from_gamma());
|
||||
if color.a() < 1. {
|
||||
let _ = write!(stop, r#" stop-opacity="{}""#, (color.a() * 1000.).round() / 1000.);
|
||||
}
|
||||
@@ -242,7 +297,7 @@ impl Gradient {
|
||||
///
|
||||
/// Can be None, a solid [Color], or a linear/radial [Gradient].
|
||||
///
|
||||
/// In the future we'll probably also add a pattern fill.
|
||||
/// In the future we'll probably also add a pattern fill. This will probably be named "Paint" in the future.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, DynAny, Hash, specta::Type)]
|
||||
pub enum Fill {
|
||||
@@ -305,7 +360,7 @@ impl Fill {
|
||||
match self {
|
||||
Self::None => r#" fill="none""#.to_string(),
|
||||
Self::Solid(color) => {
|
||||
let mut result = format!(r##" fill="#{}""##, color.rgb_hex());
|
||||
let mut result = format!(r##" fill="#{}""##, color.to_rgb_hex_srgb_from_gamma());
|
||||
if color.a() < 1. {
|
||||
let _ = write!(result, r#" fill-opacity="{}""#, (color.a() * 1000.).round() / 1000.);
|
||||
}
|
||||
@@ -325,6 +380,14 @@ impl Fill {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a solid color from the fill
|
||||
pub fn as_solid(&self) -> Option<Color> {
|
||||
match self {
|
||||
Self::Solid(color) => Some(*color),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Fill {
|
||||
@@ -355,18 +418,13 @@ impl From<Gradient> for Fill {
|
||||
pub enum FillChoice {
|
||||
#[default]
|
||||
None,
|
||||
/// WARNING: Color is gamma, not linear!
|
||||
Solid(Color),
|
||||
/// WARNING: Color stops are gamma, not linear!
|
||||
Gradient(GradientStops),
|
||||
}
|
||||
|
||||
impl FillChoice {
|
||||
pub fn from_optional_color(color: Option<Color>) -> Self {
|
||||
match color {
|
||||
Some(color) => Self::Solid(color),
|
||||
None => Self::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_solid(&self) -> Option<Color> {
|
||||
let Self::Solid(color) = self else { return None };
|
||||
Some(*color)
|
||||
@@ -575,7 +633,7 @@ impl Stroke {
|
||||
let line_join_miter_limit = (self.line_join_miter_limit != 4.).then_some(self.line_join_miter_limit);
|
||||
|
||||
// Render the needed stroke attributes
|
||||
let mut attributes = format!(r##" stroke="#{}""##, color.rgb_hex());
|
||||
let mut attributes = format!(r##" stroke="#{}""##, color.to_rgb_hex_srgb_from_gamma());
|
||||
if color.a() < 1. {
|
||||
let _ = write!(&mut attributes, r#" stroke-opacity="{}""#, (color.a() * 1000.).round() / 1000.);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ where
|
||||
},
|
||||
};
|
||||
|
||||
let color = gradient.evalute(factor);
|
||||
let color = gradient.evaluate(factor);
|
||||
|
||||
if fill {
|
||||
vector_data.instance.style.set_fill(Fill::Solid(color));
|
||||
|
||||
Reference in New Issue
Block a user