Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to derive Pixel #93

Merged
merged 1 commit into from
Apr 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions palette/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ use encoding::Linear;
/// use num_traits::Float;
///
/// /// sRGB, but with a reversed memory layout.
/// #[derive(PartialEq, Debug, FromColor)]
/// #[derive(PartialEq, Debug, FromColor, Pixel)]
/// #[palette_manual_from(Rgb = "from_rgb_internal")]
/// #[palette_component = "T"]
/// #[repr(C)] // Makes sure the memory layout is as we want it.
Expand All @@ -123,11 +123,6 @@ use encoding::Linear;
/// red: T,
/// }
///
/// // Careful with this one! It requires `#[repr(C)]`.
/// unsafe impl<T> Pixel<T> for Bgr<T> {
/// const CHANNELS: usize = 3;
/// }
///
/// // Rgb is a bit more complex than other colors, so we are
/// // implementing a private conversion function and letting it
/// // derive `From` automatically. It will take a round trip
Expand Down Expand Up @@ -368,7 +363,7 @@ where
/// use num_traits::Float;
///
/// /// sRGB, but with a reversed memory layout.
/// #[derive(Copy, Clone, IntoColor)]
/// #[derive(Copy, Clone, IntoColor, Pixel)]
/// #[palette_manual_into(Rgb = "into_rgb_internal")]
/// #[palette_component = "T"]
/// #[repr(C)] // Makes sure the memory layout is as we want it.
Expand All @@ -378,11 +373,6 @@ where
/// red: T,
/// }
///
/// // Careful with this one! It requires `#[repr(C)]`.
/// unsafe impl<T> Pixel<T> for Bgr<T> {
/// const CHANNELS: usize = 3;
/// }
///
/// // Rgb is a bit more complex than other colors, so we are
/// // implementing a private conversion function and letting it
/// // derive `Into` automatically.
Expand Down
110 changes: 104 additions & 6 deletions palette/src/encoding/pixel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,106 @@ mod raw;
/// Represents colors that can be serialized and deserialized from raw color components.
///
/// This uses bit by bit conversion, so make sure that anything that implements it can be
/// represented as a contiguous sequence of a single type `T`.
/// represented as a contiguous sequence of a single type `T`. This is most safely done using
/// `#[derive(Pixel)]`.
///
/// # Deriving
///
/// `Pixel` can be automatically derived. The only requirements are that the type is a `struct`,
/// that it has a `#[repr(C)]` attribute, and that all of its fields have the same types. It stays
/// on the conservative side and will show an error if any of those requirements are not fulfilled.
/// If some fields have different types, but the same memory layout, or are zero-sized, they can be
/// marked with attributes to show that their types are safe to use.
///
/// ## Field Attributes
///
/// * `#[palette_unsafe_same_layout_as = "SomeType"]`: Mark the field as having the same memory
/// layout as `SomeType`.
///
/// **Unsafety:** corrupt data and undefined behavior may occur if this is not true!
///
/// * `#[palette_unsafe_zero_sized]`: Mark the field as being zero-sized, and thus not taking up
/// any memory space. This means that it can be ignored.
///
/// **Unsafety:** corrupt data and undefined behavior may occur if this is not true!
///
/// ## Examples
///
/// Basic use:
///
/// ```rust
/// #[macro_use]
/// extern crate palette_derive;
/// extern crate palette;
///
/// use palette::Pixel;
///
/// #[derive(PartialEq, Debug, Pixel)]
/// #[repr(C)]
/// struct MyCmyk {
/// cyan: f32,
/// magenta: f32,
/// yellow: f32,
/// key: f32,
/// }
///
/// fn main() {
/// let buffer = [0.1, 0.2, 0.3, 0.4];
/// let color = MyCmyk::from_raw(&buffer);
///
/// assert_eq!(
/// color,
/// &MyCmyk {
/// cyan: 0.1,
/// magenta: 0.2,
/// yellow: 0.3,
/// key: 0.4,
/// }
/// );
/// }
/// ```
///
/// Heterogenous field types:
///
/// ```rust
/// #[macro_use]
/// extern crate palette_derive;
/// extern crate palette;
///
/// use std::marker::PhantomData;
///
/// use palette::{Pixel, RgbHue};
/// use palette::rgb::RgbStandard;
/// use palette::encoding::Srgb;
///
/// #[derive(PartialEq, Debug, Pixel)]
/// #[repr(C)]
/// struct MyCoolColor<S: RgbStandard> {
/// #[palette_unsafe_zero_sized]
/// standard: PhantomData<S>,
/// // RgbHue is a wrapper with `#[repr(C)]`, so it can safely
/// // be converted straight from `f32`.
/// #[palette_unsafe_same_layout_as = "f32"]
/// hue: RgbHue<f32>,
/// lumen: f32,
/// chroma: f32,
/// }
///
/// fn main() {
/// let buffer = [172.0, 100.0, 0.3];
/// let color = MyCoolColor::<Srgb>::from_raw(&buffer);
///
/// assert_eq!(
/// color,
/// &MyCoolColor {
/// hue: 172.0.into(),
/// lumen: 100.0,
/// chroma: 0.3,
/// standard: PhantomData,
/// }
/// );
/// }
/// ```
pub unsafe trait Pixel<T>: Sized {
/// The number of color channels.
const CHANNELS: usize;
Expand Down Expand Up @@ -58,7 +157,7 @@ pub unsafe trait Pixel<T>: Sized {
/// Cast a slice of raw color components to a slice of colors.
///
/// ```rust
/// use palette::{Srgb, Pixel};
/// use palette::{Pixel, Srgb};
///
/// let raw = &[255u8, 128, 64, 10, 20, 30];
/// let colors = Srgb::from_raw_slice(raw);
Expand All @@ -77,7 +176,7 @@ pub unsafe trait Pixel<T>: Sized {
/// Cast a mutable slice of raw color components to a mutable slice of colors.
///
/// ```rust
/// use palette::{Srgb, Pixel};
/// use palette::{Pixel, Srgb};
///
/// let raw = &mut [255u8, 128, 64, 10, 20, 30];
/// {
Expand All @@ -102,7 +201,7 @@ pub unsafe trait Pixel<T>: Sized {
/// Cast a slice of colors to a slice of raw color components.
///
/// ```rust
/// use palette::{Srgb, Pixel};
/// use palette::{Pixel, Srgb};
///
/// let colors = &[Srgb::new(255u8, 128, 64), Srgb::new(10, 20, 30)];
/// let raw = Srgb::into_raw_slice(colors);
Expand All @@ -119,7 +218,7 @@ pub unsafe trait Pixel<T>: Sized {
/// Cast a mutable slice of colors to a mutable slice of raw color components.
///
/// ```rust
/// use palette::{Srgb, Pixel};
/// use palette::{Pixel, Srgb};
///
/// let colors = &mut [Srgb::new(255u8, 128, 64), Srgb::new(10, 20, 30)];
/// {
Expand All @@ -131,7 +230,6 @@ pub unsafe trait Pixel<T>: Sized {
/// raw[3] = 200;
/// }
///
///
/// assert_eq!(colors[0].blue, 100);
/// assert_eq!(colors[1].red, 200);
/// ```
Expand Down
16 changes: 9 additions & 7 deletions palette/src/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub type Hsla<S = Srgb, T = f32> = Alpha<Hsl<S, T>, T>;
///more gray, or making it darker.
///
///See [HSV](struct.Hsv.html) for a very similar color space, with brightness instead of lightness.
#[derive(Debug, PartialEq, FromColor)]
#[derive(Debug, PartialEq, FromColor, Pixel)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[palette_internal]
#[palette_rgb_space = "S"]
Expand All @@ -40,6 +40,7 @@ where
{
///The hue of the color, in degrees. Decides if it's red, blue, purple,
///etc.
#[palette_unsafe_same_layout_as = "T"]
pub hue: RgbHue<T>,

///The colorfulness of the color. 0.0 gives gray scale colors and 1.0 will
Expand All @@ -53,6 +54,7 @@ where
///The white point and RGB primaries this color is adapted to. The default
///is the sRGB standard.
#[cfg_attr(feature = "serde", serde(skip))]
#[palette_unsafe_zero_sized]
pub space: PhantomData<S>,
}

Expand All @@ -73,10 +75,6 @@ where
}
}

unsafe impl<S: RgbSpace, T: Component + Float> Pixel<T> for Hsl<S, T> {
const CHANNELS: usize = 3;
}

impl<T> Hsl<Srgb, T>
where
T: Component + Float,
Expand Down Expand Up @@ -586,13 +584,17 @@ mod test {
fn serialize() {
let serialized = ::serde_json::to_string(&Hsl::new(0.3, 0.8, 0.1)).unwrap();

assert_eq!(serialized, r#"{"hue":0.3,"saturation":0.8,"lightness":0.1}"#);
assert_eq!(
serialized,
r#"{"hue":0.3,"saturation":0.8,"lightness":0.1}"#
);
}

#[cfg(feature = "serde")]
#[test]
fn deserialize() {
let deserialized: Hsl = ::serde_json::from_str(r#"{"hue":0.3,"saturation":0.8,"lightness":0.1}"#).unwrap();
let deserialized: Hsl =
::serde_json::from_str(r#"{"hue":0.3,"saturation":0.8,"lightness":0.1}"#).unwrap();

assert_eq!(deserialized, Hsl::new(0.3, 0.8, 0.1));
}
Expand Down
8 changes: 3 additions & 5 deletions palette/src/hsv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub type Hsva<S = Srgb, T = f32> = Alpha<Hsv<S, T>, T>;
///_lightness_. The difference is that, for example, red (100% R, 0% G, 0% B)
///and white (100% R, 100% G, 100% B) has the same brightness (or value), but
///not the same lightness.
#[derive(Debug, PartialEq, FromColor)]
#[derive(Debug, PartialEq, FromColor, Pixel)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[palette_internal]
#[palette_white_point = "S::WhitePoint"]
Expand All @@ -39,6 +39,7 @@ where
{
///The hue of the color, in degrees. Decides if it's red, blue, purple,
///etc.
#[palette_unsafe_same_layout_as = "T"]
pub hue: RgbHue<T>,

///The colorfulness of the color. 0.0 gives gray scale colors and 1.0 will
Expand All @@ -53,6 +54,7 @@ where
///The white point and RGB primaries this color is adapted to. The default
///is the sRGB standard.
#[cfg_attr(feature = "serde", serde(skip))]
#[palette_unsafe_zero_sized]
pub space: PhantomData<S>,
}

Expand All @@ -73,10 +75,6 @@ where
}
}

unsafe impl<S: RgbSpace, T: Component + Float> Pixel<T> for Hsv<S, T> {
const CHANNELS: usize = 3;
}

impl<T> Hsv<Srgb, T>
where
T: Component + Float,
Expand Down
8 changes: 3 additions & 5 deletions palette/src/hwb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub type Hwba<S = Srgb, T = f32> = Alpha<Hwb<S, T>, T>;
///then a degree of whiteness and blackness to mix into that base hue.
///
///It is very intuitive for humans to use and many color-pickers are based on the HWB color system
#[derive(Debug, PartialEq, FromColor)]
#[derive(Debug, PartialEq, FromColor, Pixel)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[palette_internal]
#[palette_rgb_space = "S"]
Expand All @@ -37,6 +37,7 @@ where
{
///The hue of the color, in degrees. Decides if it's red, blue, purple,
///etc. Same as the hue for HSL and HSV.
#[palette_unsafe_same_layout_as = "T"]
pub hue: RgbHue<T>,

///The whiteness of the color. It specifies the amount white to mix into the hue.
Expand All @@ -54,6 +55,7 @@ where
///The white point and RGB primaries this color is adapted to. The default
///is the sRGB standard.
#[cfg_attr(feature = "serde", serde(skip))]
#[palette_unsafe_zero_sized]
pub space: PhantomData<S>,
}

Expand All @@ -74,10 +76,6 @@ where
}
}

unsafe impl<S: RgbSpace, T: Component + Float> Pixel<T> for Hwb<S, T> {
const CHANNELS: usize = 3;
}

impl<T> Hwb<Srgb, T>
where
T: Component + Float,
Expand Down
7 changes: 2 additions & 5 deletions palette/src/lab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub type Laba<Wp, T = f32> = Alpha<Lab<Wp, T>, T>;
///
///The parameters of L\*a\*b\* are quite different, compared to many other color
///spaces, so manipulating them manually may be unintuitive.
#[derive(Debug, PartialEq, FromColor)]
#[derive(Debug, PartialEq, FromColor, Pixel)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[palette_internal]
#[palette_white_point = "Wp"]
Expand All @@ -49,6 +49,7 @@ where
///The white point associated with the color's illuminant and observer.
///D65 for 2 degree observer is used by default.
#[cfg_attr(feature = "serde", serde(skip))]
#[palette_unsafe_zero_sized]
pub white_point: PhantomData<Wp>,
}

Expand All @@ -69,10 +70,6 @@ where
}
}

unsafe impl<Wp: WhitePoint, T: Component + Float> Pixel<T> for Lab<Wp, T> {
const CHANNELS: usize = 3;
}

impl<T> Lab<D65, T>
where
T: Component + Float,
Expand Down
8 changes: 3 additions & 5 deletions palette/src/lch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub type Lcha<Wp, T = f32> = Alpha<Lch<Wp, T>, T>;
///cylindrical color space, like [HSL](struct.Hsl.html) and
///[HSV](struct.Hsv.html). This gives it the same ability to directly change
///the hue and colorfulness of a color, while preserving other visual aspects.
#[derive(Debug, PartialEq, FromColor)]
#[derive(Debug, PartialEq, FromColor, Pixel)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[palette_internal]
#[palette_white_point = "Wp"]
Expand All @@ -43,11 +43,13 @@ where

///The hue of the color, in degrees. Decides if it's red, blue, purple,
///etc.
#[palette_unsafe_same_layout_as = "T"]
pub hue: LabHue<T>,

///The white point associated with the color's illuminant and observer.
///D65 for 2 degree observer is used by default.
#[cfg_attr(feature = "serde", serde(skip))]
#[palette_unsafe_zero_sized]
pub white_point: PhantomData<Wp>,
}

Expand All @@ -68,10 +70,6 @@ where
}
}

unsafe impl<Wp: WhitePoint, T: Component + Float> Pixel<T> for Lch<Wp, T> {
const CHANNELS: usize = 3;
}

impl<T> Lch<D65, T>
where
T: Component + Float,
Expand Down
Loading