From 71b440508c000ace0af4fa8d269abc9f5f6fae65 Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Mon, 28 Aug 2023 18:07:04 +0200 Subject: [PATCH 1/3] Improve some documentation for beginners --- palette/src/alpha/alpha.rs | 102 ++++++++++++++++++- palette/src/convert.rs | 118 +++++++++++++++++----- palette/src/lib.rs | 21 ++++ palette/src/rgb.rs | 45 ++++++++- palette/src/rgb/rgb.rs | 198 ++++++++++++++++++++++++++++++++++--- 5 files changed, 444 insertions(+), 40 deletions(-) diff --git a/palette/src/alpha/alpha.rs b/palette/src/alpha/alpha.rs index c42ec11fc..93b6adf6c 100644 --- a/palette/src/alpha/alpha.rs +++ b/palette/src/alpha/alpha.rs @@ -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 has a transparent type alias, 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 = Srgba::new(171, 193, 35, 128); +/// +/// // Conversion methods from the color type is 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::(); +/// +/// // Hexadecimal is also supported for RGBA, with or without the #: +/// let rgb_from_hex1: Srgba = "#f034e65a".parse().unwrap(); +/// let rgb_from_hex2: Srgba = "f034e65a".parse().unwrap(); +/// assert_eq!(rgb_from_hex1, rgb_from_hex2); +/// +/// // This includes the shorthand format: +/// let rgb_from_short_hex: Srgba = "f3ea".parse().unwrap(); +/// let rgb_from_long_hex: Srgba = "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(rgba: Srgba) -> T { +/// rgba.red // Error: cannot move out of dereference of `Alpha, T>` +/// } +/// ``` +/// +/// `red` has to be accessed through `color`: +/// +/// ``` +/// use palette::Srgba; +/// +/// fn get_red(rgba: Srgba) -> T { +/// rgba.color.red +/// } +/// ``` #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct Alpha { /// 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, } diff --git a/palette/src/convert.rs b/palette/src/convert.rs index e4b52b408..a4ae67bec 100644 --- a/palette/src/convert.rs +++ b/palette/src/convert.rs @@ -1,18 +1,90 @@ //! 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, with some exceptions: +//! +//! * It's not always possible to change the component type. This helps type +//! inference and opens up for optimizations where it can be selectively +//! allowed. +//! * It's not always possible to change meta types, such as converting +//! `Hsl` to `Hsl>, T>` without converting to `Rgb`, then `Rgb, T>` first. This improves type inference. +//! * Some colors have specific requirements, such as [`Oklab`](crate::Oklab), +//! that requires the white point to be [`D65`](crate::white_point::D65). This +//! is because there's more than one way to change the white point and the +//! library can't know which is the best for the situation. +//! +//! 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 +//! source color will use the memory space of the destination 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 verifies 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> = 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. +//! // This makes conversion back to RGB more efficient, by skipping the HSL step on the way back. +//! let hwb_colors = hsl_colors.then_into_color_mut::<[Hwb]>(); +//! +//! // The colors are automatically converted back to RGB at the end of the scope. +//! } +//! ``` +//! //! # Deriving //! -//! `FromColorUnclamped` can be derived in a mostly automatic way. -//! The default minimum requirement is to implement `FromColorUnclamped`, 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 blanked implemented based on it. The default minimum requirement +//! is to implement `FromColorUnclamped`, 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}; @@ -55,25 +127,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 //! @@ -217,11 +289,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, diff --git a/palette/src/lib.rs b/palette/src/lib.rs index f7d80b139..b4d76466d 100644 --- a/palette/src/lib.rs +++ b/palette/src/lib.rs @@ -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 diff --git a/palette/src/rgb.rs b/palette/src/rgb.rs index dc4a9f7d0..a237dabea 100644 --- a/palette/src/rgb.rs +++ b/palette/src/rgb.rs @@ -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 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 = Rgb; + /// 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 = Rgba; /// 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 = Rgb, 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 = Rgba, 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 = Rgb, 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 = Rgba, T>; /// An RGB space and a transfer function. diff --git a/palette/src/rgb/rgb.rs b/palette/src/rgb/rgb.rs index d60e42225..16ba062c7 100644 --- a/palette/src/rgb/rgb.rs +++ b/palette/src/rgb/rgb.rs @@ -56,10 +56,129 @@ pub type Rgba = Alpha, T>; /// light, where gray scale colors are created when these three channels are /// equal in strength. /// -/// Many conversions and operations on this color space requires that it's -/// linear, meaning that gamma correction is required when converting to and -/// from a displayable RGB, such as sRGB. See the [`encoding`](crate::encoding) -/// module for encoding formats. +/// # Creating a Value +/// +/// RGB comes in different shapes and formats. You will probably want to start +/// with either the [`Srgb`](crate::Srgb) or [`Srgba`](crate::Srgba) alias, +/// which represents the common sRGB format that most images and tools use. +/// Then, depending on your input, you can either just call [`new`](Rgb::new) or +/// convert from another data format. +/// +/// ``` +/// use palette::Srgb; +/// +/// let rgb_u8 = Srgb::new(171u8, 193, 35); +/// let rgb_f32 = Srgb::new(0.3f32, 0.8, 0.1); +/// +/// // `new` is also `const`: +/// const RGB_U8: Srgb = Srgb::new(171, 193, 35); +/// +/// // Converting from one number format to another is as simple as this: +/// let rgb_u8_from_f32 = Srgb::new(0.3f32, 0.8, 0.1).into_format::(); +/// +/// // Hexadecimal is also supported, with or without the #: +/// let rgb_from_hex1: Srgb = "#f034e6".parse().unwrap(); +/// let rgb_from_hex2: Srgb = "f034e6".parse().unwrap(); +/// assert_eq!(rgb_from_hex1, rgb_from_hex2); +/// +/// // This includes the shorthand format: +/// let rgb_from_short_hex: Srgb = "f3e".parse().unwrap(); +/// let rgb_from_long_hex: Srgb = "ff33ee".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 = Srgb::from([171u8, 193, 35]); +/// let rgb_from_tuple = Srgb::from((171u8, 193, 35)); +/// let rgb_from_u32 = Srgb::from(0x607F00); +/// ``` +/// +/// # Linear, sRGB and Gamma Correction +/// +/// Many conversions and operations on RGB require that it's linear, meaning +/// that gamma correction is required when converting to and from displayable +/// RGB, such as sRGB. It's common to store and send RGB values where the +/// numbers are on a non-linear scale. In a non-linear format, a value of, for +/// example, 0.5 would not represent a light intensity of 50%, which makes some +/// operations (such as blurring) give incorrect results. +/// +/// You will probably encounter or use [`LinSrgb`](crate::LinSrgb) or +/// [`LinSrgba`](crate::LinSrgba) at some point. These are aliases for linear +/// sRGB and would usually be obtained by converting an [`Srgb`] value with +/// [`into_linear`](Rgb::into_linear). +/// +/// ```no_run +/// use palette::{LinSrgb, Srgb}; +/// +/// // This function uses linear sRGB for something. But how do we interface with it? +/// fn uses_linear_srgb(input: LinSrgb) -> LinSrgb { todo!() } +/// +/// // Linear sRGB will usually be created from non-linear sRGB: +/// let output = uses_linear_srgb(Srgb::new(0.3, 0.8, 0.1).into_linear()); +/// +/// // It's also possible to convert directly from u8 to f32 for sRGB. +/// // This is much faster than using `into_format` first: +/// let output = uses_linear_srgb(Srgb::new(171u8, 193, 35).into_linear()); +/// +/// // Converting the output back to `Srgb` (or `Srgb`) is just as simple: +/// let output_u8 = Srgb::::from_linear(output); +/// // ..or: +/// let output_u8: Srgb = output.into_encoding(); +/// ``` +/// +/// It's of course also possible to create a linear value from constants, but +/// it's not necessarily as intuitive. It's best to avoid storing them as +/// `LinSrgb` (or `LinRgb<_, u8>`) values, to avoid banding among dark +/// colors. +/// +/// See the [`encoding`](crate::encoding) module for built-in encoding formats. +/// +/// # Storage Formats and Pixel Buffers +/// +/// It's common to read and write RGB values as bytes, hexadecimal strings, or +/// sometimes `u32` values. A single RGB value can be converted to all of these +/// formats and more. +/// +/// ```no_run +/// use palette::{Srgb, LinSrgb}; +/// +/// let source: LinSrgb = todo!(); +/// +/// let u8_array: [u8; 3] = Srgb::from_linear(source).into(); +/// let hex_string1 = format!("#{:x}", Srgb::::from_linear(source)); // The # is optional. +/// let u32_value: u32 = Srgb::from_linear(source).into(); +/// ``` +/// +/// It's also possible to control the component order. +/// [`PackedArgb`](crate::rgb::PackedArgb) is one of a few aliases for +/// [`Packed`], which represents a color that has been "packed" into a specific +/// data format. This can be a `u32` or `[u8; 4]`, for example. This is helpful +/// for reading and writing colors with a different order than the default RGBA. +/// +/// ```no_run +/// use palette::{rgb::PackedArgb, Srgba, LinSrgba}; +/// +/// let source: LinSrgba = todo!(); +/// +/// let u8_array: [u8; 4] = PackedArgb::from(Srgba::from_linear(source)).into(); +/// let u32_value: u32 = PackedArgb::from(Srgba::from_linear(source)).into(); +/// ``` +/// +/// If you need to work with colors in a byte buffer, such as `[u8]`, `Vec` +/// or the `image` crate, there's a quick way to borrow that buffer as a slice +/// of RGB(A) colors. The [`cast`](crate::cast) module has a number of traits +/// and functions for casting values without copying them. +/// +/// ```no_run +/// use image::RgbImage; +/// use palette::{cast::ComponentsAsMut, Srgb}; +/// +/// let mut image: RgbImage = todo!(); +/// let pixels: &mut [Srgb] = image.components_as_mut(); +/// +/// for pixel in pixels { +/// std::mem::swap(&mut pixel.red, &mut pixel.blue); +/// } +/// ``` #[derive(Debug, ArrayCast, FromColorUnclamped, WithAlpha)] #[cfg_attr(feature = "serializing", derive(Serialize, Deserialize))] #[palette( @@ -70,16 +189,16 @@ pub type Rgba = Alpha, T>; )] #[repr(C)] pub struct Rgb { - /// The amount of red light, where 0.0 is no red light and 1.0f (or 255u8) - /// is the highest displayable amount. + /// The amount of red light, where 0.0 is no red light and 1.0 (or 255u8) is + /// the highest displayable amount. pub red: T, - /// The amount of green light, where 0.0 is no green light and 1.0f (or + /// The amount of green light, where 0.0 is no green light and 1.0 (or /// 255u8) is the highest displayable amount. pub green: T, - /// The amount of blue light, where 0.0 is no blue light and 1.0f (or - /// 255u8) is the highest displayable amount. + /// The amount of blue light, where 0.0 is no blue light and 1.0 (or 255u8) + /// is the highest displayable amount. pub blue: T, /// The kind of RGB standard. sRGB is the default. @@ -103,6 +222,21 @@ impl Clone for Rgb { impl Rgb { /// Create an RGB color. + /// + /// It's possible to create a color in one number format and convert it to + /// another format with either [`into_format`](Rgb::into_format) or + /// [`into_linear`](Rgb::into_linear). + /// + /// ``` + /// use palette::{Srgb, LinSrgb}; + /// + /// // Changes only the number format: + /// let rgb_f32: Srgb = Srgb::new(171u8, 193, 35).into_format(); + /// + /// // Changes the number format and converts to linear in one go. + /// // This is faster than `.into_format().into_linear()`: + /// let linear: LinSrgb = Srgb::new(171u8, 193, 35).into_linear(); + /// ``` pub const fn new(red: T, green: T, blue: T) -> Rgb { Rgb { red, @@ -112,7 +246,17 @@ impl Rgb { } } - /// Convert into another component type. + /// Convert the RGB components into another number type. + /// + /// ``` + /// use palette::Srgb; + /// + /// let rgb_u8: Srgb = Srgb::new(0.3, 0.7, 0.2).into_format(); + /// ``` + /// + /// See also [`into_linear`](Rgb::into_linear) and + /// [`into_encoding`](Rgb::into_encoding) for a faster option if you need to + /// change between linear and non-linear encoding at the same time. pub fn into_format(self) -> Rgb where U: FromStimulus, @@ -125,7 +269,17 @@ impl Rgb { } } - /// Convert from another component type. + /// Convert the RGB components from another number type. + /// + /// ``` + /// use palette::Srgb; + /// + /// let rgb_u8 = Srgb::::from_format(Srgb::new(0.3, 0.7, 0.2)); + /// ``` + /// + /// See also [`from_linear`](Rgb::from_linear) and + /// [`from_encoding`](Rgb::from_encoding) for a faster option if you need to + /// change between linear and non-linear encoding at the same time. pub fn from_format(color: Rgb) -> Self where T: FromStimulus, @@ -366,7 +520,16 @@ impl Alpha, A> { } } - /// Convert into another component type. + /// Convert the RGBA components into other number types. + /// + /// ``` + /// use palette::Srgba; + /// + /// let rgba_u8: Srgba = Srgba::new(0.3, 0.7, 0.2, 0.5).into_format(); + /// ``` + /// + /// See also `into_linear` and `into_encoding` for a faster option if you + /// need to change between linear and non-linear encoding at the same time. pub fn into_format(self) -> Alpha, B> where U: FromStimulus, @@ -378,7 +541,16 @@ impl Alpha, A> { } } - /// Convert from another component type. + /// Convert the RGBA components from other number types. + /// + /// ``` + /// use palette::Srgba; + /// + /// let rgba_u8 = Srgba::::from_format(Srgba::new(0.3, 0.7, 0.2, 0.5)); + /// ``` + /// + /// See also `from_linear` and `from_encoding` for a faster option if you + /// need to change between linear and non-linear encoding at the same time. pub fn from_format(color: Alpha, B>) -> Self where T: FromStimulus, From 70c2dfb18610ae0f9b96e2d78e36ce06c04df124 Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Mon, 28 Aug 2023 22:02:08 +0200 Subject: [PATCH 2/3] Apply suggested typo fixes and improvements Co-authored-by: Collyn O'Kane <47607823+okaneco@users.noreply.github.com> --- palette/src/alpha/alpha.rs | 4 ++-- palette/src/convert.rs | 4 ++-- palette/src/rgb.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/palette/src/alpha/alpha.rs b/palette/src/alpha/alpha.rs index 93b6adf6c..31c76021a 100644 --- a/palette/src/alpha/alpha.rs +++ b/palette/src/alpha/alpha.rs @@ -39,7 +39,7 @@ use crate::{ /// /// # Creating Transparent Values /// -/// The color types in Palette has a transparent type alias, such as +/// 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 @@ -54,7 +54,7 @@ use crate::{ /// // `new` is also `const`: /// const RGBA_U8: Srgba = Srgba::new(171, 193, 35, 128); /// -/// // Conversion methods from the color type is usually available for transparent +/// // 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::(); /// diff --git a/palette/src/convert.rs b/palette/src/convert.rs index a4ae67bec..a2eb31e79 100644 --- a/palette/src/convert.rs +++ b/palette/src/convert.rs @@ -44,7 +44,7 @@ //! source color will use the memory space of the destination 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 verifies by the [`ArrayCast`](crate::cast::ArrayCast) +//! components. This is verified by the [`ArrayCast`](crate::cast::ArrayCast) //! trait. //! //! In-place conversion is done with the [`FromColorMut`] and [`IntoColorMut`] @@ -72,7 +72,7 @@ //! # Deriving //! //! `FromColorUnclamped` can be derived in a mostly automatic way. The other -//! traits are blanked implemented based on it. The default minimum requirement +//! traits are blanket implemented based on it. The default minimum requirement //! is to implement `FromColorUnclamped`, but it can also be customized to //! make use of generics and have other manual implementations. //! diff --git a/palette/src/rgb.rs b/palette/src/rgb.rs index a237dabea..ab41e5b10 100644 --- a/palette/src/rgb.rs +++ b/palette/src/rgb.rs @@ -75,7 +75,7 @@ mod rgb; /// 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 locking the more generic [`Rgb`] type to the sRGB format. +/// 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 = Rgb; From aacfbbf04fb1658c7072fc9ac59fb627c6123b2f Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Tue, 29 Aug 2023 14:21:52 +0200 Subject: [PATCH 3/3] Improve the convert documentation a bit --- palette/src/convert.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/palette/src/convert.rs b/palette/src/convert.rs index a2eb31e79..228b1eb49 100644 --- a/palette/src/convert.rs +++ b/palette/src/convert.rs @@ -20,18 +20,24 @@ //! ``` //! //! Most of the color space types can be converted directly to each other, with -//! these traits, with some exceptions: -//! -//! * It's not always possible to change the component type. This helps type -//! inference and opens up for optimizations where it can be selectively -//! allowed. -//! * It's not always possible to change meta types, such as converting -//! `Hsl` to `Hsl>, T>` without converting to `Rgb`, then `Rgb, T>` first. This improves type inference. -//! * Some colors have specific requirements, such as [`Oklab`](crate::Oklab), -//! that requires the white point to be [`D65`](crate::white_point::D65). This -//! is because there's more than one way to change the white point and the -//! library can't know which is the best for the situation. +//! 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 @@ -41,7 +47,7 @@ //! # In-place Conversion //! //! It's possible for some color spaces to be converted in-place, meaning the -//! source color will use the memory space of the destination color. 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) @@ -62,10 +68,11 @@ //! let hsl_colors = <[Hsl]>::from_color_mut(&mut rgb_colors); //! //! // The converted colors can be converted again, without keeping the previous guard around. -//! // This makes conversion back to RGB more efficient, by skipping the HSL step on the way back. //! 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. //! } //! ``` //!