Skip to content

Commit

Permalink
Merge #81
Browse files Browse the repository at this point in the history
81: Make a new system for converting to and from arrays and slices r=Ogeon a=Ogeon

This should make some interoperability with other systems better, by making it easier to convert buffers and individual pixels to palette colors.

This includes
 * Bit by bit conversion system for colors.
 * `Rgb` works with `u8` and other integer types.
 * `Luma` works with `u8` and other integer types, and can also be non-linear.
 * Implements `AsRef` to convert colors to raw data.
 * A slight improvement to the hue types. No need to explicitly call `.into()` when setting a hue.

Closes #74
  • Loading branch information
bors[bot] committed Mar 11, 2018
2 parents 315c602 + 7cd6b57 commit 548cab0
Show file tree
Hide file tree
Showing 49 changed files with 3,341 additions and 1,945 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ extern crate palette;
use palette::{Srgb, LinSrgb, Lch, Hue};

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()));
let new_color = LinSrgb::from(lch_color.shift_hue(180.0));
```

This results in the following two colors:
Expand Down
32 changes: 21 additions & 11 deletions build/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,36 @@ use std::fs::File;
#[cfg(feature = "named")]
pub fn build() {
use std::path::Path;
use std::io::{Write, BufRead, BufReader};
use std::io::{BufRead, BufReader, Write};

let out_dir = ::std::env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("named.rs");

let reader = BufReader::new(File::open("build/svg_colors.txt").expect("could not open svg_colors.txt"));
let reader =
BufReader::new(File::open("build/svg_colors.txt").expect("could not open svg_colors.txt"));
let mut writer = File::create(dest_path).expect("couldn't create named.rs");
let mut entries = vec![];

for line in reader.lines() {
let line = line.unwrap();
let mut parts = line.split('\t');
let name = parts.next().expect("couldn't get the color name");
let mut rgb = parts.next().expect(&format!("couldn't get color for {}", name)).split(", ");
let red: u8 = rgb.next().and_then(|r| r.trim().parse().ok()).expect(&format!("couldn't get red for {}", name));
let green: u8 = rgb.next().and_then(|r| r.trim().parse().ok()).expect(&format!("couldn't get green for {}", name));
let blue: u8 = rgb.next().and_then(|r| r.trim().parse().ok()).expect(&format!("couldn't get blue for {}", name));
let mut rgb = parts
.next()
.expect(&format!("couldn't get color for {}", name))
.split(", ");
let red: u8 = rgb.next()
.and_then(|r| r.trim().parse().ok())
.expect(&format!("couldn't get red for {}", name));
let green: u8 = rgb.next()
.and_then(|r| r.trim().parse().ok())
.expect(&format!("couldn't get green for {}", name));
let blue: u8 = rgb.next()
.and_then(|r| r.trim().parse().ok())
.expect(&format!("couldn't get blue for {}", name));

writeln!(writer, "\n///<div style=\"display: inline-block; width: 3em; height: 1em; border: 1px solid black; background: {0};\"></div>", name).unwrap();
writeln!(writer, "pub const {}: (u8, u8, u8) = ({}, {}, {});", name.to_uppercase(), red, green, blue).unwrap();
writeln!(writer, "pub const {}: ::rgb::Srgb<u8> = ::rgb::Srgb {{ red: {}, green: {}, blue: {}, standard: ::std::marker::PhantomData }};", name.to_uppercase(), red, green, blue).unwrap();

entries.push((name.to_owned(), name.to_uppercase()));
}
Expand All @@ -34,7 +44,10 @@ pub fn build() {
fn gen_from_str(writer: &mut File, entries: &[(String, String)]) {
use std::io::Write;

write!(writer, "static COLORS: ::phf::Map<&'static str, (u8, u8, u8)> = ").unwrap();
write!(
writer,
"static COLORS: ::phf::Map<&'static str, ::rgb::Srgb<u8>> = "
).unwrap();
let mut map = ::phf_codegen::Map::new();
for &(ref key, ref value) in entries {
map.entry(&**key, value);
Expand All @@ -43,9 +56,6 @@ fn gen_from_str(writer: &mut File, entries: &[(String, String)]) {
writeln!(writer, ";").unwrap();
}




#[cfg(not(feature = "named"))]
pub fn build() {}

Expand Down
125 changes: 77 additions & 48 deletions examples/color_scheme.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
extern crate palette;
extern crate image;
extern crate clap;
extern crate image;
extern crate palette;

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

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

use clap::{App, AppSettings, Arg, SubCommand};

Expand All @@ -19,20 +19,23 @@ fn main() {
.required(true)
.empty_values(false)
.index(1)
.help("[0-255] The red channel of the primary color.")
).arg(
.help("[0-255] The red channel of the primary color."),
)
.arg(
Arg::with_name("green")
.required(true)
.empty_values(false)
.index(2)
.help("[0-255] The green channel of the primary color.")
).arg(
.help("[0-255] The green channel of the primary color."),
)
.arg(
Arg::with_name("blue")
.required(true)
.empty_values(false)
.index(3)
.help("[0-255] The blue channel of the primary color.")
).subcommand(
.help("[0-255] The blue channel of the primary color."),
)
.subcommand(
SubCommand::with_name("triad")
.about("A three point scheme, centered around the complementary.")
.arg(
Expand All @@ -42,9 +45,10 @@ fn main() {
.short("d")
.value_name("degrees")
.takes_value(true)
.empty_values(false)
)
).subcommand(
.empty_values(false),
),
)
.subcommand(
SubCommand::with_name("analogous")
.about("Like triad, but centered around the primary.")
.arg(
Expand All @@ -54,9 +58,10 @@ fn main() {
.short("d")
.value_name("degrees")
.takes_value(true)
.empty_values(false)
)
).subcommand(
.empty_values(false),
),
)
.subcommand(
SubCommand::with_name("rectangle")
.about("A four point scheme.")
.arg(
Expand All @@ -66,71 +71,83 @@ fn main() {
.short("d")
.value_name("degrees")
.takes_value(true)
.empty_values(false)
)
).subcommand(
SubCommand::with_name("complementary")
.about("A simple two point color scheme.")
).get_matches();
.empty_values(false),
),
)
.subcommand(
SubCommand::with_name("complementary").about("A simple two point color scheme."),
)
.get_matches();

//Get the components of the primary color
let red = matches.value_of("red")
let red: u8 = matches
.value_of("red")
.and_then(|r| r.parse().ok())
.expect("the red channel must be a number in the range [0-255]");
let green = matches.value_of("green")
let green: u8 = matches
.value_of("green")
.and_then(|r| r.parse().ok())
.expect("the green channel must be a number in the range [0-255]");
let blue = matches.value_of("blue")
let blue: u8 = matches
.value_of("blue")
.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_linear().into();
let primary: Color = Srgb::new(red, green, blue)
.into_format()
.into_linear()
.into();

//Generate the secondary colors, depending on the input arguments
let secondary = match matches.subcommand() {
("triad", matches) | ("", matches) => {
//Two secondary colors that are close to the complementary, or evenly spaced
let distance: f32 = matches.and_then(|m| m.value_of("distance"))
let distance: f32 = matches
.and_then(|m| m.value_of("distance"))
.and_then(|d| d.parse().ok())
.unwrap_or(120.0);

let shift = 180.0 - (distance / 2.0);

vec![
primary.shift_hue(shift.into()),
primary.shift_hue((-shift).into()),
primary.shift_hue(shift),
primary.shift_hue((-shift)),
]
},
}
("analogous", matches) => {
//Two secondary colors that are close to the primary
let distance: f32 = matches.and_then(|m| m.value_of("distance"))
let distance: f32 = matches
.and_then(|m| m.value_of("distance"))
.and_then(|d| d.parse().ok())
.unwrap_or(60.0);

let shift = distance / 2.0;

vec![
primary.shift_hue(shift.into()),
primary.shift_hue((-shift).into()),
primary.shift_hue(shift),
primary.shift_hue((-shift)),
]
},
}
("rectangle", matches) => {
//Three secondary colors that forms a rectangle or a square, together with the primary
let distance: f32 = matches.and_then(|m| m.value_of("distance"))
//Three secondary colors that forms a rectangle or a square, together with the
// primary
let distance: f32 = matches
.and_then(|m| m.value_of("distance"))
.and_then(|d| d.parse().ok())
.unwrap_or(90.0);

let shift1 = distance;
let shift2 = 180.0 + distance;

vec![
primary.shift_hue(shift1.into()),
primary.shift_hue(180.0.into()),
primary.shift_hue(shift2.into()),
primary.shift_hue(shift1),
primary.shift_hue(180.0),
primary.shift_hue(shift2),
]
},
("complementary", _) => vec![primary.shift_hue(180.0.into())], //Simply the complementary color
(name, _) => panic!("unknown subcommand: {}", name)
}
("complementary", _) => vec![primary.shift_hue(180.0)], /* Simply the
* complementary color */
(name, _) => panic!("unknown subcommand: {}", name),
};

//Create an image for the swatches
Expand All @@ -141,7 +158,10 @@ fn main() {

//Draw the secondary swatches
for (n, color) in secondary.into_iter().enumerate() {
blit_shades(color, image.sub_image((n as u32 + 1) * SWATCH_SIZE, 0, SWATCH_SIZE, SWATCH_SIZE));
blit_shades(
color,
image.sub_image((n as u32 + 1) * SWATCH_SIZE, 0, SWATCH_SIZE, SWATCH_SIZE),
);
}

match image.save("examples/color_scheme.png") {
Expand All @@ -150,16 +170,25 @@ fn main() {
}
}

fn blit_shades<I: GenericImage<Pixel=image::Rgb<u8>> + 'static>(color: Color, mut canvas: SubImage<I>) {
fn blit_shades<I: GenericImage<Pixel = image::Rgb<u8>> + 'static>(
color: Color,
mut canvas: SubImage<I>,
) {
let width = canvas.width();
let height = canvas.height();

let primary = Srgb::linear_to_pixel(color);
let primary = Srgb::from_linear(color.into()).into_format().into_raw();

//Generate one lighter and two darker versions of the color
let light = Srgb::linear_to_pixel(color.lighten(0.1));
let dark1 = Srgb::linear_to_pixel(color.darken(0.1));
let dark2 = Srgb::linear_to_pixel(color.darken(0.2));
let light = Srgb::from_linear(color.lighten(0.1).into())
.into_format()
.into_raw();
let dark1 = Srgb::from_linear(color.darken(0.1).into())
.into_format()
.into_raw();
let dark2 = Srgb::from_linear(color.darken(0.2).into())
.into_format()
.into_raw();

for (x, y, pixel) in canvas.pixels_mut() {
if y < height / 2 {
Expand Down
35 changes: 21 additions & 14 deletions examples/gradient.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,53 @@
extern crate palette;
extern crate image;
extern crate palette;

use palette::{Gradient, LinSrgb, Srgb, Lch};
use palette::{Gradient, Lch, LinSrgb, Pixel, Srgb};

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

fn main() {
//A gradient of evenly spaced colors
let grad1 = Gradient::new(vec![
LinSrgb::new(1.0, 0.1, 0.1),
LinSrgb::new(0.1, 0.1, 1.0),
LinSrgb::new(0.1, 1.0, 0.1)
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, 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))
(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
//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(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
//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)))
(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::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);
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_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();

for (_, _, pixel) in image.sub_image(i as u32, 0, 1, 31).pixels_mut() {
pixel.data = c1
Expand Down
Loading

0 comments on commit 548cab0

Please sign in to comment.