Skip to content

Commit

Permalink
Merge Ogeon#60
Browse files Browse the repository at this point in the history
60: [WIP] Generalize the RGB types over RGB standards r=Ogeon a=Ogeon

This changes the `Rgb` type to have a type parameter that implements the `RgbStandard` trait. This trait defines a set of primaries, white point and transfer function. It does also make the RGB types more uniform and type safe.

Closes Ogeon#66, closes Ogeon#31, closes Ogeon#58
  • Loading branch information
bors[bot] committed Feb 17, 2018
2 parents c5114e5 + 127fc1a commit 7b88439
Show file tree
Hide file tree
Showing 41 changed files with 2,504 additions and 2,252 deletions.
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,10 @@ This example takes an sRGB color, converts it to CIE L\*C\*h°, shifts its hue b

```Rust
extern crate palette;
use palette::{Rgb, Lch, Hue};
use palette::pixel::Srgb;
use palette::{Srgb, LinSrgb, Lch, Hue};

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

This results in the following two colors:
Expand All @@ -90,10 +89,9 @@ a desaturated version of the original.

```Rust
extern crate palette;
use palette::{Color, Shade, Saturate};
use palette::pixel::Srgb;
use palette::{Color, Srgb, Shade, Saturate};

let color: Color = Srgb::new(0.8, 0.2, 0.1).into();
let color: Color = Srgb::new(0.8, 0.2, 0.1).into_linear().into();
let lighter = color.lighten(0.1);
let desaturated = color.desaturate(0.5);
```
Expand All @@ -114,16 +112,16 @@ one is in RGB and the other in is HSV space.

```Rust
extern crate palette;
use palette::{Rgb, Hsv, Gradient};
use palette::{LinSrgb, Hsv, Gradient};

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

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

Expand Down
5 changes: 2 additions & 3 deletions examples/color_scheme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ extern crate palette;
extern crate image;
extern crate clap;

use palette::{Color, Hue, Shade};
use palette::pixel::Srgb;
use palette::{Color, Hue, Shade, Srgb};

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

Expand Down Expand Up @@ -85,7 +84,7 @@ fn main() {
.and_then(|r| r.parse().ok())
.expect("the blue channel must be a number in the range [0-255]");

let primary: Color = Srgb::new_u8(red, green, blue).into();
let primary: Color = Srgb::new_u8(red, green, blue).into_linear().into();

//Generate the secondary colors, depending on the input arguments
let secondary = match matches.subcommand() {
Expand Down
31 changes: 15 additions & 16 deletions examples/gradient.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
extern crate palette;
extern crate image;

use palette::{Gradient, Rgb, Lch};
use palette::pixel::Srgb;
use palette::{Gradient, LinSrgb, Srgb, Lch};

use image::{RgbImage, GenericImage};

fn main() {
//A gradient of evenly spaced colors
let grad1 = Gradient::new(vec![
Rgb::new(1.0, 0.1, 0.1),
Rgb::new(0.1, 0.1, 1.0),
Rgb::new(0.1, 1.0, 0.1)
LinSrgb::new(1.0, 0.1, 0.1),
LinSrgb::new(0.1, 0.1, 1.0),
LinSrgb::new(0.1, 1.0, 0.1)
]);

//The same colors as in grad1, but with the blue point shifted down
let grad2 = Gradient::with_domain(vec![
(0.0, Rgb::new(1.0, 0.1, 0.1)),
(0.25, Rgb::new(0.1, 0.1, 1.0)),
(1.0, Rgb::new(0.1, 1.0, 0.1))
(0.0, LinSrgb::new(1.0, 0.1, 0.1)),
(0.25, LinSrgb::new(0.1, 0.1, 1.0)),
(1.0, LinSrgb::new(0.1, 1.0, 0.1))
]);

//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(Rgb::new(1.0, 0.1, 0.1)),
Lch::from(Rgb::new(0.1, 0.1, 1.0)),
Lch::from(Rgb::new(0.1, 1.0, 0.1))
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))
]);

//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(Rgb::new(1.0, 0.1, 0.1))),
(0.25, Lch::from(Rgb::new(0.1, 0.1, 1.0))),
(1.0, Lch::from(Rgb::new(0.1, 1.0, 0.1)))
(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)))
]);

let mut image = RgbImage::new(256, 128);

for (i, ((c1, c2), (c3, c4))) in grad1.take(256).zip(grad2.take(256)).zip(grad3.take(256).zip(grad4.take(256))).enumerate() {
let c1 = Srgb::from(c1).to_pixel();
let c2 = Srgb::from(c2).to_pixel();
let c1 = Srgb::linear_to_pixel(c1);
let c2 = Srgb::linear_to_pixel(c2);
let c3 = Srgb::linear_to_pixel(c3);
let c4 = Srgb::linear_to_pixel(c4);

Expand Down
5 changes: 2 additions & 3 deletions examples/hue.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
extern crate palette;
extern crate image;

use palette::{Rgb, Hsl, Lch, Hue};
use palette::pixel::Srgb;
use palette::{Srgb, Hsl, Lch, Hue};

fn main() {
let mut image = image::open("res/fruits.png").expect("could not open 'res/fruits.png'").to_rgb();
Expand All @@ -11,7 +10,7 @@ fn main() {
//right part. Notice how LCh manages to preserve the apparent lightness of
//of the colors, compared to the original.
for (x, y, pixel) in image.enumerate_pixels_mut() {
let color: Rgb = Srgb::from_pixel(&pixel.data).into();
let color = Srgb::from_pixel(&pixel.data);

pixel.data = if x < y {
let saturated = Hsl::from(color).shift_hue(180.0.into());
Expand Down
46 changes: 21 additions & 25 deletions examples/readme_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,53 @@ extern crate num_traits;

use image::{RgbImage, GenericImage};

use palette::{Rgba, Gradient, Mix};
use palette::pixel::Srgb;
use palette::white_point::D65;
use palette::{Gradient, Mix, Srgb, LinSrgb};

mod color_spaces {
use palette::{Rgb, Lch, Hue};
use palette::pixel::Srgb;
use palette::{Srgb, LinSrgb, Lch, Hue};
use display_colors;

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

display_colors("examples/readme_color_spaces.png", &[
::palette::pixel::Srgb::new(0.8, 0.2, 0.1).to_pixel(),
::palette::pixel::Srgb::from(new_color).to_pixel()
::palette::Srgb::new(0.8, 0.2, 0.1).into_pixel(),
::palette::Srgb::linear_to_pixel(new_color),
]);
}
}

mod manipulation {
use palette::{Color, Shade, Saturate};
use palette::pixel::Srgb;
use palette::{Color, Srgb, Shade, Saturate};
use display_colors;

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

display_colors("examples/readme_manipulation.png", &[
::palette::pixel::Srgb::from(color).to_pixel(),
::palette::pixel::Srgb::from(lighter).to_pixel(),
::palette::pixel::Srgb::from(desaturated).to_pixel()
::palette::Srgb::linear_to_pixel(color),
::palette::Srgb::linear_to_pixel(lighter),
::palette::Srgb::linear_to_pixel(desaturated),
]);
}
}

mod gradients {
use palette::{Rgb, Hsv, Gradient};
use palette::{LinSrgb, Hsv, Gradient};
use display_gradients;

pub fn run() {
let grad1 = Gradient::new(vec![
Rgb::new(1.0, 0.1, 0.1),
Rgb::new(0.1, 1.0, 1.0)
LinSrgb::new(1.0, 0.1, 0.1),
LinSrgb::new(0.1, 1.0, 1.0)
]);

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

display_gradients("examples/readme_gradients.png", grad1, grad2);
Expand All @@ -75,18 +71,18 @@ fn display_colors(filename: &str, colors: &[[u8; 3]]) {
}
}

fn display_gradients<A: Mix<Scalar=f64> + Clone, B: Mix<Scalar=f64> + Clone>(filename: &str, grad1: Gradient<A>, grad2: Gradient<B>) where
Rgba<D65, f64>: From<A>,
Rgba<D65, f64>: From<B>,
fn display_gradients<A: Mix<Scalar=f32> + Clone, B: Mix<Scalar=f32> + Clone>(filename: &str, grad1: Gradient<A>, grad2: Gradient<B>) where
LinSrgb: From<A>,
LinSrgb: From<B>,
{
let mut image = RgbImage::new(256, 64);

for (x, _, pixel) in image.sub_image(0, 0, 256, 32).pixels_mut() {
pixel.data = Srgb::linear_to_pixel(grad1.get(x as f64 / 255.0));
pixel.data = Srgb::linear_to_pixel(grad1.get(x as f32 / 255.0));
}

for (x, _, pixel) in image.sub_image(0, 32, 256, 32).pixels_mut() {
pixel.data = Srgb::linear_to_pixel(grad2.get(x as f64/ 255.0));
pixel.data = Srgb::linear_to_pixel(grad2.get(x as f32/ 255.0));
}

match image.save(filename) {
Expand Down
11 changes: 5 additions & 6 deletions examples/saturate.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
extern crate palette;
extern crate image;

use palette::{Rgb, Hsl, Lch, Saturate};
use palette::pixel::Srgb;
use palette::{Srgb, Hsl, Lch, Saturate};

use image::GenericImage;

Expand All @@ -15,16 +14,16 @@ fn main() {
//Increase the saturation by 80% (!) as HSL in the left half, and as LCh
//in the right half. Notice the strong yellow tone in the HSL part.
for (_, _, pixel) in image.sub_image(0, 0, width / 2, height).pixels_mut() {
let color: Rgb = Srgb::from_pixel(&pixel.data).into();
let color: Hsl = Srgb::pixel_to_linear(&pixel.data);

let saturated = Hsl::from(color).saturate(0.8);
let saturated = color.saturate(0.8);
pixel.data = Srgb::linear_to_pixel(saturated);
}

for (_, _, pixel) in image.sub_image(width / 2, 0, width / 2, height).pixels_mut() {
let color: Rgb = Srgb::from_pixel(&pixel.data).into();
let color: Lch = Srgb::pixel_to_linear(&pixel.data);

let saturated = Lch::from(color).saturate(0.8);
let saturated = color.saturate(0.8);
pixel.data = Srgb::linear_to_pixel(saturated);
}

Expand Down
5 changes: 2 additions & 3 deletions examples/shade.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
extern crate palette;
extern crate image;

use palette::{Rgb, Lab, Hsv, Shade};
use palette::pixel::Srgb;
use palette::{LinSrgb, Srgb, Lab, Hsv, Shade};

use image::{RgbImage, GenericImage};

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

Expand Down
11 changes: 5 additions & 6 deletions src/blend/blend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,20 @@ pub trait Blend: Sized {
///acceptable, including functions and closures.
///
///```
///use palette::{Rgb, Rgba, Blend};
///use palette::{LinSrgb, LinSrgba, Blend};
///use palette::blend::PreAlpha;
///use palette::white_point::D65;
///
///type PreRgba = PreAlpha<Rgb<D65, f32>, f32>;
///type PreRgba = PreAlpha<LinSrgb<f32>, f32>;
///
///fn blend_mode(a: PreRgba, b: PreRgba) -> PreRgba {
/// PreAlpha {
/// color: Rgb::new(a.red * b.green, a.green * b.blue, a.blue * b.red),
/// color: LinSrgb::new(a.red * b.green, a.green * b.blue, a.blue * b.red),
/// alpha: a.alpha * b.alpha,
/// }
///}
///
///let a = Rgba::new(0.2, 0.5, 0.1, 0.8);
///let b = Rgba::new(0.6, 0.3, 0.5, 0.1);
///let a = LinSrgba::new(0.2, 0.5, 0.1, 0.8);
///let b = LinSrgba::new(0.6, 0.3, 0.5, 0.1);
///let c = a.blend(b, blend_mode);
///```
fn blend<F>(self, destination: Self, blend_function: F) -> Self where F: BlendFunction<Self::Color> {
Expand Down
12 changes: 6 additions & 6 deletions src/blend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
//!color type:
//!
//!```
//!use palette::{Rgba, Blend};
//!use palette::{LinSrgba, Blend};
//!
//!let a = Rgba::new(0.2, 0.5, 0.1, 0.8);
//!let b = Rgba::new(0.6, 0.3, 0.5, 0.1);
//!let a = LinSrgba::new(0.2, 0.5, 0.1, 0.8);
//!let b = LinSrgba::new(0.6, 0.3, 0.5, 0.1);
//!let c = a.overlay(b);
//!```
//!
Expand All @@ -19,16 +19,16 @@
//!`blend` function, from the `Blend` trait:
//!
//!```
//!use palette::{Rgba, Blend};
//!use palette::{LinSrgba, Blend};
//!use palette::blend::{Equations, Parameter};
//!
//!let blend_mode = Equations::from_parameters(
//! Parameter::SourceAlpha,
//! Parameter::OneMinusSourceAlpha
//!);
//!
//!let a = Rgba::new(0.2, 0.5, 0.1, 0.8);
//!let b = Rgba::new(0.6, 0.3, 0.5, 0.1);
//!let a = LinSrgba::new(0.2, 0.5, 0.1, 0.8);
//!let b = LinSrgba::new(0.6, 0.3, 0.5, 0.1);
//!let c = a.blend(b, blend_mode);
//!```
//!
Expand Down
10 changes: 5 additions & 5 deletions src/blend/pre_alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ use {Alpha, ComponentWise, Mix, Blend, clamp};
///computations in composition chains.
///
///```
///use palette::{Blend, Rgb, Rgba};
///use palette::{Blend, LinSrgb, LinSrgba};
///use palette::blend::PreAlpha;
///
///let a = PreAlpha::from(Rgba::new(0.4, 0.5, 0.5, 0.3));
///let b = PreAlpha::from(Rgba::new(0.3, 0.8, 0.4, 0.4));
///let c = PreAlpha::from(Rgba::new(0.7, 0.1, 0.8, 0.8));
///let a = PreAlpha::from(LinSrgba::new(0.4, 0.5, 0.5, 0.3));
///let b = PreAlpha::from(LinSrgba::new(0.3, 0.8, 0.4, 0.4));
///let c = PreAlpha::from(LinSrgba::new(0.7, 0.1, 0.8, 0.8));
///
///let res = Rgb::from_premultiplied(a.screen(b).overlay(c));
///let res = LinSrgb::from_premultiplied(a.screen(b).overlay(c));
///```
///
///Note that converting to and from premultiplied alpha will cause the alpha
Expand Down
Loading

0 comments on commit 7b88439

Please sign in to comment.