From 813ea125cab1a363ea50f1ba6fdc0f3d4bcab715 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 21 Feb 2020 04:10:09 -0500 Subject: [PATCH] Add hex methods for RGB, packed format for u32 RGB Add into_hex and from_hex methods for RGB and RGBA Implement packed RGBA structs with representations - RGBA - ARGB - BGRA - ABGR --- palette/src/lib.rs | 5 +- palette/src/rgb/mod.rs | 3 +- palette/src/rgb/packed.rs | 458 ++++++++++++++++++++++++++++++++++++++ palette/src/rgb/rgb.rs | 60 ++++- 4 files changed, 521 insertions(+), 5 deletions(-) create mode 100644 palette/src/rgb/packed.rs diff --git a/palette/src/lib.rs b/palette/src/lib.rs index 391d79284..b53c91a25 100644 --- a/palette/src/lib.rs +++ b/palette/src/lib.rs @@ -176,7 +176,10 @@ pub use hwb::{Hwb, Hwba}; pub use lab::{Lab, Laba}; pub use lch::{Lch, Lcha}; pub use luma::{GammaLuma, GammaLumaa, LinLuma, LinLumaa, SrgbLuma, SrgbLumaa}; -pub use rgb::{GammaSrgb, GammaSrgba, LinSrgb, LinSrgba, Srgb, Srgba}; +pub use rgb::{ + GammaSrgb, GammaSrgba, LinSrgb, LinSrgba, PackedAbgr, PackedArgb, PackedBgra, PackedRgba, Srgb, + Srgba, +}; pub use xyz::{Xyz, Xyza}; pub use yxy::{Yxy, Yxya}; diff --git a/palette/src/rgb/mod.rs b/palette/src/rgb/mod.rs index 6378a2d6e..621b875b3 100644 --- a/palette/src/rgb/mod.rs +++ b/palette/src/rgb/mod.rs @@ -6,9 +6,10 @@ use crate::encoding::{self, Gamma, Linear, TransferFn}; use crate::white_point::WhitePoint; use crate::{Component, FloatComponent, FromComponent, Yxy}; +pub use self::packed::{PackedAbgr, PackedArgb, PackedBgra, PackedRgba}; pub use self::rgb::{Rgb, Rgba}; -//mod linear; +mod packed; mod rgb; /// Nonlinear sRGB. diff --git a/palette/src/rgb/packed.rs b/palette/src/rgb/packed.rs new file mode 100644 index 000000000..b39bac76d --- /dev/null +++ b/palette/src/rgb/packed.rs @@ -0,0 +1,458 @@ +use crate::component::{Component, FromComponent, IntoComponent}; +use crate::rgb::{Rgb, RgbStandard, Rgba}; + +/// RGBA color packed in RGBA order. +/// +/// Packed integer types represented in `u32` with 2 hexadecimal digits (8-bits) +/// to express each value of the Red, Green, Blue, and Alpha components in an +/// sRGB color with alpha. Each type represents a different ordering of the RGBA +/// struct. +/// +/// Note that conversion from float to integer component types maps the floating +/// point value to the integer's value range and then rounds the result before +/// casting to the integer type. An example of the difference in calculated +/// results is shown below. +/// ``` +/// use approx::assert_relative_eq; +/// use palette::{PackedRgba, Srgba}; +/// +/// let packed: PackedRgba = Srgba::new(0.5, 0.0, 0.0, 0.5).into(); +/// assert_eq!(0x8000_0080, packed.color); +/// +/// let unpacked = PackedRgba::from(0x80FF_FF80).into(); +/// assert_relative_eq!(Srgba::new(0.5, 1.0, 1.0, 0.5), unpacked, epsilon = 0.01); +/// +/// // The second assert is essentially how library components are converted +/// let float = 0.5f32 * 255.0; +/// assert_eq!(0x7F, float as u32); +/// assert_eq!(0x80, float.round() as u32); +/// ``` +/// +/// When an `Srgb` type is packed, the alpha value will be `FF` in the +/// corresponding `u32`. Converting from a packed color type back to an `Srgb` +/// type will disregard the alpha value. + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct PackedRgba { + /// The sRGB color packed into a `u32`. + pub color: u32, +} +/// RGBA color packed in ARGB order. +/// +/// See [PackedRgba](struct.PackedRgba.html) for more details. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct PackedArgb { + /// The sRGB color packed into a `u32`. + pub color: u32, +} +/// RGBA color packed in BGRA order. +/// +/// See [PackedRgba](struct.PackedRgba.html) for more details. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct PackedBgra { + /// The sRGB color packed into a `u32`. + pub color: u32, +} +/// RGBA color packed in ABGR order. +/// +/// See [PackedRgba](struct.PackedRgba.html) for more details. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct PackedAbgr { + /// The sRGB color packed into a `u32`. + pub color: u32, +} + +impl From for PackedRgba { + fn from(color: u32) -> Self { + PackedRgba { color } + } +} + +impl Into for PackedRgba { + fn into(self) -> u32 { + self.color + } +} + +impl From> for PackedRgba +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgb) -> Self { + let dst: Rgb = color.into_format(); + PackedRgba { + color: 0x0000_00FF + | ((dst.red as u32) << 24) + | ((dst.green as u32) << 16) + | ((dst.blue as u32) << 8), + } + } +} + +impl Into> for PackedRgba +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgb { + let bytes = self.color.to_be_bytes(); + Rgb::from_components((bytes[0], bytes[1], bytes[2])).into_format() + } +} + +impl From> for PackedRgba +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgba) -> Self { + PackedRgba { + color: Rgba::::into_hex(color), + } + } +} + +impl Into> for PackedRgba +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgba { + Rgba::::from_hex(self.color) + } +} + +impl From for PackedArgb { + fn from(color: u32) -> Self { + PackedArgb { color } + } +} + +impl Into for PackedArgb { + fn into(self) -> u32 { + self.color + } +} + +impl From> for PackedArgb +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgb) -> Self { + let dst: Rgb = color.into_format(); + PackedArgb { + color: 0xFF00_0000 + | ((dst.red as u32) << 16) + | ((dst.green as u32) << 8) + | (dst.blue as u32), + } + } +} + +impl Into> for PackedArgb +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgb { + let bytes = self.color.to_be_bytes(); + Rgb::from_components((bytes[1], bytes[2], bytes[3])).into_format() + } +} + +impl From> for PackedArgb +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgba) -> Self { + let dst: Rgba = color.into_format(); + PackedArgb { + color: 0x0000_0000 + | ((dst.alpha as u32) << 24) + | ((dst.red as u32) << 16) + | ((dst.green as u32) << 8) + | (dst.blue as u32), + } + } +} + +impl Into> for PackedArgb +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgba { + let bytes = self.color.to_be_bytes(); + Rgba::from_components((bytes[1], bytes[2], bytes[3], bytes[0])).into_format() + } +} + +impl From for PackedBgra { + fn from(color: u32) -> Self { + PackedBgra { color } + } +} + +impl Into for PackedBgra { + fn into(self) -> u32 { + self.color + } +} + +impl From> for PackedBgra +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgb) -> Self { + let dst: Rgb = color.into_format(); + PackedBgra { + color: 0x0000_00FF + | ((dst.blue as u32) << 24) + | ((dst.green as u32) << 16) + | ((dst.red as u32) << 8), + } + } +} + +impl Into> for PackedBgra +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgb { + let bytes = self.color.to_be_bytes(); + Rgb::from_components((bytes[2], bytes[1], bytes[0])).into_format() + } +} + +impl From> for PackedBgra +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgba) -> Self { + let dst: Rgba = color.into_format(); + PackedBgra { + color: 0x0000_0000 + | ((dst.blue as u32) << 24) + | ((dst.green as u32) << 16) + | ((dst.red as u32) << 8) + | (dst.alpha as u32), + } + } +} + +impl Into> for PackedBgra +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgba { + let bytes = self.color.to_be_bytes(); + Rgba::from_components((bytes[2], bytes[1], bytes[0], bytes[3])).into_format() + } +} + +impl From for PackedAbgr { + fn from(color: u32) -> Self { + PackedAbgr { color } + } +} + +impl Into for PackedAbgr { + fn into(self) -> u32 { + self.color + } +} + +impl From> for PackedAbgr +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgb) -> Self { + let dst: Rgb = color.into_format(); + PackedAbgr { + color: 0xFF00_0000 + | ((dst.blue as u32) << 16) + | ((dst.green as u32) << 8) + | (dst.red as u32), + } + } +} + +impl Into> for PackedAbgr +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgb { + let bytes = self.color.to_be_bytes(); + Rgb::from_components((bytes[3], bytes[2], bytes[1])).into_format() + } +} + +impl From> for PackedAbgr +where + T: Component + IntoComponent, + S: RgbStandard, +{ + fn from(color: Rgba) -> Self { + let dst: Rgba = color.into_format(); + PackedAbgr { + color: 0x0000_0000 + | ((dst.alpha as u32) << 24) + | ((dst.blue as u32) << 16) + | ((dst.green as u32) << 8) + | (dst.red as u32), + } + } +} + +impl Into> for PackedAbgr +where + T: Component + FromComponent, + S: RgbStandard, +{ + fn into(self) -> Rgba { + let bytes = self.color.to_be_bytes(); + Rgba::from_components((bytes[3], bytes[2], bytes[1], bytes[0])).into_format() + } +} + +#[cfg(test)] +mod test { + use crate::{PackedAbgr, PackedArgb, PackedBgra, PackedRgba, Srgb, Srgba}; + + #[test] + fn rgba() { + let a1: PackedRgba = Srgb::new(0.5, 0.0, 0.0).into(); + let a2: PackedRgba = Srgb::new(0.0, 1.0, 0.0).into(); + let a3: PackedRgba = Srgb::new(0.0, 0.0, 0.5).into(); + let x1: u32 = 0x8000_00FF; + let x2: u32 = 0x00FF_00FF; + let x3: u32 = 0x0000_80FF; + assert_eq!(a1.color, x1); + assert_eq!(a2.color, x2); + assert_eq!(a3.color, x3); + + let unpacked = PackedRgba::from(0x80FF_80FF).into(); + assert_relative_eq!(Srgb::new(0.5, 1.0, 0.5), unpacked, epsilon = 0.01); + + let b1: PackedRgba = Srgba::new(0.5, 0.0, 0.0, 0.0).into(); + let b2: PackedRgba = Srgba::new(0.0, 1.0, 0.0, 0.0).into(); + let b3: PackedRgba = Srgba::new(0.0, 0.0, 0.5, 0.0).into(); + let b4: PackedRgba = Srgba::new(0.0, 0.0, 0.0, 1.0).into(); + let y1: u32 = 0x8000_0000; + let y2: u32 = 0x00FF_0000; + let y3: u32 = 0x0000_8000; + let y4: u32 = 0x0000_00FF; + assert_eq!(b1.color, y1); + assert_eq!(b2.color, y2); + assert_eq!(b3.color, y3); + assert_eq!(b4.color, y4); + + let unpacked = PackedRgba::from(0x80FF_80FF).into(); + assert_relative_eq!(Srgba::new(0.5, 1.0, 0.5, 1.0), unpacked, epsilon = 0.01); + } + + #[test] + fn argb() { + let a1: PackedArgb = Srgb::new(0.5, 0.0, 0.0).into(); + let a2: PackedArgb = Srgb::new(0.0, 1.0, 0.0).into(); + let a3: PackedArgb = Srgb::new(0.0, 0.0, 0.5).into(); + let x1: u32 = 0xFF80_0000; + let x2: u32 = 0xFF00_FF00; + let x3: u32 = 0xFF00_0080; + assert_eq!(a1.color, x1); + assert_eq!(a2.color, x2); + assert_eq!(a3.color, x3); + + let unpacked = PackedArgb::from(0x80FF_80FF).into(); + assert_relative_eq!(Srgb::new(1.0, 0.5, 1.0), unpacked, epsilon = 0.01); + + let b1: PackedArgb = Srgba::new(0.5, 0.0, 0.0, 0.0).into(); + let b2: PackedArgb = Srgba::new(0.0, 1.0, 0.0, 0.0).into(); + let b3: PackedArgb = Srgba::new(0.0, 0.0, 0.5, 0.0).into(); + let b4: PackedArgb = Srgba::new(0.0, 0.0, 0.0, 1.0).into(); + let y1: u32 = 0x0080_0000; + let y2: u32 = 0x0000_FF00; + let y3: u32 = 0x0000_0080; + let y4: u32 = 0xFF00_0000; + assert_eq!(b1.color, y1); + assert_eq!(b2.color, y2); + assert_eq!(b3.color, y3); + assert_eq!(b4.color, y4); + + let unpacked = PackedArgb::from(0x80FF_80FF).into(); + assert_relative_eq!(Srgba::new(1.0, 0.5, 1.0, 0.5), unpacked, epsilon = 0.01); + } + + #[test] + fn bgra() { + let a1: PackedBgra = Srgb::new(0.5, 0.0, 0.0).into(); + let a2: PackedBgra = Srgb::new(0.0, 1.0, 0.0).into(); + let a3: PackedBgra = Srgb::new(0.0, 0.0, 0.5).into(); + let x1: u32 = 0x0000_80FF; + let x2: u32 = 0x00FF_00FF; + let x3: u32 = 0x8000_00FF; + assert_eq!(a1.color, x1); + assert_eq!(a2.color, x2); + assert_eq!(a3.color, x3); + + let unpacked = PackedBgra::from(0x80FF_FF80).into(); + assert_relative_eq!(Srgb::new(1.0, 1.0, 0.5), unpacked, epsilon = 0.01); + + let b1: PackedBgra = Srgba::new(0.5, 0.0, 0.0, 0.0).into(); + let b2: PackedBgra = Srgba::new(0.0, 1.0, 0.0, 0.0).into(); + let b3: PackedBgra = Srgba::new(0.0, 0.0, 0.5, 0.0).into(); + let b4: PackedBgra = Srgba::new(0.0, 0.0, 0.0, 1.0).into(); + let y1: u32 = 0x0000_8000; + let y2: u32 = 0x00FF_0000; + let y3: u32 = 0x8000_0000; + let y4: u32 = 0x0000_00FF; + assert_eq!(b1.color, y1); + assert_eq!(b2.color, y2); + assert_eq!(b3.color, y3); + assert_eq!(b4.color, y4); + + let unpacked = PackedBgra::from(0x80FF_FF80).into(); + assert_relative_eq!(Srgba::new(1.0, 1.0, 0.5, 0.5), unpacked, epsilon = 0.01); + } + + #[test] + fn abgr() { + let a1: PackedAbgr = Srgb::new(0.5, 0.0, 0.0).into(); + let a2: PackedAbgr = Srgb::new(0.0, 1.0, 0.0).into(); + let a3: PackedAbgr = Srgb::new(0.0, 0.0, 0.5).into(); + let x1: u32 = 0xFF00_0080; + let x2: u32 = 0xFF00_FF00; + let x3: u32 = 0xFF80_0000; + assert_eq!(a1.color, x1); + assert_eq!(a2.color, x2); + assert_eq!(a3.color, x3); + + let unpacked = PackedAbgr::from(0x80FF_FF80).into(); + assert_relative_eq!(Srgb::new(0.5, 1.0, 1.0), unpacked, epsilon = 0.01); + + let b1: PackedAbgr = Srgba::new(0.5, 0.0, 0.0, 0.0).into(); + let b2: PackedAbgr = Srgba::new(0.0, 1.0, 0.0, 0.0).into(); + let b3: PackedAbgr = Srgba::new(0.0, 0.0, 0.5, 0.0).into(); + let b4: PackedAbgr = Srgba::new(0.0, 0.0, 0.0, 1.0).into(); + let y1: u32 = 0x0000_0080; + let y2: u32 = 0x0000_FF00; + let y3: u32 = 0x0080_0000; + let y4: u32 = 0xFF00_0000; + assert_eq!(b1.color, y1); + assert_eq!(b2.color, y2); + assert_eq!(b3.color, y3); + assert_eq!(b4.color, y4); + + let unpacked = PackedAbgr::from(0x80FF_FF80).into(); + assert_relative_eq!(Srgba::new(0.5, 1.0, 1.0, 0.5), unpacked, epsilon = 0.01); + } +} diff --git a/palette/src/rgb/rgb.rs b/palette/src/rgb/rgb.rs index 427d0ed4d..5b0d4cb99 100644 --- a/palette/src/rgb/rgb.rs +++ b/palette/src/rgb/rgb.rs @@ -19,8 +19,8 @@ use crate::rgb::{RgbSpace, RgbStandard, TransferFn}; use crate::white_point::WhitePoint; use crate::{clamp, contrast_ratio, from_f64}; use crate::{ - Blend, Component, ComponentWise, FloatComponent, FromComponent, GetHue, Limited, Mix, Pixel, - RelativeContrast, Shade, + Blend, Component, ComponentWise, FloatComponent, FromComponent, GetHue, IntoComponent, Limited, + Mix, Pixel, RelativeContrast, Shade, }; use crate::{Hsl, Hsv, Hwb, Lab, Lch, Luma, RgbHue, Xyz, Yxy}; @@ -116,6 +116,26 @@ impl Rgb { pub fn from_components((red, green, blue): (T, T, T)) -> Self { Self::new(red, green, blue) } + + /// Convert to a packed `u32` with format `0xRRGGBB`. + pub fn into_hex(color: Rgb) -> u32 + where + U: Component + IntoComponent, + { + 0x0000_0000 + | ((U::into_component(color.red) as u32) << 16) + | ((U::into_component(color.green) as u32) << 8) + | (U::into_component(color.blue) as u32) + } + + /// Convert from a packed `u32` with format `0xRRGGBB`. + pub fn from_hex(color: u32) -> Rgb + where + U: Component + FromComponent, + { + let bytes = color.to_be_bytes(); + Rgb::from_components((bytes[1], bytes[2], bytes[3])).into_format() + } } impl Rgb { @@ -226,6 +246,27 @@ impl Alpha, A> { pub fn from_components((red, green, blue, alpha): (T, T, T, A)) -> Self { Self::new(red, green, blue, alpha) } + + /// Convert to a packed `u32` with format `0xRRGGBBAA`. + pub fn into_hex(color: Rgba) -> u32 + where + U: Component + IntoComponent, + { + 0x0000_0000 + | ((U::into_component(color.red) as u32) << 24) + | ((U::into_component(color.green) as u32) << 16) + | ((U::into_component(color.blue) as u32) << 8) + | (U::into_component(color.alpha) as u32) + } + + /// Convert from a packed `u32` with format `0xRRGGBBAA`. + pub fn from_hex(color: u32) -> Rgba + where + U: Component + FromComponent, + { + let bytes = color.to_be_bytes(); + Rgba::from_components((bytes[0], bytes[1], bytes[2], bytes[3])).into_format() + } } /// [`Rgba`](rgb/type.Rgba.html) implementations. @@ -1058,7 +1099,7 @@ where #[cfg(test)] mod test { - use super::Rgb; + use super::{Rgb, Rgba}; use crate::encoding::Srgb; use core::str::FromStr; @@ -1169,6 +1210,19 @@ mod test { ); } + #[test] + fn rgb_hex_into_from() { + let c1 = Rgb::::from_hex(0x11007FFF); + let c2 = Rgb::::new(0u8, 127, 255); + assert_eq!(c1, c2); + assert_eq!(Rgb::::into_hex(c1), 0x0000_7FFF); + + let c1 = Rgba::::from_hex(0x007F_FF80); + let c2 = Rgba::::new(0u8, 127, 255, 128); + assert_eq!(c1, c2); + assert_eq!(Rgba::::into_hex(c1), 0x007F_FF80); + } + #[cfg(feature = "serializing")] #[test] fn serialize() {