Skip to content

Commit

Permalink
Add traits for casting collections of colors to and from other data t…
Browse files Browse the repository at this point in the history
…ypes
  • Loading branch information
Ogeon committed Jul 16, 2023
1 parent d9e07a0 commit 889578a
Show file tree
Hide file tree
Showing 11 changed files with 2,015 additions and 54 deletions.
12 changes: 6 additions & 6 deletions palette/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ A longer and more advanced example that shows how to implement the conversion tr

### Pixels And Buffers

When working with image or pixel buffers, or any color type that can be converted to a slice of components (ex. `&[u8]`), the `cast` module provides functions for turning them into slices of Palette colors without cloning the whole buffer:
When working with image or pixel buffers, or any color type that can be converted to a slice of components (ex. `&[u8]`), the `cast` module provides traits and functions for turning them into slices of Palette colors without cloning the whole buffer:

```rust
use palette::{cast, Srgb};
use palette::{cast::ComponentsInto, Srgb};

// The input to this function could be data from an image file or
// maybe a texture in a game.
fn swap_red_and_blue(my_rgb_image: &mut [u8]) {
// Convert `my_rgb_image` into `&mut [Srgb<u8>]` without copying.
let my_rgb_image: &mut [Srgb<u8>] = cast::from_component_slice_mut(my_rgb_image);
let my_rgb_image: &mut [Srgb<u8>] = my_rgb_image.components_into();

for color in my_rgb_image {
std::mem::swap(&mut color.red, &mut color.blue);
Expand Down Expand Up @@ -152,13 +152,13 @@ This image shows the transition from the color to `new_color` in HSL and HSV:
In addition to the operator traits, the SVG blend and composition functions have also been implemented.

```rust
use palette::{blend::Compose, cast, Srgb, WithAlpha};
use palette::{blend::Compose, cast::ComponentsInto, Srgb, WithAlpha};

// The input to this function could be data from image files.
fn alpha_blend_images(image1: &mut [u8], image2: &[u8]) {
// Convert the images into `&mut [Srgb<u8>]` and `&[Srgb<u8>]` without copying.
let image1: &mut [Srgb<u8>] = cast::from_component_slice_mut(image1);
let image2: &[Srgb<u8>] = cast::from_component_slice(image2);
let image1: &mut [Srgb<u8>] = image1.components_into();
let image2: &[Srgb<u8>] = image2.components_into();

for (color1, color2) in image1.iter_mut().zip(image2) {
// Convert the colors to linear floating point format and give them transparency values.
Expand Down
10 changes: 5 additions & 5 deletions palette/examples/readme_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ fn converting() {
}

fn pixels_and_buffers() {
use palette::{cast, Srgb};
use palette::{cast::ComponentsInto, Srgb};

// The input to this function could be data from an image file or
// maybe a texture in a game.
fn swap_red_and_blue(my_rgb_image: &mut [u8]) {
// Convert `my_rgb_image` into `&mut [Srgb<u8>]` without copying.
let my_rgb_image: &mut [Srgb<u8>] = cast::from_component_slice_mut(my_rgb_image);
let my_rgb_image: &mut [Srgb<u8>] = my_rgb_image.components_into();

for color in my_rgb_image {
std::mem::swap(&mut color.red, &mut color.blue);
Expand Down Expand Up @@ -91,13 +91,13 @@ fn color_operations_1() {
}

fn color_operations_2() {
use palette::{blend::Compose, cast, Srgb, WithAlpha};
use palette::{blend::Compose, cast::ComponentsInto, Srgb, WithAlpha};

// The input to this function could be data from image files.
fn alpha_blend_images(image1: &mut [u8], image2: &[u8]) {
// Convert the images into `&mut [Srgb<u8>]` and `&[Srgb<u8>]` without copying.
let image1: &mut [Srgb<u8>] = cast::from_component_slice_mut(image1);
let image2: &[Srgb<u8>] = cast::from_component_slice(image2);
let image1: &mut [Srgb<u8>] = image1.components_into();
let image2: &[Srgb<u8>] = image2.components_into();

for (color1, color2) in image1.iter_mut().zip(image2) {
// Convert the colors to linear floating point format and give them transparency values.
Expand Down
4 changes: 2 additions & 2 deletions palette/examples/struct_of_arrays.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use palette::{cast, color_difference::EuclideanDistance, IntoColor, Oklab, Srgb};
use palette::{cast::ComponentsInto, color_difference::EuclideanDistance, IntoColor, Oklab, Srgb};

fn main() {
let image = image::open("example-data/input/fruits.png")
.expect("could not open 'example-data/input/fruits.png'")
.to_rgb8();

let image = cast::from_component_slice::<Srgb<u8>>(image.as_raw());
let image: &[Srgb<u8>] = image.as_raw().components_into();

// Convert and collect the colors in a struct-of-arrays (SoA) format, where
// each component is a Vec of all the pixels' component values.
Expand Down
69 changes: 44 additions & 25 deletions palette/src/cast.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
//! Traits and functions for casting colors to and from other data types.
//!
//! The functions in this module casts without changing the underlying data. See
//! the [`convert`](crate::convert) module for how to convert between color
//! spaces.
//! The functions and traits in this module cast without changing the underlying
//! data. See the [`convert`](crate::convert) module for how to convert between
//! color spaces.
//!
//! # Traits or Functions?
//!
//! This module provides both a set of traits ([`FromComponents`],
//! [`IntoUints`], etc.) and a set of functions ([`from_component_slice`],
//! [`into_uint_array`], etc.) that effectively implement the same
//! functionality. The traits are all implemented using the functions, so they
//! can be seen as bits of more implicit syntax sugar for the more explicit
//! functions.
//!
//! A general recommendation is to use the traits, since they provide mostly
//! better ergonomics.
//!
//! # Arrays and Slices
//!
//! Types that implement [`ArrayCast`] can be cast to and from arrays and slices
//! with little to no overhead. This makes it easy to work with image buffers
//! and types from other crates without having to copy the data first.
//!
//! Casting can be either be done with the free functions in this module, or
//! using the helper traits.
//!
//! ## Casting Arrays
//!
//! Arrays can be type checked to have the correct size at compile time, making
Expand All @@ -18,12 +33,13 @@
//! same after casting.
//!
//! ```
//! use palette::{cast, Srgb, IntoColor};
//! use palette::{cast::{self, ArraysInto}, Srgb, IntoColor};
//!
//! let color = cast::from_array::<Srgb<u8>>([23u8, 198, 76]).into_linear();
//! let color = cast::from_array::<Srgb<u8>>([23, 198, 76]).into_linear();
//! // Note: `Srgb::<u8>::from([23, 198, 76])` works too.
//!
//! let buffer = &mut [[64u8, 139, 10], [93, 18, 214]];
//! let color_buffer = cast::from_array_slice_mut::<Srgb<u8>>(buffer);
//! let buffer = &mut [[64, 139, 10], [93, 18, 214]];
//! let color_buffer: &mut [Srgb<u8>] = buffer.arrays_into();
//!
//! for destination in color_buffer {
//! let linear_dst = destination.into_linear::<f32>();
Expand All @@ -36,7 +52,7 @@
//! ```compile_fail
//! use palette::{cast, Srgb};
//!
//! let color = cast::from_array::<Srgb<u8>>([23u8, 198]); // Too few components.
//! let color = cast::from_array::<Srgb<u8>>([23, 198]); // Too few components.
//! ```
//!
//! ## Casting Component Buffers
Expand All @@ -52,34 +68,35 @@
//! or multiplying the length.
//!
//! ```
//! use palette::{cast, Srgb};
//! use palette::{cast::{self, TryFromComponents}, Srgb};
//!
//! let correct_buffer = &[64u8, 139, 10, 93, 18, 214];
//! assert!(cast::try_from_component_slice::<Srgb<u8>>(correct_buffer).is_ok());
//! let correct_buffer = &[64, 139, 10, 93, 18, 214];
//! assert!(<&[Srgb<u8>]>::try_from_components(correct_buffer).is_ok());
//!
//! let incorrect_buffer = &[64u8, 139, 10, 93, 18, 214, 198, 76];
//! assert!(cast::try_from_component_slice::<Srgb<u8>>(incorrect_buffer).is_err());
//! let incorrect_buffer = &[64, 139, 10, 93, 18, 214, 198, 76];
//! assert!(<&[Srgb<u8>]>::try_from_components(incorrect_buffer).is_err());
//! ```
//!
//! An alternative, for when the length can be trusted to be correct, is to use
//! the `from_component_*` functions that panic on error.
//! the `ComponentsInto::components_into` and `FromComponents::from_components`
//! methods, or the `from_component_*` functions, that panic on error.
//!
//! This works:
//!
//! ```
//! use palette::{cast, Srgb};
//! use palette::{cast::ComponentsInto, Srgb};
//!
//! let correct_buffer = &[64u8, 139, 10, 93, 18, 214];
//! let color_buffer = cast::from_component_slice::<Srgb<u8>>(correct_buffer);
//! let correct_buffer = &[64, 139, 10, 93, 18, 214];
//! let color_buffer: &[Srgb<u8>] = correct_buffer.components_into();
//! ```
//!
//! But this panics:
//!
//! ```should_panic
//! use palette::{cast, Srgb};
//! use palette::{cast::ComponentsInto, Srgb};
//!
//! let incorrect_buffer = &[64u8, 139, 10, 93, 18, 214, 198, 76];
//! let color_buffer = cast::from_component_slice::<Srgb<u8>>(incorrect_buffer);
//! let incorrect_buffer = &[64, 139, 10, 93, 18, 214, 198, 76];
//! let color_buffer: &[Srgb<u8>] = incorrect_buffer.components_into();
//! ```
//!
//! ## Casting Single Colors
Expand Down Expand Up @@ -111,10 +128,10 @@
//!
//! ```
//! // `PackedArgb` is an alias for `Packed<rgb::channels::Argb, P = u32>`.
//! use palette::{rgb::PackedArgb, cast, Srgba};
//! use palette::{rgb::PackedArgb, cast::ComponentsInto, Srgba};
//!
//! let components = &[1.0f32, 0.8, 0.2, 0.3, 1.0, 0.5, 0.7, 0.6];
//! let colors = cast::from_component_slice::<PackedArgb<_>>(components);
//! let colors: &[PackedArgb<_>] = components.components_into();
//!
//! // Notice how the alpha values have moved from the beginning to the end:
//! assert_eq!(Srgba::from(colors[0]), Srgba::new(0.8, 0.2, 0.3, 1.0));
Expand All @@ -135,18 +152,20 @@
//!
//! ```
//! // `PackedArgb` is an alias for `Packed<rgb::channels::Argb, P = u32>`.
//! use palette::{rgb::PackedArgb, cast, Srgba};
//! use palette::{rgb::PackedArgb, cast::UintsInto, Srgba};
//!
//! let raw = &[0xFF7F0080u32, 0xFF60BBCC];
//! let colors = cast::from_uint_slice::<PackedArgb>(raw);
//! let colors: &[PackedArgb] = raw.uints_into();
//!
//! assert_eq!(colors.len(), 2);
//! assert_eq!(Srgba::from(colors[0]), Srgba::new(0x7F, 0x00, 0x80, 0xFF));
//! assert_eq!(Srgba::from(colors[1]), Srgba::new(0x60, 0xBB, 0xCC, 0xFF));
//! ```

mod array;
mod array_traits;
mod packed;
mod uint;
mod uint_traits;

pub use self::{array::*, packed::*, uint::*};
pub use self::{array::*, array_traits::*, packed::*, uint::*, uint_traits::*};
Loading

0 comments on commit 889578a

Please sign in to comment.