Skip to content

Commit 48453b6

Browse files
bors[bot]Ogeon
andauthored
Merge #188
188: Allow HSV, HSL and HWB to represent nonlinear RGB r=Ogeon a=Ogeon This removes the old restriction that meant HSV, HSL and HWB can only represent linear RGB. They can now be converted to and from RGB (and each other) as long as the RGB standard is the same. This restriction is there to still allow type inference and reduce implementation complexity. So converting `Hsl<Srgb>` (not linear) to `Rgb<Srgb>` is still a single step process, while converting to `Rgb<Linear<Srgb>>` is a two step process. There are still shortcuts in place to only change the RGB standard, meaning it's possible to write `Hsl::<Linear<Srgb>>::from_color(Hsl::<Srgb>::new(...))`, similar to how it worked before. This will mean that existing code may have a different output, adding more breaking changes to the pile. Fixes #160, fixes #187 Co-authored-by: Erik Hedvall <[email protected]>
2 parents 71b1434 + ae930c2 commit 48453b6

File tree

14 files changed

+437
-408
lines changed

14 files changed

+437
-408
lines changed

palette/benches/rgb.rs

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
22
use palette::convert::FromColorUnclamped;
33
use palette::encoding;
4-
use palette::{Hsl, Hsv, Hwb, LinSrgb, Srgb};
4+
use palette::{Hsl, Hsv, Hwb, IntoColor, LinSrgb, Srgb};
5+
6+
type SrgbHsv = Hsv<encoding::Srgb>;
7+
type SrgbHsl = Hsl<encoding::Srgb>;
8+
type SrgbHwb = Hwb<encoding::Srgb>;
9+
type LinHsv = Hsv<encoding::Linear<encoding::Srgb>>;
10+
type LinHsl = Hsl<encoding::Linear<encoding::Srgb>>;
11+
type LinHwb = Hwb<encoding::Linear<encoding::Srgb>>;
512

613
#[path = "../tests/convert/data_color_mine.rs"]
714
#[allow(dead_code)]
@@ -19,6 +26,12 @@ use data_color_mine::{load_data, ColorMine};
1926
- xyz to rgb
2027
- hsl to rgb
2128
- hsv to rgb
29+
- hsv to linear hsv
30+
- linear hsv to hsv
31+
- hsl to linear hsl
32+
- linear hsl to hsl
33+
- hwb to linear hwb
34+
- linear hwb to hwb
2235
- linsrgb to rgb
2336
- rgb_u8 to linsrgb_f32
2437
- linsrgb_f32 to rgb_u8
@@ -27,22 +40,25 @@ use data_color_mine::{load_data, ColorMine};
2740
fn rgb_conversion(c: &mut Criterion) {
2841
let mut group = c.benchmark_group("Rgb family");
2942
let colormine: Vec<ColorMine> = load_data();
30-
let rgb: Vec<Srgb> = colormine.iter().map(|x| Srgb::from(x.linear_rgb)).collect();
31-
let rgb_u8: Vec<Srgb<u8>> = rgb.iter().map(|x| x.into_format().into()).collect();
43+
let rgb_u8: Vec<Srgb<u8>> = colormine.iter().map(|x| x.rgb.into_format()).collect();
44+
45+
let linear_hsv: Vec<LinHsv> = colormine.iter().map(|x| x.hsv.into_color()).collect();
46+
let linear_hsl: Vec<LinHsl> = colormine.iter().map(|x| x.hsl.into_color()).collect();
47+
let linear_hwb: Vec<LinHwb> = colormine.iter().map(|x| x.hwb.into_color()).collect();
3248

3349
group.throughput(Throughput::Elements(colormine.len() as u64));
3450

35-
group.bench_with_input("rgb to linsrgb", &rgb, |b, rgb| {
51+
group.bench_with_input("rgb to linsrgb", &colormine, |b, colormine| {
3652
b.iter(|| {
37-
for c in rgb {
38-
black_box(c.into_linear());
53+
for c in colormine {
54+
black_box(c.rgb.into_linear());
3955
}
4056
})
4157
});
42-
group.bench_with_input("rgb to hsl", &rgb, |b, rgb| {
58+
group.bench_with_input("rgb to hsl", &colormine, |b, colormine| {
4359
b.iter(|| {
44-
for c in rgb {
45-
black_box(Hsl::from_color_unclamped(*c));
60+
for c in colormine {
61+
black_box(Hsl::from_color_unclamped(c.rgb));
4662
}
4763
})
4864
});
@@ -53,10 +69,10 @@ fn rgb_conversion(c: &mut Criterion) {
5369
}
5470
})
5571
});
56-
group.bench_with_input("rgb to hsv", &rgb, |b, rgb| {
72+
group.bench_with_input("rgb to hsv", &colormine, |b, colormine| {
5773
b.iter(|| {
58-
for c in rgb {
59-
black_box(Hsv::from_color_unclamped(*c));
74+
for c in colormine {
75+
black_box(Hsv::from_color_unclamped(c.rgb));
6076
}
6177
})
6278
});
@@ -102,6 +118,48 @@ fn rgb_conversion(c: &mut Criterion) {
102118
}
103119
})
104120
});
121+
group.bench_with_input("hsv to linear hsv", &colormine, |b, colormine| {
122+
b.iter(|| {
123+
for c in colormine {
124+
black_box(LinHsv::from_color_unclamped(c.hsv));
125+
}
126+
})
127+
});
128+
group.bench_with_input("linear hsv to hsv", &linear_hsv, |b, linear_hsv| {
129+
b.iter(|| {
130+
for &c in linear_hsv {
131+
black_box(SrgbHsv::from_color_unclamped(c));
132+
}
133+
})
134+
});
135+
group.bench_with_input("hsl to linear hsl", &colormine, |b, colormine| {
136+
b.iter(|| {
137+
for c in colormine {
138+
black_box(LinHsl::from_color_unclamped(c.hsl));
139+
}
140+
})
141+
});
142+
group.bench_with_input("linear hsl to hsl", &linear_hsl, |b, linear_hsl| {
143+
b.iter(|| {
144+
for &c in linear_hsl {
145+
black_box(SrgbHsl::from_color_unclamped(c));
146+
}
147+
})
148+
});
149+
group.bench_with_input("hwb to linear hwb", &colormine, |b, colormine| {
150+
b.iter(|| {
151+
for c in colormine {
152+
black_box(LinHwb::from_color_unclamped(c.hwb));
153+
}
154+
})
155+
});
156+
group.bench_with_input("linear hwb to hwb", &linear_hwb, |b, linear_hwb| {
157+
b.iter(|| {
158+
for &c in linear_hwb {
159+
black_box(SrgbHwb::from_color_unclamped(c));
160+
}
161+
})
162+
});
105163
group.bench_with_input("linsrgb to rgb", &colormine, |b, colormine| {
106164
b.iter(|| {
107165
for c in colormine {

palette/examples/hue.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use palette::{FromColor, Hsl, Hue, IntoColor, Lch, Pixel, Srgb};
1+
use palette::{FromColor, Hsl, Hue, Lch, Pixel, Srgb};
22

33
fn main() {
44
let mut image = image::open("res/fruits.png")
@@ -12,15 +12,11 @@ fn main() {
1212
let color = Srgb::from_raw(&pixel.0).into_format();
1313

1414
pixel.0 = if x < y {
15-
let saturated = Hsl::from_color(color).shift_hue(180.0);
16-
Srgb::from_linear(saturated.into_color())
17-
.into_format()
18-
.into_raw()
15+
let hue_shifted = Hsl::from_color(color).shift_hue(180.0);
16+
Srgb::from_color(hue_shifted).into_format().into_raw()
1917
} else {
20-
let saturated = Lch::from_color(color).shift_hue(180.0);
21-
Srgb::from_linear(saturated.into_color())
22-
.into_format()
23-
.into_raw()
18+
let hue_shifted = Lch::from_color(color).shift_hue(180.0);
19+
Srgb::from_color(hue_shifted).into_format().into_raw()
2420
};
2521
}
2622

palette/examples/saturate.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use palette::{Hsl, IntoColor, Lch, Pixel, Saturate, Srgb};
1+
use palette::{FromColor, Hsl, IntoColor, Lch, Pixel, Saturate, Srgb};
22

33
use image::{GenericImage, GenericImageView};
44

@@ -19,18 +19,13 @@ fn main() {
1919
for y in 0..height {
2020
let color: Hsl = Srgb::from_raw(&sub_image.get_pixel(x, y).0)
2121
.into_format()
22-
.into_linear()
2322
.into_color();
2423

2524
let saturated = color.saturate(0.8);
2625
sub_image.put_pixel(
2726
x,
2827
y,
29-
image::Rgb(
30-
Srgb::from_linear(saturated.into_color())
31-
.into_format()
32-
.into_raw(),
33-
),
28+
image::Rgb(Srgb::from_color(saturated).into_format().into_raw()),
3429
);
3530
}
3631
}
@@ -43,18 +38,13 @@ fn main() {
4338
for y in 0..height {
4439
let color: Lch = Srgb::from_raw(&sub_image.get_pixel(x, y).0)
4540
.into_format()
46-
.into_linear()
4741
.into_color();
4842

4943
let saturated = color.saturate(0.8);
5044
sub_image.put_pixel(
5145
x,
5246
y,
53-
image::Rgb(
54-
Srgb::from_linear(saturated.into_color())
55-
.into_format()
56-
.into_raw(),
57-
),
47+
image::Rgb(Srgb::from_color(saturated).into_format().into_raw()),
5848
);
5949
}
6050
}

palette/src/convert.rs

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,39 @@
1515
//! They can be attached to either the item itself, or to the fields.
1616
//!
1717
//! ```
18-
//! # use palette::rgb::RgbSpace;
18+
//! # use palette::rgb::{RgbStandard, RgbSpace};
1919
//! # use palette::convert::FromColorUnclamped;
2020
//! # use palette::{Xyz, Component, FloatComponent};
2121
//! #
2222
//! #[palette(
2323
//! component = "T",
24-
//! rgb_space = "S",
25-
//! white_point = "S::WhitePoint",
24+
//! rgb_standard = "S",
25+
//! white_point = "<S::Space as RgbSpace>::WhitePoint",
2626
//! )]
2727
//! #[derive(FromColorUnclamped)]
2828
//! #[repr(C)]
29-
//! struct ExampleType<S: RgbSpace, T: Component> {
29+
//! struct ExampleType<S: RgbStandard, T: Component> {
3030
//! // ...
3131
//! #[palette(alpha)]
3232
//! alpha: T,
33-
//! space: std::marker::PhantomData<S>,
33+
//! standard: std::marker::PhantomData<S>,
3434
//! }
3535
//!
36-
//! # impl<S: RgbSpace, T: FloatComponent> FromColorUnclamped<Xyz<S::WhitePoint, T>> for ExampleType<S, T> {
37-
//! # fn from_color_unclamped(color: Xyz<S::WhitePoint, T>) -> Self {
38-
//! # ExampleType {alpha: T::max_intensity(), space: std::marker::PhantomData}
36+
//! # impl<S, T> FromColorUnclamped<Xyz<<S::Space as RgbSpace>::WhitePoint, T>> for ExampleType<S, T>
37+
//! # where
38+
//! # S: RgbStandard,
39+
//! # T: FloatComponent
40+
//! # {
41+
//! # fn from_color_unclamped(color: Xyz<<S::Space as RgbSpace>::WhitePoint, T>) -> Self {
42+
//! # ExampleType {alpha: T::max_intensity(), standard: std::marker::PhantomData}
3943
//! # }
4044
//! # }
4145
//! #
42-
//! # impl<S: RgbSpace, T: FloatComponent> FromColorUnclamped<ExampleType<S, T>> for Xyz<S::WhitePoint, T> {
46+
//! # impl<S, T> FromColorUnclamped<ExampleType<S, T>> for Xyz<<S::Space as RgbSpace>::WhitePoint, T>
47+
//! # where
48+
//! # S: RgbStandard,
49+
//! # T: FloatComponent
50+
//! # {
4351
//! # fn from_color_unclamped(color: ExampleType<S, T>) -> Self {
4452
//! # Xyz::default()
4553
//! # }
@@ -60,9 +68,9 @@
6068
//! component type that should be used when deriving. The default is `f32`, but
6169
//! it may be any other type, including type parameters.
6270
//!
63-
//! * `rgb_space = "some::rgb_space::Type"`: Sets the RGB space
71+
//! * `rgb_standard = "some::rgb_standard::Type"`: Sets the RGB standard
6472
//! type that should be used when deriving. The default is to either use `Srgb`
65-
//! or a best effort to convert between spaces, so sometimes it has to be set
73+
//! or a best effort to convert between standards, but sometimes it has to be set
6674
//! to a specific type. This also accepts type parameters.
6775
//!
6876
//! ## Field Attributes
@@ -138,7 +146,7 @@
138146
//! #[palette(
139147
//! skip_derives(Rgb),
140148
//! component = "T",
141-
//! rgb_space = "palette::encoding::Srgb"
149+
//! rgb_standard = "palette::encoding::Srgb"
142150
//! )]
143151
//! #[derive(Copy, Clone, Pixel, FromColorUnclamped)]
144152
//! #[repr(C)] // Makes sure the memory layout is as we want it.
@@ -185,8 +193,8 @@
185193
//! 0.0,
186194
//! 0.0,
187195
//! 0.0,
188-
//! 0.7353569830524495,
189-
//! 0.5370987304831942,
196+
//! 0.5,
197+
//! 0.25,
190198
//! ];
191199
//! let hsv: Hsv<_, f64> = Bgr::from_raw_slice(&buffer)[1].into_color();
192200
//!
@@ -209,7 +217,7 @@
209217
//! /// CSS style sRGB.
210218
//! #[palette(
211219
//! skip_derives(Rgb),
212-
//! rgb_space = "palette::encoding::Srgb"
220+
//! rgb_standard = "palette::encoding::Srgb"
213221
//! )]
214222
//! #[derive(PartialEq, Debug, FromColorUnclamped, WithAlpha)]
215223
//! struct CssRgb {
@@ -528,7 +536,7 @@ mod tests {
528536
skip_derives(Xyz, Luma),
529537
white_point = "S::WhitePoint",
530538
component = "f64",
531-
rgb_space = "S",
539+
rgb_standard = "Linear<S>",
532540
palette_internal,
533541
palette_internal_not_base_type
534542
)]
@@ -597,7 +605,7 @@ mod tests {
597605
skip_derives(Lch, Luma),
598606
white_point = "crate::white_point::E",
599607
component = "T",
600-
rgb_space = "(crate::encoding::Srgb, crate::white_point::E)",
608+
rgb_standard = "Linear<(crate::encoding::Srgb, crate::white_point::E)>",
601609
palette_internal,
602610
palette_internal_not_base_type
603611
)]
@@ -667,7 +675,7 @@ mod tests {
667675
let lch: Lch<_, f64> = Default::default();
668676
WithXyz::<crate::encoding::Srgb>::from_color(lch);
669677

670-
let rgb: Rgb<crate::encoding::Srgb, f64> = Default::default();
678+
let rgb: Rgb<_, f64> = Default::default();
671679
WithXyz::<crate::encoding::Srgb>::from_color(rgb);
672680

673681
let hsl: Hsl<_, f64> = Default::default();
@@ -701,8 +709,7 @@ mod tests {
701709
let lch: Alpha<Lch<_, f64>, u8> = Alpha::from(Lch::default());
702710
WithXyz::<crate::encoding::Srgb>::from_color(lch);
703711

704-
let rgb: Alpha<Rgb<crate::encoding::Srgb, f64>, u8> =
705-
Alpha::from(Rgb::<crate::encoding::Srgb, f64>::default());
712+
let rgb: Alpha<Rgb<_, f64>, u8> = Alpha::from(Rgb::default());
706713
WithXyz::<crate::encoding::Srgb>::from_color(rgb);
707714

708715
let hsl: Alpha<Hsl<_, f64>, u8> = Alpha::from(Hsl::default());
@@ -736,7 +743,7 @@ mod tests {
736743
let lch: Lch<_, f64> = Default::default();
737744
Alpha::<WithXyz<crate::encoding::Srgb>, u8>::from_color(lch);
738745

739-
let rgb: Rgb<crate::encoding::Srgb, f64> = Default::default();
746+
let rgb: Rgb<_, f64> = Default::default();
740747
Alpha::<WithXyz<crate::encoding::Srgb>, u8>::from_color(rgb);
741748

742749
let hsl: Hsl<_, f64> = Default::default();
@@ -770,7 +777,7 @@ mod tests {
770777
let lch: Lch<_, f64> = Default::default();
771778
Alpha::<WithXyz<crate::encoding::Srgb>, u8>::from_color(lch);
772779

773-
let rgb: Rgb<crate::encoding::Srgb, f64> = Default::default();
780+
let rgb: Rgb<_, f64> = Default::default();
774781
Alpha::<WithXyz<crate::encoding::Srgb>, u8>::from_color(rgb);
775782

776783
let hsl: Hsl<_, f64> = Default::default();
@@ -795,7 +802,7 @@ mod tests {
795802
let _yxy: Yxy<_, f64> = color.into_color();
796803
let _lab: Lab<_, f64> = color.into_color();
797804
let _lch: Lch<_, f64> = color.into_color();
798-
let _rgb: Rgb<crate::encoding::Srgb, f64> = color.into_color();
805+
let _rgb: Rgb<_, f64> = color.into_color();
799806
let _hsl: Hsl<_, f64> = color.into_color();
800807
let _hsv: Hsv<_, f64> = color.into_color();
801808
let _hwb: Hwb<_, f64> = color.into_color();
@@ -812,7 +819,7 @@ mod tests {
812819
let _yxy: Yxy<_, f64> = color.into_color();
813820
let _lab: Lab<_, f64> = color.into_color();
814821
let _lch: Lch<_, f64> = color.into_color();
815-
let _rgb: Rgb<crate::encoding::Srgb, f64> = color.into_color();
822+
let _rgb: Rgb<_, f64> = color.into_color();
816823
let _hsl: Hsl<_, f64> = color.into_color();
817824
let _hsv: Hsv<_, f64> = color.into_color();
818825
let _hwb: Hwb<_, f64> = color.into_color();
@@ -828,7 +835,7 @@ mod tests {
828835
let _yxy: Alpha<Yxy<_, f64>, u8> = color.into_color();
829836
let _lab: Alpha<Lab<_, f64>, u8> = color.into_color();
830837
let _lch: Alpha<Lch<_, f64>, u8> = color.into_color();
831-
let _rgb: Alpha<Rgb<crate::encoding::Srgb, f64>, u8> = color.into_color();
838+
let _rgb: Alpha<Rgb<_, f64>, u8> = color.into_color();
832839
let _hsl: Alpha<Hsl<_, f64>, u8> = color.into_color();
833840
let _hsv: Alpha<Hsv<_, f64>, u8> = color.into_color();
834841
let _hwb: Alpha<Hwb<_, f64>, u8> = color.into_color();
@@ -845,7 +852,7 @@ mod tests {
845852
let _yxy: Alpha<Yxy<_, f64>, u8> = color.into_color();
846853
let _lab: Alpha<Lab<_, f64>, u8> = color.into_color();
847854
let _lch: Alpha<Lch<_, f64>, u8> = color.into_color();
848-
let _rgb: Alpha<Rgb<crate::encoding::Srgb, f64>, u8> = color.into_color();
855+
let _rgb: Alpha<Rgb<_, f64>, u8> = color.into_color();
849856
let _hsl: Alpha<Hsl<_, f64>, u8> = color.into_color();
850857
let _hsv: Alpha<Hsv<_, f64>, u8> = color.into_color();
851858
let _hwb: Alpha<Hwb<_, f64>, u8> = color.into_color();
@@ -869,7 +876,7 @@ mod tests {
869876
let lch: Lch<crate::white_point::E, f64> = Default::default();
870877
WithoutXyz::<f64>::from_color(lch);
871878

872-
let rgb: Rgb<(_, crate::encoding::Srgb), f64> = Default::default();
879+
let rgb: Rgb<_, f64> = Default::default();
873880
WithoutXyz::<f64>::from_color(rgb);
874881

875882
let hsl: Hsl<_, f64> = Default::default();
@@ -894,7 +901,7 @@ mod tests {
894901
let _yxy: Yxy<crate::white_point::E, f64> = color.into_color();
895902
let _lab: Lab<crate::white_point::E, f64> = color.into_color();
896903
let _lch: Lch<crate::white_point::E, f64> = color.into_color();
897-
let _rgb: Rgb<(_, crate::encoding::Srgb), f64> = color.into_color();
904+
let _rgb: Rgb<_, f64> = color.into_color();
898905
let _hsl: Hsl<_, f64> = color.into_color();
899906
let _hsv: Hsv<_, f64> = color.into_color();
900907
let _hwb: Hwb<_, f64> = color.into_color();

0 commit comments

Comments
 (0)