Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sRGB awareness for Color #616

Merged
merged 11 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 45 additions & 10 deletions crates/bevy_render/src/color.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::texture::Texture;
use crate::{
colorspace::*,
impl_render_resource_bytes,
renderer::{RenderResource, RenderResourceType},
};
Expand All @@ -10,7 +11,7 @@ use bevy_property::Property;
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Mul, MulAssign};

/// A RGBA color
/// RGBA color in linear colorspace and 32-bit per component.
julhe marked this conversation as resolved.
Show resolved Hide resolved
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Property)]
pub struct Color {
Expand All @@ -23,18 +24,32 @@ pub struct Color {
unsafe impl Byteable for Color {}

impl Color {
pub const BLACK: Color = Color::rgb(0.0, 0.0, 0.0);
pub const BLUE: Color = Color::rgb(0.0, 0.0, 1.0);
pub const GREEN: Color = Color::rgb(0.0, 1.0, 0.0);
pub const NONE: Color = Color::rgba(0.0, 0.0, 0.0, 0.0);
pub const RED: Color = Color::rgb(1.0, 0.0, 0.0);
pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);

pub const fn rgb(r: f32, g: f32, b: f32) -> Color {
pub const BLACK: Color = Color::rgb_linear(0.0, 0.0, 0.0);
pub const BLUE: Color = Color::rgb_linear(0.0, 0.0, 1.0);
pub const GREEN: Color = Color::rgb_linear(0.0, 1.0, 0.0);
pub const NONE: Color = Color::rgba_linear(0.0, 0.0, 0.0, 0.0);
pub const RED: Color = Color::rgb_linear(1.0, 0.0, 0.0);
pub const WHITE: Color = Color::rgb_linear(1.0, 1.0, 1.0);

// TODO: cant make rgb and rgba const due traits not allowed in const functions
// see issue #57563 https://github.com/rust-lang/rust/issues/57563
/// New ``Color`` from sRGB colorspace.
pub fn rgb(r: f32, g: f32, b: f32) -> Color {
Color { r, g, b, a: 1.0 }.as_srgb_to_linear()
}

/// New ``Color`` from sRGB colorspace.
pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
Color { r, g, b, a }.as_srgb_to_linear()
}

/// New ``Color`` from linear colorspace.
pub const fn rgb_linear(r: f32, g: f32, b: f32) -> Color {
Color { r, g, b, a: 1.0 }
}

pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
/// New ``Color`` from linear colorspace.
pub const fn rgba_linear(r: f32, g: f32, b: f32, a: f32) -> Color {
Color { r, g, b, a }
}

Expand Down Expand Up @@ -74,12 +89,14 @@ impl Color {
Err(HexColorError::Length)
}

/// New ``Color`` from sRGB colorspace.
pub fn rgb_u8(r: u8, g: u8, b: u8) -> Color {
Color::rgba_u8(r, g, b, u8::MAX)
}

// Float operations in const fn are not stable yet
// see https://github.com/rust-lang/rust/issues/57241
/// New ``Color`` from sRGB colorspace.
pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Color {
Color::rgba(
r as f32 / u8::MAX as f32,
Expand All @@ -88,6 +105,24 @@ impl Color {
a as f32 / u8::MAX as f32,
)
}

fn as_srgb_to_linear(self) -> Color {
Color {
r: self.r.srgb_to_linear(),
g: self.g.srgb_to_linear(),
b: self.b.srgb_to_linear(),
a: self.a, //alpha is always linear
}
}

pub fn to_srgb(self) -> Color {
Color {
r: self.r.linear_to_srgb(),
g: self.g.linear_to_srgb(),
b: self.b.linear_to_srgb(),
a: self.a, //alpha is always linear
}
}
julhe marked this conversation as resolved.
Show resolved Hide resolved
}

impl Default for Color {
Expand Down
48 changes: 48 additions & 0 deletions crates/bevy_render/src/colorspace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// sRGB
//==================================================================================================
pub trait SrgbColorSpace {
fn linear_to_srgb(self) -> Self;
julhe marked this conversation as resolved.
Show resolved Hide resolved
fn srgb_to_linear(self) -> Self;
}

//source: https://entropymine.com/imageworsener/srgbformula/
impl SrgbColorSpace for f32 {
fn linear_to_srgb(self) -> f32 {
if self <= 0.0 {
return self;
}

if self <= 0.0031308 {
self * 12.92 // linear falloff in dark values
} else {
(1.055 * self.powf(1.0 / 2.4)) - 0.055 //gamma curve in other area
}
}

fn srgb_to_linear(self) -> f32 {
if self <= 0.0 {
return self;
}
if self <= 0.04045 {
self / 12.92 // linear falloff in dark values
} else {
((self + 0.055) / 1.055).powf(2.4) //gamma curve in other area
}
}
}

#[test]
fn test_srgb_full_roundtrip() {
let u8max: f32 = u8::max_value() as f32;
for color in 0..u8::max_value() {
let color01 = color as f32 / u8max;
let color_roundtrip = color01.linear_to_srgb().srgb_to_linear();
// roundtrip is not perfect due to numeric precision, even with f64
// so ensure the error is at least ready for u8 (where sRGB is used)
assert_eq!(
(color01 * u8max).round() as u8,
(color_roundtrip * u8max).round() as u8
);
}
}
//==================================================================================================
1 change: 1 addition & 0 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod batch;
pub mod camera;
pub mod color;
pub mod colorspace;
pub mod draw;
pub mod entity;
pub mod mesh;
Expand Down