Skip to content

Commit

Permalink
Merge #351
Browse files Browse the repository at this point in the history
351: Improve some documentation for beginners r=Ogeon a=Ogeon

An attempt to add some documentation that may be useful for those who are new to Palette. It can always be improved more, but this is at least something. It adds a "where do I start?" section in the crate root, explains different ways of creating RGB and RGBA values (that generally promotes `Srgb` and `Srgba`), and adds some conversion docs in `convert`.

Co-authored-by: Erik Hedvall <[email protected]>
Co-authored-by: Erik Hedvall <[email protected]>
  • Loading branch information
3 people authored Aug 30, 2023
2 parents 9ce4a63 + aacfbbf commit fc6c530
Show file tree
Hide file tree
Showing 5 changed files with 451 additions and 40 deletions.
102 changes: 99 additions & 3 deletions palette/src/alpha/alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,111 @@ use crate::{
NextArray, Saturate, SaturateAssign, SetHue, ShiftHue, ShiftHueAssign, WithAlpha, WithHue,
};

/// An alpha component wrapper for colors.
/// An alpha component wrapper for colors, for adding transparency.
///
/// Instead of having separate types for "RGB with alpha", "HSV with alpha", and
/// so on, Palette uses this wrapper type to attach the alpha component. The
/// memory representation is the same as if `alpha` was the last member/property
/// of `color`, which is just as space efficient. The perk of having a wrapper
/// is that the alpha can easily be added to or separated form any color.
///
/// # Creating Transparent Values
///
/// The color types in Palette have transparent type aliases, such as
/// [`Srgba`](crate::Srgba) for [`Srgb`][crate::Srgb] or [`Hsla`](crate::Hsla)
/// for [`Hsl`](crate::Hsl). These aliases implement `new` and other useful
/// methods. Here's the same example as for [`Rgb`](crate::rgb::Rgb), but with
/// transparency:
///
/// ```
/// use palette::Srgba;
///
/// let rgba_u8 = Srgba::new(171u8, 193, 35, 128);
/// let rgab_f32 = Srgba::new(0.3f32, 0.8, 0.1, 0.5);
///
/// // `new` is also `const`:
/// const RGBA_U8: Srgba<u8> = Srgba::new(171, 193, 35, 128);
///
/// // Conversion methods from the color type are usually available for transparent
/// // values too. For example `into_format` for changing the number format:
/// let rgb_u8_from_f32 = Srgba::new(0.3f32, 0.8, 0.1, 0.5).into_format::<u8, u8>();
///
/// // Hexadecimal is also supported for RGBA, with or without the #:
/// let rgb_from_hex1: Srgba<u8> = "#f034e65a".parse().unwrap();
/// let rgb_from_hex2: Srgba<u8> = "f034e65a".parse().unwrap();
/// assert_eq!(rgb_from_hex1, rgb_from_hex2);
///
/// // This includes the shorthand format:
/// let rgb_from_short_hex: Srgba<u8> = "f3ea".parse().unwrap();
/// let rgb_from_long_hex: Srgba<u8> = "ff33eeaa".parse().unwrap();
/// assert_eq!(rgb_from_short_hex, rgb_from_long_hex);
///
/// // It's also possible to convert from (and to) arrays, tuples and `u32` values:
/// let rgb_from_array = Srgba::from([171u8, 193, 35, 128]);
/// let rgb_from_tuple = Srgba::from((171u8, 193, 35, 128));
/// let rgb_from_u32 = Srgba::from(0x607F005A);
/// ```
///
/// Opaque values can be made transparent using the [`WithAlpha`] trait, in
/// addition to simply wrapping them in `Alpha`. [`WithAlpha`] is also useful in
/// generic code, since it's implemented for both opaque and transparent types.
///
/// ```
/// use palette::{WithAlpha, Srgb};
///
/// let rgb = Srgb::new(171u8, 193, 35);
/// let rgba = rgb.with_alpha(128u8);
/// assert_eq!(rgba.alpha, 128);
/// ```
///
/// You may have noticed the `u8` in `rgb.with_alpha(128u8)`. That's because
/// `Alpha` allows the transparency component to have a different type than the
/// color components. It would be just as valid to write
/// `rgb.with_alpha(0.5f32)`, for example.
///
/// # Accessing Color Components
///
/// To help with the nesting, `Alpha` implements [`Deref`] and [`DerefMut`].
/// This use of the traits is a bit unconventional, since `Alpha` isn't a smart
/// pointer. It turned out to be a quite successful experiment that stuck
/// around.
///
/// ```
/// use palette::Srgba;
///
/// let rgba = Srgba::new(171u8, 193, 35, 128);
/// let red = rgba.red; // Accesses `rgba.color.red`.
/// let alpha = rgba.alpha; // Accesses `rgba.alpha`.
/// let rgb = rgba.color; // Accesses `rgba.color`;
/// ```
///
/// The main drawback is in generic code:
///
/// ```compile_fail
/// use palette::Srgba;
///
/// fn get_red<T>(rgba: Srgba<T>) -> T {
/// rgba.red // Error: cannot move out of dereference of `Alpha<Rgb<_, T>, T>`
/// }
/// ```
///
/// `red` has to be accessed through `color`:
///
/// ```
/// use palette::Srgba;
///
/// fn get_red<T>(rgba: Srgba<T>) -> T {
/// rgba.color.red
/// }
/// ```
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Alpha<C, T> {
/// The color.
pub color: C,

/// The transparency component. 0.0 is fully transparent and 1.0 is fully
/// opaque.
/// The transparency component. 0.0 (or 0u8) is fully transparent and 1.0
/// (or 255u8) is fully opaque.
pub alpha: T,
}

Expand Down
125 changes: 102 additions & 23 deletions palette/src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,97 @@
//! Traits for converting between color spaces.
//!
//! Each color space type, such as [`Rgb`](crate::rgb::Rgb) and
//! [`Hsl`](crate::Hsl), implement a number of conversion traits:
//!
//! * [`FromColor`] - Similar to [`From`], converts from another color space.
//! * [`IntoColor`] - Similar to [`Into`], converts into another color space.
//! * [`FromColorUnclamped`] - The same as [`FromColor`], but the resulting
//! values may be outside the typical bounds.
//! * [`IntoColorUnclamped`] - The same as [`IntoColor`], but the resulting
//! values may be outside the typical bounds.
//!
//! ```
//! use palette::{FromColor, IntoColor, Srgb, Hsl};
//!
//! let rgb = Srgb::new(0.3f32, 0.8, 0.1);
//!
//! let hsl1: Hsl = rgb.into_color();
//! let hsl2 = Hsl::from_color(rgb);
//! ```
//!
//! Most of the color space types can be converted directly to each other, with
//! these traits. If you look at the implemented traits for any color type, you
//! will see a substantial list of `FromColorUnclamped` implementations. There
//! are, however, exceptions and restrictions in some cases:
//!
//! * **It's not always possible to change the component type while
//! converting.** This can only be enabled in specific cases, to allow type
//! inference to work. The input and output component types need to be the
//! same in the general case.
//! * **It's not always possible to change meta types while converting.** Meta
//! types are the additional input types on colors, such as white point or RGB
//! standard. Similar to component types, these are generally restricted to
//! help type inference.
//! * **Some color spaces want specific component types.** For example,
//! [`Xyz`](crate::Xyz) and many other color spaces require real-ish numbers
//! (`f32`, `f64`, etc.).
//! * **Some color spaces want specific meta types.** For example,
//! [`Oklab`](crate::Oklab) requires the white point to be
//! [`D65`](crate::white_point::D65).
//!
//! These limitations are usually the reason for why the compiler gives an error
//! when calling `into_color`, `from_color`, or the corresponding unclamped
//! methods. They are possible to work around by splitting the conversion into
//! multiple steps.
//!
//! # In-place Conversion
//!
//! It's possible for some color spaces to be converted in-place, meaning the
//! destination color will use the memory space of the source color. The
//! requirement for this is that the source and destination color types have the
//! same memory layout. That is, the same component types and the same number of
//! components. This is verified by the [`ArrayCast`](crate::cast::ArrayCast)
//! trait.
//!
//! In-place conversion is done with the [`FromColorMut`] and [`IntoColorMut`]
//! traits, as well as their unclamped counterparts, [`FromColorUnclampedMut`]
//! and [`IntoColorUnclampedMut`]. They work for both single colors and slices
//! of colors.
//!
//! ```
//! use palette::{convert::FromColorMut, Srgb, Hsl, Hwb};
//!
//! let mut rgb_colors: Vec<Srgb<f32>> = vec![/* ... */];
//!
//! {
//! // Creates a scope guard that prevents `rgb_colors` from being modified as RGB.
//! let hsl_colors = <[Hsl]>::from_color_mut(&mut rgb_colors);
//!
//! // The converted colors can be converted again, without keeping the previous guard around.
//! let hwb_colors = hsl_colors.then_into_color_mut::<[Hwb]>();
//!
//! // The colors are automatically converted back to RGB at the end of the scope.
//! // The use of `then_into_color_mut` above makes this conversion a single HWB -> RGB step,
//! // instead of HWB -> HSL -> RGB, since it consumed the HSL guard.
//! }
//! ```
//!
//! # Deriving
//!
//! `FromColorUnclamped` can be derived in a mostly automatic way.
//! The default minimum requirement is to implement `FromColorUnclamped<Xyz>`, but it can
//! also be customized to make use of generics and have other manual implementations.
//! `FromColorUnclamped` can be derived in a mostly automatic way. The other
//! traits are blanket implemented based on it. The default minimum requirement
//! is to implement `FromColorUnclamped<Xyz>`, but it can also be customized to
//! make use of generics and have other manual implementations.
//!
//! It is also recommended to derive or implement [`WithAlpha`](crate::WithAlpha),
//! to be able to convert between all `Alpha` wrapped color types.
//! It is also recommended to derive or implement
//! [`WithAlpha`](crate::WithAlpha), to be able to convert between all `Alpha`
//! wrapped color types.
//!
//! ## Configuration Attributes
//!
//! The derives can be configured using one or more `#[palette(...)]` attributes.
//! They can be attached to either the item itself, or to the fields.
//! The derives can be configured using one or more `#[palette(...)]`
//! attributes. They can be attached to either the item itself, or to the
//! fields.
//!
//! ```
//! # use palette::rgb::{RgbStandard, RgbSpace};
Expand Down Expand Up @@ -55,25 +134,25 @@
//!
//! ### Item Attributes
//!
//! * `skip_derives(Luma, Rgb)`: No conversion derives will be implemented for these colors.
//! They are instead to be implemented manually, and serve as the basis for the automatic
//! implementations.
//! * `skip_derives(Luma, Rgb)`: No conversion derives will be implemented for
//! these colors. They are instead to be implemented manually, and serve as the
//! basis for the automatic implementations.
//!
//! * `white_point = "some::white_point::Type"`: Sets the white
//! point type that should be used when deriving. The default is `D65`, but it
//! may be any other type, including type parameters.
//! * `white_point = "some::white_point::Type"`: Sets the white point type that
//! should be used when deriving. The default is `D65`, but it may be any other
//! type, including type parameters.
//!
//! * `component = "some::component::Type"`: Sets the color
//! component type that should be used when deriving. The default is `f32`, but
//! it may be any other type, including type parameters.
//! * `component = "some::component::Type"`: Sets the color component type that
//! should be used when deriving. The default is `f32`, but it may be any other
//! type, including type parameters.
//!
//! * `rgb_standard = "some::rgb_standard::Type"`: Sets the RGB standard
//! type that should be used when deriving. The default is to either use `Srgb`
//! or a best effort to convert between standards, but sometimes it has to be set
//! to a specific type. This also accepts type parameters.
//! * `rgb_standard = "some::rgb_standard::Type"`: Sets the RGB standard type
//! that should be used when deriving. The default is to either use `Srgb` or a
//! best effort to convert between standards, but sometimes it has to be set to
//! a specific type. This also accepts type parameters.
//!
//! * `luma_standard = "some::rgb_standard::Type"`: Sets the Luma standard
//! type that should be used when deriving, similar to `rgb_standard`.
//! * `luma_standard = "some::rgb_standard::Type"`: Sets the Luma standard type
//! that should be used when deriving, similar to `rgb_standard`.
//!
//! ### Field Attributes
//!
Expand Down Expand Up @@ -217,11 +296,11 @@
//! use palette::convert::{FromColorUnclamped, IntoColorUnclamped};
//!
//! /// CSS style sRGB.
//! #[derive(PartialEq, Debug, FromColorUnclamped, WithAlpha)]
//! #[palette(
//! skip_derives(Rgb),
//! rgb_standard = "palette::encoding::Srgb"
//! )]
//! #[derive(PartialEq, Debug, FromColorUnclamped, WithAlpha)]
//! struct CssRgb {
//! red: u8,
//! green: u8,
Expand Down
21 changes: 21 additions & 0 deletions palette/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@
//! accessible for anyone. It uses the type system to enforce correctness and to
//! avoid mistakes, such as mixing incompatible color types.
//!
//! # Where Do I Start?
//!
//! The sections below give an overview of how the types in this library work,
//! including color conversion. If you want to get your hands dirty, you'll
//! probably want to start with [`Srgb`] or [`Srgba`]. They are aliases for the
//! more generic [`Rgb`](rgb::Rgb) type and represent sRGB(A), the most common
//! RGB format in images and tools. Their documentation has more details and
//! examples.
//!
//! The documentation for each module and type goes deeper into their concepts.
//! Here are a few you may want to read:
//!
//! * [`Rgb`](rgb::Rgb) - For getting started with RGB values.
//! * [`Alpha`] - For more details on transparency.
//! * [`convert`] - Describes the conversion traits and how to use and implement
//! them.
//! * [`cast`] - Describes how to cast color types to and from other data
//! formats, such as arrays and unsigned integers.
//! * [`color_difference`] - Describes different ways of measuring the
//! difference between colors.
//!
//! # Type Safety for Colors
//!
//! Digital colors are not "just RGB", and not even RGB is "just RGB". There are
Expand Down
45 changes: 44 additions & 1 deletion palette/src/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,64 @@ pub use self::rgb::{FromHexError, Iter, Rgb, Rgba};
pub mod channels;
mod rgb;

/// Non-linear sRGB.
/// Non-linear sRGB, the most common RGB input/output format.
///
/// If you are looking for "just RGB", this is probably it. This type alias
/// helps by locking the more generic [`Rgb`] type to the sRGB format.
///
/// See [`Rgb`] for more details on how to create a value and use it.
pub type Srgb<T = f32> = Rgb<encoding::Srgb, T>;

/// Non-linear sRGB with an alpha component.
///
/// This is a transparent version of [`Srgb`], which is commonly used as the
/// input or output format. If you are looking for "just RGBA", this is probably
/// it.
///
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
/// create a value and use it.
pub type Srgba<T = f32> = Rgba<encoding::Srgb, T>;

/// Linear sRGB.
///
/// You probably want [`Srgb`] if you are looking for an input or output format
/// (or "just RGB"). This is the linear version of sRGB, which is what you would
/// usually convert to before working with the color.
///
/// See [`Rgb`] for more details on how to create a value and use it.
#[doc(alias = "linear")]
pub type LinSrgb<T = f32> = Rgb<Linear<encoding::Srgb>, T>;

/// Linear sRGB with an alpha component.
///
/// You probably want [`Srgba`] if you are looking for an input or output format
/// (or "just RGB"). This is the linear version of sRGBA, which is what you
/// would usually convert to before working with the color.
///
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
/// create a value and use it.
#[doc(alias = "linear")]
pub type LinSrgba<T = f32> = Rgba<Linear<encoding::Srgb>, T>;

/// Gamma 2.2 encoded sRGB.
///
/// This is similar to [`Srgb`], but uses the exponent function as an
/// approximation. It's a common trick to speed up conversion when accuracy can
/// be sacrificed. It's still faster to use `Srgb` when also converting to and
/// from `u8` at the same time.
///
/// See [`Rgb`] for more details on how to create a value and use it.
pub type GammaSrgb<T = f32> = Rgb<Gamma<encoding::Srgb>, T>;

/// Gamma 2.2 encoded sRGB with an alpha component.
///
/// This is similar to [`Srgba`], but uses the exponent function as an
/// approximation. It's a common trick to speed up conversion when accuracy can
/// be sacrificed. It's still faster to use `Srgba` when also converting to and
/// from `u8` at the same time.
///
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
/// create a value and use it.
pub type GammaSrgba<T = f32> = Rgba<Gamma<encoding::Srgb>, T>;

/// An RGB space and a transfer function.
Expand Down
Loading

0 comments on commit fc6c530

Please sign in to comment.