Skip to content

Commit

Permalink
Add ArraysAs, AsArrays, and corresponding traits for components and u…
Browse files Browse the repository at this point in the history
…ints
  • Loading branch information
Ogeon committed Aug 8, 2023
1 parent e6d74bb commit 2807ecb
Show file tree
Hide file tree
Showing 12 changed files with 973 additions and 62 deletions.
14 changes: 9 additions & 5 deletions palette/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ A longer and more advanced example that shows how to implement the conversion tr
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::ComponentsInto, Srgb};
use palette::{cast::ComponentsAsMut, 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>] = my_rgb_image.components_into();
let my_rgb_image: &mut [Srgb<u8>] = my_rgb_image.components_as_mut();

for color in my_rgb_image {
std::mem::swap(&mut color.red, &mut color.blue);
Expand Down Expand Up @@ -152,13 +152,17 @@ 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::ComponentsInto, Srgb, WithAlpha};
use palette::{
blend::Compose,
cast::{ComponentsAs, ComponentsAsMut},
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>] = image1.components_into();
let image2: &[Srgb<u8>] = image2.components_into();
let image1: &mut [Srgb<u8>] = image1.components_as_mut();
let image2: &[Srgb<u8>] = image2.components_as();

for (color1, color2) in image1.iter_mut().zip(image2) {
// Convert the colors to linear floating point format and give them transparency values.
Expand Down
14 changes: 9 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::ComponentsInto, Srgb};
use palette::{cast::ComponentsAsMut, 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>] = my_rgb_image.components_into();
let my_rgb_image: &mut [Srgb<u8>] = my_rgb_image.components_as_mut();

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

fn color_operations_2() {
use palette::{blend::Compose, cast::ComponentsInto, Srgb, WithAlpha};
use palette::{
blend::Compose,
cast::{ComponentsAs, ComponentsAsMut},
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>] = image1.components_into();
let image2: &[Srgb<u8>] = image2.components_into();
let image1: &mut [Srgb<u8>] = image1.components_as_mut();
let image2: &[Srgb<u8>] = image2.components_as();

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::ComponentsInto, color_difference::EuclideanDistance, IntoColor, Oklab, Srgb};
use palette::{cast::ComponentsAs, 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: &[Srgb<u8>] = image.as_raw().components_into();
let image: &[Srgb<u8>] = image.components_as();

// 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
56 changes: 31 additions & 25 deletions palette/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@
//! same after casting.
//!
//! ```
//! use palette::{cast::{self, ArraysInto}, Srgb, IntoColor};
//! use palette::{cast::{self, ArraysAsMut}, Srgb, IntoColor};
//!
//! let color = cast::from_array::<Srgb<u8>>([23, 198, 76]).into_linear();
//! // Note: `Srgb::<u8>::from([23, 198, 76])` works too.
//!
//! let buffer = &mut [[64, 139, 10], [93, 18, 214]];
//! let color_buffer: &mut [Srgb<u8>] = buffer.arrays_into();
//! let mut buffer = [[64, 139, 10], [93, 18, 214]];
//! let color_buffer: &mut [Srgb<u8>] = buffer.arrays_as_mut();
//!
//! for destination in color_buffer {
//! let linear_dst = destination.into_linear::<f32>();
//! *destination = (linear_dst + color).into_encoding();
//! }
//! ```
//!
//! Trying to cast an array of the wrong size will not compile:
//! Trying to cast a single array of the wrong size will not compile:
//!
//! ```compile_fail
//! use palette::{cast, Srgb};
Expand All @@ -68,35 +68,37 @@
//! or multiplying the length.
//!
//! ```
//! use palette::{cast::{self, TryFromComponents}, Srgb};
//! use palette::{cast::{self, TryComponentsAs}, Srgb};
//!
//! let correct_buffer = &[64, 139, 10, 93, 18, 214];
//! assert!(<&[Srgb<u8>]>::try_from_components(correct_buffer).is_ok());
//! let correct_buffer = [64, 139, 10, 93, 18, 214];
//! let should_be_ok: Result<&[Srgb<u8>], _> = correct_buffer.try_components_as();
//! assert!(should_be_ok.is_ok());
//!
//! let incorrect_buffer = &[64, 139, 10, 93, 18, 214, 198, 76];
//! assert!(<&[Srgb<u8>]>::try_from_components(incorrect_buffer).is_err());
//! let incorrect_buffer = [64, 139, 10, 93, 18, 214, 198, 76];
//! let should_be_err: Result<&[Srgb<u8>], _> = incorrect_buffer.try_components_as();
//! assert!(should_be_err.is_err());
//! ```
//!
//! An alternative, for when the length can be trusted to be correct, is to use
//! the `ComponentsInto::components_into` and `FromComponents::from_components`
//! methods, or the `from_component_*` functions, that panic on error.
//! methods without the `try_*` prefix, such as `ComponentsAs::components_as`,
//! or the `from_component_*` functions, that panic on error.
//!
//! This works:
//!
//! ```
//! use palette::{cast::ComponentsInto, Srgb};
//! use palette::{cast::ComponentsAs, Srgb};
//!
//! let correct_buffer = &[64, 139, 10, 93, 18, 214];
//! let color_buffer: &[Srgb<u8>] = correct_buffer.components_into();
//! let correct_buffer = [64, 139, 10, 93, 18, 214];
//! let color_buffer: &[Srgb<u8>] = correct_buffer.components_as();
//! ```
//!
//! But this panics:
//!
//! ```should_panic
//! use palette::{cast::ComponentsInto, Srgb};
//! use palette::{cast::ComponentsAs, Srgb};
//!
//! let incorrect_buffer = &[64, 139, 10, 93, 18, 214, 198, 76];
//! let color_buffer: &[Srgb<u8>] = incorrect_buffer.components_into();
//! let incorrect_buffer = [64, 139, 10, 93, 18, 214, 198, 76];
//! let color_buffer: &[Srgb<u8>] = incorrect_buffer.components_as();
//! ```
//!
//! ## Casting Single Colors
Expand Down Expand Up @@ -128,10 +130,10 @@
//!
//! ```
//! // `PackedArgb` is an alias for `Packed<rgb::channels::Argb, P = u32>`.
//! use palette::{rgb::PackedArgb, cast::ComponentsInto, Srgba};
//! use palette::{rgb::PackedArgb, cast::ComponentsAs, Srgba};
//!
//! let components = &[1.0f32, 0.8, 0.2, 0.3, 1.0, 0.5, 0.7, 0.6];
//! let colors: &[PackedArgb<_>] = components.components_into();
//! let components = [1.0f32, 0.8, 0.2, 0.3, 1.0, 0.5, 0.7, 0.6];
//! let colors: &[PackedArgb<_>] = components.components_as();
//!
//! // 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 @@ -152,24 +154,28 @@
//!
//! ```
//! // `PackedArgb` is an alias for `Packed<rgb::channels::Argb, P = u32>`.
//! use palette::{rgb::PackedArgb, cast::UintsInto, Srgba};
//! use palette::{rgb::PackedArgb, cast::UintsAs, Srgba};
//!
//! let raw = &[0xFF7F0080u32, 0xFF60BBCC];
//! let colors: &[PackedArgb] = raw.uints_into();
//! let raw = [0xFF7F0080u32, 0xFF60BBCC];
//! let colors: &[PackedArgb] = raw.uints_as();
//!
//! 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 as_arrays_traits;
mod as_components_traits;
mod as_uints_traits;
mod from_into_arrays_traits;
mod from_into_components_traits;
mod from_into_uints_traits;
mod packed;
mod uint;

pub use self::{
array::*, from_into_arrays_traits::*, from_into_components_traits::*,
from_into_uints_traits::*, packed::*, uint::*,
array::*, as_arrays_traits::*, as_components_traits::*, as_uints_traits::*,
from_into_arrays_traits::*, from_into_components_traits::*, from_into_uints_traits::*,
packed::*, uint::*,
};
Loading

0 comments on commit 2807ecb

Please sign in to comment.