[color-picker] edit your own (s)RGBA arrays
Both with and without premultiplied alpha
This commit is contained in:
parent
b9a3240ca3
commit
bc0d6baefb
6 changed files with 340 additions and 39 deletions
6
TODO.md
6
TODO.md
|
@ -14,7 +14,11 @@ TODO-list for the Egui project. If you looking for something to do, look here.
|
|||
* [x] linear rgb <-> sRGB
|
||||
* [x] HSV
|
||||
* [x] Color edit button with popup color picker
|
||||
* [ ] Easily edit users own (s)RGBA quadruplets (`&mut [u8;4]`/`[f32;4]`)
|
||||
* [x] Gamma for value (brightness) slider
|
||||
* [x] Easily edit users own (s)RGBA quadruplets (`&mut [u8;4]`/`[f32;4]`)
|
||||
* [ ] RGB editing without alpha
|
||||
* [ ] Additive blending aware color picker
|
||||
* [ ] Premultiplied alpha is a bit of a pain in the ass. Maybe rethink this a bit.
|
||||
* Containers
|
||||
* [ ] Scroll areas
|
||||
* [x] Vertical scrolling
|
||||
|
|
|
@ -312,6 +312,7 @@ pub struct DemoWindow {
|
|||
num_columns: usize,
|
||||
|
||||
widgets: Widgets,
|
||||
colors: ColorWidgets,
|
||||
layout: LayoutDemo,
|
||||
tree: Tree,
|
||||
box_painting: BoxPainting,
|
||||
|
@ -324,6 +325,7 @@ impl Default for DemoWindow {
|
|||
num_columns: 2,
|
||||
|
||||
widgets: Default::default(),
|
||||
colors: Default::default(),
|
||||
layout: Default::default(),
|
||||
tree: Tree::demo(),
|
||||
box_painting: Default::default(),
|
||||
|
@ -351,6 +353,12 @@ impl DemoWindow {
|
|||
self.widgets.ui(ui);
|
||||
});
|
||||
|
||||
CollapsingHeader::new("Colors")
|
||||
.default_open(true)
|
||||
.show(ui, |ui| {
|
||||
self.colors.ui(ui);
|
||||
});
|
||||
|
||||
CollapsingHeader::new("Layout")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| self.layout.ui(ui));
|
||||
|
@ -531,7 +539,7 @@ impl Widgets {
|
|||
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.add(Label::new("Click to select a different text color: ").text_color(self.color));
|
||||
ui.color_edit_button(&mut self.color);
|
||||
ui.color_edit_button_srgba(&mut self.color);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
@ -552,6 +560,78 @@ impl Widgets {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
struct ColorWidgets {
|
||||
srgba_unmul: [u8; 4],
|
||||
srgba_premul: [u8; 4],
|
||||
rgba_unmul: [f32; 4],
|
||||
rgba_premul: [f32; 4],
|
||||
}
|
||||
|
||||
impl Default for ColorWidgets {
|
||||
fn default() -> Self {
|
||||
// Approximately the same color.
|
||||
ColorWidgets {
|
||||
srgba_unmul: [0, 255, 183, 127],
|
||||
srgba_premul: [0, 187, 140, 127],
|
||||
rgba_unmul: [0.0, 1.0, 0.5, 0.5],
|
||||
rgba_premul: [0.0, 0.5, 0.25, 0.5],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorWidgets {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
if ui.button("Reset").clicked {
|
||||
*self = Default::default();
|
||||
}
|
||||
|
||||
ui.label("Egui lets you edit colors stored as either sRGBA or linear RGBA and with or without premultiplied alpha");
|
||||
|
||||
let Self {
|
||||
srgba_unmul,
|
||||
srgba_premul,
|
||||
rgba_unmul,
|
||||
rgba_premul,
|
||||
} = self;
|
||||
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.color_edit_button_srgba_unmultiplied(srgba_unmul);
|
||||
ui.label(format!(
|
||||
"sRGBA: {} {} {} {}",
|
||||
srgba_unmul[0], srgba_unmul[1], srgba_unmul[2], srgba_unmul[3],
|
||||
));
|
||||
});
|
||||
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.color_edit_button_srgba_premultiplied(srgba_premul);
|
||||
ui.label(format!(
|
||||
"sRGBA with premultiplied alpha: {} {} {} {}",
|
||||
srgba_premul[0], srgba_premul[1], srgba_premul[2], srgba_premul[3],
|
||||
));
|
||||
});
|
||||
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.color_edit_button_rgba_unmultiplied(rgba_unmul);
|
||||
ui.label(format!(
|
||||
"Linear RGBA: {:.02} {:.02} {:.02} {:.02}",
|
||||
rgba_unmul[0], rgba_unmul[1], rgba_unmul[2], rgba_unmul[3],
|
||||
));
|
||||
});
|
||||
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.color_edit_button_rgba_premultiplied(rgba_premul);
|
||||
ui.label(format!(
|
||||
"Linear RGBA with premultiplied alpha: {:.02} {:.02} {:.02} {:.02}",
|
||||
rgba_premul[0], rgba_premul[1], rgba_premul[2], rgba_premul[3],
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
struct BoxPainting {
|
||||
|
|
|
@ -208,7 +208,7 @@ impl From<Srgba> for Rgba {
|
|||
linear_from_srgb_byte(srgba[0]),
|
||||
linear_from_srgb_byte(srgba[1]),
|
||||
linear_from_srgb_byte(srgba[2]),
|
||||
srgba[3] as f32 / 255.0,
|
||||
linear_from_alpha_byte(srgba[3]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -219,11 +219,12 @@ impl From<Rgba> for Srgba {
|
|||
srgb_byte_from_linear(rgba[0]),
|
||||
srgb_byte_from_linear(rgba[1]),
|
||||
srgb_byte_from_linear(rgba[2]),
|
||||
clamp(rgba[3] * 255.0, 0.0..=255.0).round() as u8,
|
||||
alpha_byte_from_linear(rgba[3]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// [0, 255] -> [0, 1]
|
||||
fn linear_from_srgb_byte(s: u8) -> f32 {
|
||||
if s <= 10 {
|
||||
s as f32 / 3294.6
|
||||
|
@ -232,6 +233,11 @@ fn linear_from_srgb_byte(s: u8) -> f32 {
|
|||
}
|
||||
}
|
||||
|
||||
fn linear_from_alpha_byte(a: u8) -> f32 {
|
||||
a as f32 / 255.0
|
||||
}
|
||||
|
||||
/// [0, 1] -> [0, 255]
|
||||
fn srgb_byte_from_linear(l: f32) -> u8 {
|
||||
if l <= 0.0 {
|
||||
0
|
||||
|
@ -244,6 +250,10 @@ fn srgb_byte_from_linear(l: f32) -> u8 {
|
|||
}
|
||||
}
|
||||
|
||||
fn alpha_byte_from_linear(a: f32) -> u8 {
|
||||
clamp(a * 255.0, 0.0..=255.0).round() as u8
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_srgba_conversion() {
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
@ -274,19 +284,31 @@ impl Hsva {
|
|||
pub fn new(h: f32, s: f32, v: f32, a: f32) -> Self {
|
||||
Self { h, s, v, a }
|
||||
}
|
||||
|
||||
/// From `sRGBA` with premultiplied alpha
|
||||
pub fn from_srgba_premultiplied(srgba: [u8; 4]) -> Self {
|
||||
Self::from_rgba_premultiplied([
|
||||
linear_from_srgb_byte(srgba[0]),
|
||||
linear_from_srgb_byte(srgba[1]),
|
||||
linear_from_srgb_byte(srgba[2]),
|
||||
linear_from_alpha_byte(srgba[3]),
|
||||
])
|
||||
}
|
||||
|
||||
impl From<Hsva> for Rgba {
|
||||
fn from(hsva: Hsva) -> Rgba {
|
||||
let Hsva { h, s, v, a } = hsva;
|
||||
let (r, g, b) = rgb_from_hsv((h, s, v));
|
||||
Rgba::new(a * r, a * g, a * b, a)
|
||||
/// From `sRGBA` without premultiplied alpha
|
||||
pub fn from_srgba_unmultiplied(srgba: [u8; 4]) -> Self {
|
||||
Self::from_rgba_unmultiplied([
|
||||
linear_from_srgb_byte(srgba[0]),
|
||||
linear_from_srgb_byte(srgba[1]),
|
||||
linear_from_srgb_byte(srgba[2]),
|
||||
linear_from_alpha_byte(srgba[3]),
|
||||
])
|
||||
}
|
||||
}
|
||||
impl From<Rgba> for Hsva {
|
||||
fn from(rgba: Rgba) -> Hsva {
|
||||
|
||||
/// From linear RGBA with premultiplied alpha
|
||||
pub fn from_rgba_premultiplied(rgba: [f32; 4]) -> Self {
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
let Rgba([r, g, b, a]) = rgba;
|
||||
let [r, g, b, a] = rgba;
|
||||
if a == 0.0 {
|
||||
Hsva::default()
|
||||
} else {
|
||||
|
@ -294,6 +316,56 @@ impl From<Rgba> for Hsva {
|
|||
Hsva { h, s, v, a }
|
||||
}
|
||||
}
|
||||
|
||||
/// From linear RGBA without premultiplied alpha
|
||||
pub fn from_rgba_unmultiplied(rgba: [f32; 4]) -> Self {
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
let [r, g, b, a] = rgba;
|
||||
let (h, s, v) = hsv_from_rgb((r, g, b));
|
||||
Hsva { h, s, v, a }
|
||||
}
|
||||
|
||||
pub fn to_rgba_premultiplied(&self) -> [f32; 4] {
|
||||
let [r, g, b, a] = self.to_rgba_unmultiplied();
|
||||
[a * r, a * g, a * b, a]
|
||||
}
|
||||
|
||||
pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {
|
||||
let Hsva { h, s, v, a } = *self;
|
||||
let (r, g, b) = rgb_from_hsv((h, s, v));
|
||||
[r, g, b, a]
|
||||
}
|
||||
|
||||
pub fn to_srgba_premultiplied(&self) -> [u8; 4] {
|
||||
let [r, g, b, a] = self.to_rgba_premultiplied();
|
||||
[
|
||||
srgb_byte_from_linear(r),
|
||||
srgb_byte_from_linear(g),
|
||||
srgb_byte_from_linear(b),
|
||||
alpha_byte_from_linear(a),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {
|
||||
let [r, g, b, a] = self.to_rgba_unmultiplied();
|
||||
[
|
||||
srgb_byte_from_linear(r),
|
||||
srgb_byte_from_linear(g),
|
||||
srgb_byte_from_linear(b),
|
||||
alpha_byte_from_linear(a),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hsva> for Rgba {
|
||||
fn from(hsva: Hsva) -> Rgba {
|
||||
Rgba(hsva.to_rgba_premultiplied())
|
||||
}
|
||||
}
|
||||
impl From<Rgba> for Hsva {
|
||||
fn from(rgba: Rgba) -> Hsva {
|
||||
Self::from_rgba_premultiplied(rgba.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hsva> for Srgba {
|
||||
|
|
|
@ -437,7 +437,7 @@ impl Stroke {
|
|||
ui.label(format!("{}: ", text));
|
||||
ui.add(DragValue::f32(width).speed(0.1).range(0.0..=5.0))
|
||||
.tooltip_text("Width");
|
||||
ui.color_edit_button(color);
|
||||
ui.color_edit_button_srgba(color);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -454,6 +454,6 @@ fn ui_slider_vec2(ui: &mut Ui, value: &mut Vec2, range: std::ops::RangeInclusive
|
|||
fn ui_color(ui: &mut Ui, srgba: &mut Srgba, text: &str) {
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.label(format!("{}: ", text));
|
||||
ui.color_edit_button(srgba);
|
||||
ui.color_edit_button_srgba(srgba);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -481,12 +481,6 @@ impl Ui {
|
|||
response
|
||||
}
|
||||
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
pub fn color_edit_button(&mut self, srgba: &mut Srgba) {
|
||||
widgets::color_picker::color_edit_button(self, srgba)
|
||||
}
|
||||
|
||||
/// Ask to allocate a certain amount of space and return a Painter for that region.
|
||||
///
|
||||
/// You may get back a `Painter` with a smaller or larger size than what you desired,
|
||||
|
@ -497,6 +491,63 @@ impl Ui {
|
|||
}
|
||||
}
|
||||
|
||||
/// # Colors
|
||||
impl Ui {
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
pub fn color_edit_button_srgba(&mut self, srgba: &mut Srgba) -> Response {
|
||||
widgets::color_picker::color_edit_button_srgba(self, srgba)
|
||||
}
|
||||
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
pub fn color_edit_button_hsva(&mut self, hsva: &mut Hsva) -> Response {
|
||||
widgets::color_picker::color_edit_button_hsva(self, hsva)
|
||||
}
|
||||
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
/// The given color is in `sRGBA` space with premultiplied alpha
|
||||
pub fn color_edit_button_srgba_premultiplied(&mut self, srgba: &mut [u8; 4]) -> Response {
|
||||
let mut color = Srgba(*srgba);
|
||||
let response = self.color_edit_button_srgba(&mut color);
|
||||
*srgba = color.0;
|
||||
response
|
||||
}
|
||||
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
/// The given color is in `sRGBA` space without premultiplied alpha.
|
||||
/// If unsure, what "premultiplied alpha" is, then this is probably the function you want to use.
|
||||
pub fn color_edit_button_srgba_unmultiplied(&mut self, srgba: &mut [u8; 4]) -> Response {
|
||||
let mut hsva = Hsva::from_srgba_unmultiplied(*srgba);
|
||||
let response = self.color_edit_button_hsva(&mut hsva);
|
||||
*srgba = hsva.to_srgba_unmultiplied();
|
||||
response
|
||||
}
|
||||
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
/// The given color is in linear RGBA space with premultiplied alpha
|
||||
pub fn color_edit_button_rgba_premultiplied(&mut self, rgba: &mut [f32; 4]) -> Response {
|
||||
let mut hsva = Hsva::from_rgba_premultiplied(*rgba);
|
||||
let response = self.color_edit_button_hsva(&mut hsva);
|
||||
*rgba = hsva.to_rgba_premultiplied();
|
||||
response
|
||||
}
|
||||
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
/// The given color is in linear RGBA space without premultiplied alpha.
|
||||
/// If unsure, what "premultiplied alpha" is, then this is probably the function you want to use.
|
||||
pub fn color_edit_button_rgba_unmultiplied(&mut self, rgba: &mut [f32; 4]) -> Response {
|
||||
let mut hsva = Hsva::from_rgba_unmultiplied(*rgba);
|
||||
let response = self.color_edit_button_hsva(&mut hsva);
|
||||
*rgba = hsva.to_rgba_unmultiplied();
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
/// # Adding Containers / Sub-uis:
|
||||
impl Ui {
|
||||
pub fn collapsing<R>(
|
||||
|
|
|
@ -38,7 +38,7 @@ fn background_checkers(painter: &Painter, rect: Rect) {
|
|||
painter.add(PaintCmd::Triangles(triangles));
|
||||
}
|
||||
|
||||
fn show_color(ui: &mut Ui, color: Srgba, desired_size: Vec2) -> Rect {
|
||||
fn show_color(ui: &mut Ui, color: Srgba, desired_size: Vec2) -> Response {
|
||||
let rect = ui.allocate_space(desired_size);
|
||||
background_checkers(ui.painter(), rect);
|
||||
ui.painter().add(PaintCmd::Rect {
|
||||
|
@ -47,7 +47,7 @@ fn show_color(ui: &mut Ui, color: Srgba, desired_size: Vec2) -> Rect {
|
|||
fill: color,
|
||||
stroke: Stroke::new(3.0, color.to_opaque()),
|
||||
});
|
||||
rect
|
||||
ui.interact_hover(rect)
|
||||
}
|
||||
|
||||
fn color_button(ui: &mut Ui, color: Srgba) -> Response {
|
||||
|
@ -184,29 +184,38 @@ fn color_slider_2d(
|
|||
response
|
||||
}
|
||||
|
||||
fn color_picker_hsva_2d(ui: &mut Ui, hsva: &mut Hsva) {
|
||||
fn color_picker_hsvag_2d(ui: &mut Ui, hsva: &mut HsvaGamma) {
|
||||
ui.vertical_centered(|ui| {
|
||||
let current_color_size = vec2(
|
||||
ui.style().spacing.slider_width,
|
||||
ui.style().spacing.clickable_diameter * 2.0,
|
||||
);
|
||||
let current_color_rect = show_color(ui, (*hsva).into(), current_color_size);
|
||||
if ui.hovered(current_color_rect) {
|
||||
show_tooltip_text(ui.ctx(), "Current color");
|
||||
}
|
||||
|
||||
let opaque = Hsva { a: 1.0, ..*hsva };
|
||||
let Hsva { h, s, v, a } = hsva;
|
||||
color_slider_2d(ui, h, s, |h, s| Hsva::new(h, s, 1.0, 1.0).into())
|
||||
show_color(ui, (*hsva).into(), current_color_size).tooltip_text("Current color");
|
||||
|
||||
show_color(ui, HsvaGamma { a: 1.0, ..*hsva }.into(), current_color_size)
|
||||
.tooltip_text("Current color (opaque)");
|
||||
|
||||
let opaque = HsvaGamma { a: 1.0, ..*hsva };
|
||||
let HsvaGamma { h, s, v, a } = hsva;
|
||||
color_slider_2d(ui, h, s, |h, s| HsvaGamma::new(h, s, 1.0, 1.0).into())
|
||||
.tooltip_text("Hue - Saturation");
|
||||
color_slider_2d(ui, v, s, |v, s| Hsva { v, s, ..opaque }.into())
|
||||
color_slider_2d(ui, v, s, |v, s| HsvaGamma { v, s, ..opaque }.into())
|
||||
.tooltip_text("Value - Saturation");
|
||||
ui.label("Alpha:");
|
||||
color_slider_1d(ui, a, |a| Hsva { a, ..opaque }.into()).tooltip_text("Alpha");
|
||||
color_slider_1d(ui, h, |h| HsvaGamma { h, ..opaque }.into()).tooltip_text("Hue");
|
||||
color_slider_1d(ui, s, |s| HsvaGamma { s, ..opaque }.into()).tooltip_text("Saturation");
|
||||
color_slider_1d(ui, v, |v| HsvaGamma { v, ..opaque }.into()).tooltip_text("Value");
|
||||
color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).tooltip_text("Alpha");
|
||||
});
|
||||
}
|
||||
|
||||
fn color_picker_hsva(ui: &mut Ui, hsva: &mut Hsva) {
|
||||
fn color_picker_hsva_2d(ui: &mut Ui, hsva: &mut Hsva) {
|
||||
let mut hsvag = HsvaGamma::from(*hsva);
|
||||
color_picker_hsvag_2d(ui, &mut hsvag);
|
||||
*hsva = Hsva::from(hsvag);
|
||||
}
|
||||
|
||||
pub fn color_edit_button_hsva(ui: &mut Ui, hsva: &mut Hsva) -> Response {
|
||||
let id = ui.make_position_id().with("foo");
|
||||
let button_response = color_button(ui, (*hsva).into()).tooltip_text("Click to edit color");
|
||||
|
||||
|
@ -231,12 +240,13 @@ fn color_picker_hsva(ui: &mut Ui, hsva: &mut Hsva) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
button_response
|
||||
}
|
||||
|
||||
// TODO: return Response so user can show a tooltip
|
||||
/// Shows a button with the given color.
|
||||
/// If the user clicks the button, a full color picker is shown.
|
||||
pub fn color_edit_button(ui: &mut Ui, srgba: &mut Srgba) {
|
||||
pub fn color_edit_button_srgba(ui: &mut Ui, srgba: &mut Srgba) -> Response {
|
||||
// To ensure we keep hue slider when `srgba` is grey we store the
|
||||
// full `Hsva` in a cache:
|
||||
|
||||
|
@ -248,9 +258,93 @@ pub fn color_edit_button(ui: &mut Ui, srgba: &mut Srgba) {
|
|||
.cloned()
|
||||
.unwrap_or_else(|| Hsva::from(*srgba));
|
||||
|
||||
color_picker_hsva(ui, &mut hsva);
|
||||
let response = color_edit_button_hsva(ui, &mut hsva);
|
||||
|
||||
*srgba = Srgba::from(hsva);
|
||||
|
||||
ui.ctx().memory().color_cache.set(*srgba, hsva);
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Like Hsva but with the `v` (value/brightness) being gamma corrected
|
||||
/// so that it is perceptually even in sliders.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
struct HsvaGamma {
|
||||
/// hue 0-1
|
||||
pub h: f32,
|
||||
/// saturation 0-1
|
||||
pub s: f32,
|
||||
/// value 0-1, in gamma-space (perceptually even)
|
||||
pub v: f32,
|
||||
/// alpha 0-1
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
impl HsvaGamma {
|
||||
pub fn new(h: f32, s: f32, v: f32, a: f32) -> Self {
|
||||
Self { h, s, v, a }
|
||||
}
|
||||
}
|
||||
|
||||
// const GAMMA: f32 = 2.2;
|
||||
|
||||
impl From<HsvaGamma> for Rgba {
|
||||
fn from(hsvag: HsvaGamma) -> Rgba {
|
||||
Hsva::from(hsvag).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HsvaGamma> for Srgba {
|
||||
fn from(hsvag: HsvaGamma) -> Srgba {
|
||||
Rgba::from(hsvag).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HsvaGamma> for Hsva {
|
||||
fn from(hsvag: HsvaGamma) -> Hsva {
|
||||
let HsvaGamma { h, s, v, a } = hsvag;
|
||||
Hsva {
|
||||
h,
|
||||
s,
|
||||
v: linear_from_srgb(v),
|
||||
a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hsva> for HsvaGamma {
|
||||
fn from(hsva: Hsva) -> HsvaGamma {
|
||||
let Hsva { h, s, v, a } = hsva;
|
||||
HsvaGamma {
|
||||
h,
|
||||
s,
|
||||
v: srgb_from_linear(v),
|
||||
a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [0, 1] -> [0, 1]
|
||||
fn linear_from_srgb(s: f32) -> f32 {
|
||||
if s < 0.0 {
|
||||
-linear_from_srgb(-s)
|
||||
} else if s <= 0.04045 {
|
||||
s / 12.92
|
||||
} else {
|
||||
((s + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
}
|
||||
|
||||
/// [0, 1] -> [0, 1]
|
||||
fn srgb_from_linear(l: f32) -> f32 {
|
||||
if l < 0.0 {
|
||||
-srgb_from_linear(-l)
|
||||
} else if l <= 0.0031308 {
|
||||
12.92 * l
|
||||
} else {
|
||||
1.055 * l.powf(1.0 / 2.4) - 0.055
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue