Skip to content

Commit dd7645d

Browse files
bors[bot]Ogeon
andauthored
Merge #176
176: Rewrite the conversion traits to work more like From and Into r=Ogeon a=Ogeon This replaces the old `FromColor` and `IntoColor` traits, as well as the implementations of `From` and `Into` between color types, with new traits that are more flexible. The new `FromColor` and `IntoColor` traits will also clamp the output to prevent out-of-bounds results. It's a _very_ breaking change. To migrate from the old system, any failing uses `from` and `into` should be replaced with `from_color` and `into_color`. Any custom color that used to implement `IntoColor` should switch to implementing `FromColorUnclamped`. A new trait, called `Transparency`, has also been added to make it possible to implement `FromColorUnclamped` for `Alpha` without running into conflicting implementations. It makes it possible to convert from any color type that implements `Transparency`, and not just `Alpha<A, T>` to `Alpha<B, T>`, for example. The semantics of the new traits are a bit different, so it may affect type inference. It may also be necessary to convert using more than one step. This is mostly for the RGB family and Luma, when changing RGB space or standard. The syntax for the helper attributes were also changed to the more common `#[palette(...)]` pattern. It helped when implementing them and looks a bit nicer, IMO. Closes #41, fixes #111. Co-authored-by: Erik Hedvall <erikwhedvall@gmail.com>
2 parents 023b002 + 3fe3039 commit dd7645d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2926
-2869
lines changed

README.md

+11-12
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ Palette provides tools for both color manipulation and conversion between color
5959

6060
RGB is probably the most widely known color space, but it's not the only one. You have probably used a color picker with a rainbow wheel and a brightness slider. That may have been an HSV or an HSL color picker, where the color is encoded as hue, saturation and brightness/lightness. There's also a group of color spaces that are designed to be perceptually uniform, meaning that the perceptual change is equal to the numerical change.
6161

62-
Selecting the proper color space can have a big impact on how the resulting image looks (as illustrated by some of the programs in `examples`), and Palette makes the conversion between them as easy as a call to `from` or `into`.
62+
Selecting the proper color space can have a big impact on how the resulting image looks (as illustrated by some of the programs in `examples`), and Palette makes the conversion between them as easy as a call to `from_color` or `into_color`.
6363

6464
This example takes an sRGB color, converts it to CIE L\*C\*h°, shifts its hue by 180° and converts it back to RGB:
6565

6666
```Rust
67-
use palette::{Srgb, LinSrgb, Lch, Hue};
67+
use palette::{FromColor, Hue, IntoColor, Lch, Srgb};
6868

69-
let lch_color: Lch = Srgb::new(0.8, 0.2, 0.1).into();
70-
let new_color = LinSrgb::from(lch_color.shift_hue(180.0));
69+
let lch_color: Lch = Srgb::new(0.8, 0.2, 0.1).into_color();
70+
let new_color = Srgb::from_color(lch_color.shift_hue(180.0));
7171
```
7272

7373
This results in the following two colors:
@@ -78,16 +78,14 @@ This results in the following two colors:
7878

7979
Palette comes with a number of color manipulation tools, that are implemented as traits. These includes lighten/darken, saturate/desaturate and hue shift. These traits are only implemented on types where they are meaningful, which means that you can't shift the hue of an RGB color without converting it to a color space where it makes sense.
8080

81-
This may seem limiting, but the point is to avoid inconsistent behavior due to arbitrary defaults, such as saturating a gray color to red when there is no available hue information. The abstract `Color` type does still support every operation, for when this is less important.
82-
83-
The following example shows how the `Color` type is used to make a lighter and a desaturated version of the original.
81+
The following example shows how to make a lighter and a desaturated version of the original.
8482

8583
```Rust
86-
use palette::{Saturate, Shade, Srgb, Lch};
84+
use palette::{FromColor, Saturate, Shade, Srgb, Lch};
8785

8886
let color = Srgb::new(0.8, 0.2, 0.1).into_linear();
8987
let lighter = color.lighten(0.1);
90-
let desaturated = Lch::from(color).desaturate(0.5);
88+
let desaturated = Lch::from_color(color).desaturate(0.5);
9189
```
9290

9391
This results in the following three colors:
@@ -101,16 +99,16 @@ There is also a linear gradient type which makes it easy to interpolate between
10199
The following example shows three gradients between the same two endpoints, but the top is in RGB space while the middle and bottom are in HSV space. The bottom gradient is an example of using the color sequence iterator.
102100

103101
```Rust
104-
use palette::{LinSrgb, Hsv, Gradient};
102+
use palette::{FromColor, LinSrgb, Hsv, Gradient};
105103

106104
let grad1 = Gradient::new(vec![
107105
LinSrgb::new(1.0, 0.1, 0.1),
108106
LinSrgb::new(0.1, 1.0, 1.0)
109107
]);
110108

111109
let grad2 = Gradient::new(vec![
112-
Hsv::from(LinSrgb::new(1.0, 0.1, 0.1)),
113-
Hsv::from(LinSrgb::new(0.1, 1.0, 1.0))
110+
Hsv::from_color(LinSrgb::new(1.0, 0.1, 0.1)),
111+
Hsv::from_color(LinSrgb::new(0.1, 1.0, 1.0))
114112
]);
115113
```
116114

@@ -125,6 +123,7 @@ Palette supports converting from a raw buffer of data into a color type using th
125123
Oftentimes, pixel data is stored in a raw buffer such as a `[u8; 3]`. `from_raw` can be used to convert into a Palette color, `into_format` converts from `Srgb<u8>` to `Srgb<f32>`, and finally `into_raw` to convert from a Palette color back to a `[u8;3]`.
126124

127125
Here's an example of turning a buffer of `[u8; 3]` into a Palette `Srgb` color and back to a raw buffer.
126+
128127
```rust
129128
use approx::assert_relative_eq;
130129
use palette::{Srgb, Pixel};

palette/examples/color_scheme.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use palette::{Hue, Lch, LinSrgb, Pixel, Shade, Srgb};
1+
use palette::{Hue, IntoColor, Lch, LinSrgb, Pixel, Shade, Srgb};
22

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

@@ -92,7 +92,7 @@ fn main() {
9292
let primary: Lch = Srgb::new(red, green, blue)
9393
.into_format::<f32>()
9494
.into_linear()
95-
.into();
95+
.into_color();
9696

9797
//Generate the secondary colors, depending on the input arguments
9898
let secondary = match matches.subcommand() {
@@ -144,14 +144,14 @@ fn main() {
144144

145145
//Draw the primary swatches
146146
blit_shades(
147-
primary.into(),
147+
primary.into_color(),
148148
image.sub_image(0, 0, SWATCH_SIZE, SWATCH_SIZE),
149149
);
150150

151151
//Draw the secondary swatches
152152
for (n, color) in secondary.into_iter().enumerate() {
153153
blit_shades(
154-
color.into(),
154+
color.into_color(),
155155
image.sub_image((n as u32 + 1) * SWATCH_SIZE, 0, SWATCH_SIZE, SWATCH_SIZE),
156156
);
157157
}

palette/examples/gradient.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fn main() {
55

66
#[cfg(feature = "std")]
77
fn main() {
8-
use palette::{Gradient, Lch, LinSrgb, Pixel, Srgb};
8+
use palette::{FromColor, Gradient, IntoColor, Lch, LinSrgb, Pixel, Srgb};
99

1010
use image::{GenericImage, GenericImageView, RgbImage};
1111

@@ -26,17 +26,17 @@ fn main() {
2626
//The same colors and offsets as in grad1, but in a color space where the hue
2727
// is a component
2828
let grad3 = Gradient::new(vec![
29-
Lch::from(LinSrgb::new(1.0, 0.1, 0.1)),
30-
Lch::from(LinSrgb::new(0.1, 0.1, 1.0)),
31-
Lch::from(LinSrgb::new(0.1, 1.0, 0.1)),
29+
Lch::from_color(LinSrgb::new(1.0, 0.1, 0.1)),
30+
Lch::from_color(LinSrgb::new(0.1, 0.1, 1.0)),
31+
Lch::from_color(LinSrgb::new(0.1, 1.0, 0.1)),
3232
]);
3333

3434
//The same colors and and color space as in grad3, but with the blue point
3535
// shifted down
3636
let grad4 = Gradient::with_domain(vec![
37-
(0.0, Lch::from(LinSrgb::new(1.0, 0.1, 0.1))),
38-
(0.25, Lch::from(LinSrgb::new(0.1, 0.1, 1.0))),
39-
(1.0, Lch::from(LinSrgb::new(0.1, 1.0, 0.1))),
37+
(0.0, Lch::from_color(LinSrgb::new(1.0, 0.1, 0.1))),
38+
(0.25, Lch::from_color(LinSrgb::new(0.1, 0.1, 1.0))),
39+
(1.0, Lch::from_color(LinSrgb::new(0.1, 1.0, 0.1))),
4040
]);
4141

4242
let mut image = RgbImage::new(256, 128);
@@ -49,8 +49,8 @@ fn main() {
4949
{
5050
let c1 = Srgb::from_linear(c1).into_format().into_raw();
5151
let c2 = Srgb::from_linear(c2).into_format().into_raw();
52-
let c3 = Srgb::from_linear(c3.into()).into_format().into_raw();
53-
let c4 = Srgb::from_linear(c4.into()).into_format().into_raw();
52+
let c3 = Srgb::from_linear(c3.into_color()).into_format().into_raw();
53+
let c4 = Srgb::from_linear(c4.into_color()).into_format().into_raw();
5454

5555
{
5656
let mut sub_image = image.sub_image(i as u32, 0, 1, 31);

palette/examples/hue.rs

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

33
fn main() {
44
let mut image = image::open("res/fruits.png")
@@ -12,11 +12,15 @@ 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).shift_hue(180.0);
16-
Srgb::from_linear(saturated.into()).into_format().into_raw()
15+
let saturated = Hsl::from_color(color).shift_hue(180.0);
16+
Srgb::from_linear(saturated.into_color())
17+
.into_format()
18+
.into_raw()
1719
} else {
18-
let saturated = Lch::from(color).shift_hue(180.0);
19-
Srgb::from_linear(saturated.into()).into_format().into_raw()
20+
let saturated = Lch::from_color(color).shift_hue(180.0);
21+
Srgb::from_linear(saturated.into_color())
22+
.into_format()
23+
.into_raw()
2024
};
2125
}
2226

palette/examples/readme_examples.rs

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
11
use image::{GenericImage, GenericImageView, RgbImage};
22

33
#[cfg(feature = "std")]
4-
use palette::{Gradient, LinSrgb, Mix};
4+
use palette::{FromColor, Gradient, IntoColor, LinSrgb, Mix};
55
use palette::{Pixel, Srgb};
66

77
mod color_spaces {
88
use crate::display_colors;
9-
use palette::{Hue, Lch, LinSrgb, Srgb};
9+
use palette::{FromColor, Hue, IntoColor, Lch, Srgb};
1010

1111
pub fn run() {
12-
let lch_color: Lch = Srgb::new(0.8, 0.2, 0.1).into();
13-
let new_color = LinSrgb::from(lch_color.shift_hue(180.0));
12+
let lch_color: Lch = Srgb::new(0.8, 0.2, 0.1).into_color();
13+
let new_color = Srgb::from_color(lch_color.shift_hue(180.0));
1414

1515
display_colors(
1616
"examples/readme_color_spaces.png",
1717
&[
1818
::palette::Srgb::new(0.8, 0.2, 0.1).into_format(),
19-
Srgb::from_linear(new_color.into()).into_format(),
19+
new_color.into_format(),
2020
],
2121
);
2222
}
2323
}
2424

2525
mod manipulation {
2626
use crate::display_colors;
27-
use palette::{Lch, Saturate, Shade, Srgb};
27+
use palette::{FromColor, IntoColor, Lch, Saturate, Shade, Srgb};
2828

2929
pub fn run() {
3030
let color = Srgb::new(0.8, 0.2, 0.1).into_linear();
3131
let lighter = color.lighten(0.1);
32-
let desaturated = Lch::from(color).desaturate(0.5);
32+
let desaturated = Lch::from_color(color).desaturate(0.5);
3333

3434
display_colors(
3535
"examples/readme_manipulation.png",
3636
&[
3737
Srgb::from_linear(color.into()).into_format(),
3838
Srgb::from_linear(lighter.into()).into_format(),
39-
Srgb::from_linear(desaturated.into()).into_format(),
39+
Srgb::from_linear(desaturated.into_color()).into_format(),
4040
],
4141
);
4242
}
@@ -45,7 +45,7 @@ mod manipulation {
4545
#[cfg(feature = "std")]
4646
mod gradients {
4747
use crate::display_gradients;
48-
use palette::{Gradient, Hsv, LinSrgb};
48+
use palette::{FromColor, Gradient, Hsv, LinSrgb};
4949

5050
pub fn run() {
5151
let grad1 = Gradient::new(vec![
@@ -54,8 +54,8 @@ mod gradients {
5454
]);
5555

5656
let grad2 = Gradient::new(vec![
57-
Hsv::from(LinSrgb::new(1.0, 0.1, 0.1)),
58-
Hsv::from(LinSrgb::new(0.1, 1.0, 1.0)),
57+
Hsv::from_color(LinSrgb::new(1.0, 0.1, 0.1)),
58+
Hsv::from_color(LinSrgb::new(0.1, 1.0, 1.0)),
5959
]);
6060

6161
display_gradients("examples/readme_gradients.png", grad1, grad2);
@@ -86,8 +86,8 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
8686
grad1: Gradient<A>,
8787
grad2: Gradient<B>,
8888
) where
89-
LinSrgb: From<A>,
90-
LinSrgb: From<B>,
89+
LinSrgb: FromColor<A>,
90+
LinSrgb: FromColor<B>,
9191
{
9292
let mut image = RgbImage::new(256, 96);
9393
{
@@ -99,7 +99,7 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
9999
x,
100100
y,
101101
image::Rgb(
102-
Srgb::from_linear(grad1.get(x as f32 / 255.0).into())
102+
Srgb::from_linear(grad1.get(x as f32 / 255.0).into_color())
103103
.into_format()
104104
.into_raw(),
105105
),
@@ -117,7 +117,7 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
117117
x,
118118
y,
119119
image::Rgb(
120-
Srgb::from_linear(grad2.get(x as f32 / 255.0).into())
120+
Srgb::from_linear(grad2.get(x as f32 / 255.0).into_color())
121121
.into_format()
122122
.into_raw(),
123123
),
@@ -131,7 +131,7 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
131131
let swatch_size = 32;
132132
let mut v1 = Vec::new();
133133
for color in grad2.take(8) {
134-
let pix: [u8; 3] = Srgb::from_linear(LinSrgb::from(color))
134+
let pix: [u8; 3] = Srgb::from_linear(LinSrgb::from_color(color))
135135
.into_format()
136136
.into_raw();
137137
v1.push(pix);

palette/examples/saturate.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use palette::{Hsl, Lch, Pixel, Saturate, Srgb};
1+
use palette::{Hsl, IntoColor, Lch, Pixel, Saturate, Srgb};
22

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

@@ -20,13 +20,17 @@ fn main() {
2020
let color: Hsl = Srgb::from_raw(&sub_image.get_pixel(x, y).0)
2121
.into_format()
2222
.into_linear()
23-
.into();
23+
.into_color();
2424

2525
let saturated = color.saturate(0.8);
2626
sub_image.put_pixel(
2727
x,
2828
y,
29-
image::Rgb(Srgb::from_linear(saturated.into()).into_format().into_raw()),
29+
image::Rgb(
30+
Srgb::from_linear(saturated.into_color())
31+
.into_format()
32+
.into_raw(),
33+
),
3034
);
3135
}
3236
}
@@ -40,13 +44,17 @@ fn main() {
4044
let color: Lch = Srgb::from_raw(&sub_image.get_pixel(x, y).0)
4145
.into_format()
4246
.into_linear()
43-
.into();
47+
.into_color();
4448

4549
let saturated = color.saturate(0.8);
4650
sub_image.put_pixel(
4751
x,
4852
y,
49-
image::Rgb(Srgb::from_linear(saturated.into()).into_format().into_raw()),
53+
image::Rgb(
54+
Srgb::from_linear(saturated.into_color())
55+
.into_format()
56+
.into_raw(),
57+
),
5058
);
5159
}
5260
}

palette/examples/shade.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use palette::{Hsv, Lab, LinSrgb, Pixel, Shade, Srgb};
1+
use palette::{FromColor, Hsv, IntoColor, Lab, LinSrgb, Pixel, Shade, Srgb};
22

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

55
fn main() {
66
//The same color in linear RGB, CIE L*a*b*, and HSV
77
let rgb = LinSrgb::new(0.5, 0.0, 0.0);
8-
let lab = Lab::from(rgb);
9-
let hsv = Hsv::from(rgb);
8+
let lab = Lab::from_color(rgb);
9+
let hsv = Hsv::from_color(rgb);
1010

1111
let mut image = RgbImage::new(220, 193);
1212

@@ -38,10 +38,10 @@ fn main() {
3838
}
3939
}
4040

41-
let lab1 = Srgb::from_linear(lab.darken(0.05 * i as f32).into())
41+
let lab1 = Srgb::from_linear(lab.darken(0.05 * i as f32).into_color())
4242
.into_format()
4343
.into_raw();
44-
let lab2 = Srgb::from_linear(lab.lighten(0.05 * i as f32).into())
44+
let lab2 = Srgb::from_linear(lab.lighten(0.05 * i as f32).into_color())
4545
.into_format()
4646
.into_raw();
4747

@@ -65,10 +65,10 @@ fn main() {
6565
}
6666
}
6767

68-
let hsv1 = Srgb::from_linear(hsv.darken(0.05 * i as f32).into())
68+
let hsv1 = Srgb::from_linear(hsv.darken(0.05 * i as f32).into_color())
6969
.into_format()
7070
.into_raw();
71-
let hsv2 = Srgb::from_linear(hsv.lighten(0.05 * i as f32).into())
71+
let hsv2 = Srgb::from_linear(hsv.lighten(0.05 * i as f32).into_color())
7272
.into_format()
7373
.into_raw();
7474

0 commit comments

Comments
 (0)