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

Rewrite the conversion traits to work more like From and Into #176

Merged
merged 1 commit into from Apr 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 11 additions & 12 deletions README.md
Expand Up @@ -59,15 +59,15 @@ Palette provides tools for both color manipulation and conversion between color

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.

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`.
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`.

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

```Rust
use palette::{Srgb, LinSrgb, Lch, Hue};
use palette::{FromColor, Hue, IntoColor, Lch, Srgb};

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

This results in the following two colors:
Expand All @@ -78,16 +78,14 @@ This results in the following two colors:

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.

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.

The following example shows how the `Color` type is used to make a lighter and a desaturated version of the original.
The following example shows how to make a lighter and a desaturated version of the original.

```Rust
use palette::{Saturate, Shade, Srgb, Lch};
use palette::{FromColor, Saturate, Shade, Srgb, Lch};

let color = Srgb::new(0.8, 0.2, 0.1).into_linear();
let lighter = color.lighten(0.1);
let desaturated = Lch::from(color).desaturate(0.5);
let desaturated = Lch::from_color(color).desaturate(0.5);
```

This results in the following three colors:
Expand All @@ -101,16 +99,16 @@ There is also a linear gradient type which makes it easy to interpolate between
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.

```Rust
use palette::{LinSrgb, Hsv, Gradient};
use palette::{FromColor, LinSrgb, Hsv, Gradient};

let grad1 = Gradient::new(vec![
LinSrgb::new(1.0, 0.1, 0.1),
LinSrgb::new(0.1, 1.0, 1.0)
]);

let grad2 = Gradient::new(vec![
Hsv::from(LinSrgb::new(1.0, 0.1, 0.1)),
Hsv::from(LinSrgb::new(0.1, 1.0, 1.0))
Hsv::from_color(LinSrgb::new(1.0, 0.1, 0.1)),
Hsv::from_color(LinSrgb::new(0.1, 1.0, 1.0))
]);
```

Expand All @@ -125,6 +123,7 @@ Palette supports converting from a raw buffer of data into a color type using th
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]`.

Here's an example of turning a buffer of `[u8; 3]` into a Palette `Srgb` color and back to a raw buffer.

```rust
use approx::assert_relative_eq;
use palette::{Srgb, Pixel};
Expand Down
8 changes: 4 additions & 4 deletions palette/examples/color_scheme.rs
@@ -1,4 +1,4 @@
use palette::{Hue, Lch, LinSrgb, Pixel, Shade, Srgb};
use palette::{Hue, IntoColor, Lch, LinSrgb, Pixel, Shade, Srgb};

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

Expand Down Expand Up @@ -92,7 +92,7 @@ fn main() {
let primary: Lch = Srgb::new(red, green, blue)
.into_format::<f32>()
.into_linear()
.into();
.into_color();

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

//Draw the primary swatches
blit_shades(
primary.into(),
primary.into_color(),
image.sub_image(0, 0, SWATCH_SIZE, SWATCH_SIZE),
);

//Draw the secondary swatches
for (n, color) in secondary.into_iter().enumerate() {
blit_shades(
color.into(),
color.into_color(),
image.sub_image((n as u32 + 1) * SWATCH_SIZE, 0, SWATCH_SIZE, SWATCH_SIZE),
);
}
Expand Down
18 changes: 9 additions & 9 deletions palette/examples/gradient.rs
Expand Up @@ -5,7 +5,7 @@ fn main() {

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

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

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

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

let mut image = RgbImage::new(256, 128);
Expand All @@ -49,8 +49,8 @@ fn main() {
{
let c1 = Srgb::from_linear(c1).into_format().into_raw();
let c2 = Srgb::from_linear(c2).into_format().into_raw();
let c3 = Srgb::from_linear(c3.into()).into_format().into_raw();
let c4 = Srgb::from_linear(c4.into()).into_format().into_raw();
let c3 = Srgb::from_linear(c3.into_color()).into_format().into_raw();
let c4 = Srgb::from_linear(c4.into_color()).into_format().into_raw();

{
let mut sub_image = image.sub_image(i as u32, 0, 1, 31);
Expand Down
14 changes: 9 additions & 5 deletions palette/examples/hue.rs
@@ -1,4 +1,4 @@
use palette::{Hsl, Hue, Lch, Pixel, Srgb};
use palette::{FromColor, Hsl, Hue, IntoColor, Lch, Pixel, Srgb};

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

pixel.0 = if x < y {
let saturated = Hsl::from(color).shift_hue(180.0);
Srgb::from_linear(saturated.into()).into_format().into_raw()
let saturated = Hsl::from_color(color).shift_hue(180.0);
Srgb::from_linear(saturated.into_color())
.into_format()
.into_raw()
} else {
let saturated = Lch::from(color).shift_hue(180.0);
Srgb::from_linear(saturated.into()).into_format().into_raw()
let saturated = Lch::from_color(color).shift_hue(180.0);
Srgb::from_linear(saturated.into_color())
.into_format()
.into_raw()
};
}

Expand Down
32 changes: 16 additions & 16 deletions palette/examples/readme_examples.rs
@@ -1,42 +1,42 @@
use image::{GenericImage, GenericImageView, RgbImage};

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

mod color_spaces {
use crate::display_colors;
use palette::{Hue, Lch, LinSrgb, Srgb};
use palette::{FromColor, Hue, IntoColor, Lch, Srgb};

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

display_colors(
"examples/readme_color_spaces.png",
&[
::palette::Srgb::new(0.8, 0.2, 0.1).into_format(),
Srgb::from_linear(new_color.into()).into_format(),
new_color.into_format(),
],
);
}
}

mod manipulation {
use crate::display_colors;
use palette::{Lch, Saturate, Shade, Srgb};
use palette::{FromColor, IntoColor, Lch, Saturate, Shade, Srgb};

pub fn run() {
let color = Srgb::new(0.8, 0.2, 0.1).into_linear();
let lighter = color.lighten(0.1);
let desaturated = Lch::from(color).desaturate(0.5);
let desaturated = Lch::from_color(color).desaturate(0.5);

display_colors(
"examples/readme_manipulation.png",
&[
Srgb::from_linear(color.into()).into_format(),
Srgb::from_linear(lighter.into()).into_format(),
Srgb::from_linear(desaturated.into()).into_format(),
Srgb::from_linear(desaturated.into_color()).into_format(),
],
);
}
Expand All @@ -45,7 +45,7 @@ mod manipulation {
#[cfg(feature = "std")]
mod gradients {
use crate::display_gradients;
use palette::{Gradient, Hsv, LinSrgb};
use palette::{FromColor, Gradient, Hsv, LinSrgb};

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

let grad2 = Gradient::new(vec![
Hsv::from(LinSrgb::new(1.0, 0.1, 0.1)),
Hsv::from(LinSrgb::new(0.1, 1.0, 1.0)),
Hsv::from_color(LinSrgb::new(1.0, 0.1, 0.1)),
Hsv::from_color(LinSrgb::new(0.1, 1.0, 1.0)),
]);

display_gradients("examples/readme_gradients.png", grad1, grad2);
Expand Down Expand Up @@ -86,8 +86,8 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
grad1: Gradient<A>,
grad2: Gradient<B>,
) where
LinSrgb: From<A>,
LinSrgb: From<B>,
LinSrgb: FromColor<A>,
LinSrgb: FromColor<B>,
{
let mut image = RgbImage::new(256, 96);
{
Expand All @@ -99,7 +99,7 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
x,
y,
image::Rgb(
Srgb::from_linear(grad1.get(x as f32 / 255.0).into())
Srgb::from_linear(grad1.get(x as f32 / 255.0).into_color())
.into_format()
.into_raw(),
),
Expand All @@ -117,7 +117,7 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
x,
y,
image::Rgb(
Srgb::from_linear(grad2.get(x as f32 / 255.0).into())
Srgb::from_linear(grad2.get(x as f32 / 255.0).into_color())
.into_format()
.into_raw(),
),
Expand All @@ -131,7 +131,7 @@ fn display_gradients<A: Mix<Scalar = f32> + Clone, B: Mix<Scalar = f32> + Clone>
let swatch_size = 32;
let mut v1 = Vec::new();
for color in grad2.take(8) {
let pix: [u8; 3] = Srgb::from_linear(LinSrgb::from(color))
let pix: [u8; 3] = Srgb::from_linear(LinSrgb::from_color(color))
.into_format()
.into_raw();
v1.push(pix);
Expand Down
18 changes: 13 additions & 5 deletions palette/examples/saturate.rs
@@ -1,4 +1,4 @@
use palette::{Hsl, Lch, Pixel, Saturate, Srgb};
use palette::{Hsl, IntoColor, Lch, Pixel, Saturate, Srgb};

use image::{GenericImage, GenericImageView};

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

let saturated = color.saturate(0.8);
sub_image.put_pixel(
x,
y,
image::Rgb(Srgb::from_linear(saturated.into()).into_format().into_raw()),
image::Rgb(
Srgb::from_linear(saturated.into_color())
.into_format()
.into_raw(),
),
);
}
}
Expand All @@ -40,13 +44,17 @@ fn main() {
let color: Lch = Srgb::from_raw(&sub_image.get_pixel(x, y).0)
.into_format()
.into_linear()
.into();
.into_color();

let saturated = color.saturate(0.8);
sub_image.put_pixel(
x,
y,
image::Rgb(Srgb::from_linear(saturated.into()).into_format().into_raw()),
image::Rgb(
Srgb::from_linear(saturated.into_color())
.into_format()
.into_raw(),
),
);
}
}
Expand Down
14 changes: 7 additions & 7 deletions palette/examples/shade.rs
@@ -1,12 +1,12 @@
use palette::{Hsv, Lab, LinSrgb, Pixel, Shade, Srgb};
use palette::{FromColor, Hsv, IntoColor, Lab, LinSrgb, Pixel, Shade, Srgb};

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

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

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

Expand Down Expand Up @@ -38,10 +38,10 @@ fn main() {
}
}

let lab1 = Srgb::from_linear(lab.darken(0.05 * i as f32).into())
let lab1 = Srgb::from_linear(lab.darken(0.05 * i as f32).into_color())
.into_format()
.into_raw();
let lab2 = Srgb::from_linear(lab.lighten(0.05 * i as f32).into())
let lab2 = Srgb::from_linear(lab.lighten(0.05 * i as f32).into_color())
.into_format()
.into_raw();

Expand All @@ -65,10 +65,10 @@ fn main() {
}
}

let hsv1 = Srgb::from_linear(hsv.darken(0.05 * i as f32).into())
let hsv1 = Srgb::from_linear(hsv.darken(0.05 * i as f32).into_color())
.into_format()
.into_raw();
let hsv2 = Srgb::from_linear(hsv.lighten(0.05 * i as f32).into())
let hsv2 = Srgb::from_linear(hsv.lighten(0.05 * i as f32).into_color())
.into_format()
.into_raw();

Expand Down