From fb667ccdbd481bbd589a812018146d52beed783c Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 7 Feb 2023 00:19:51 +0000 Subject: [PATCH] Implement grb Fixes #55 --- Cargo.toml | 1 + src/alt.rs | 22 ++++++++++-- src/internal/convert/mod.rs | 4 +++ src/internal/ops.rs | 6 ++++ src/internal/rgb.rs | 72 ++++++++++++++++++++++++------------- 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f8870eb..8bd6105 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ default = ["as-bytes"] # safe as_bytes() casts require a marker trait for non-padded, non-pointery types. as-bytes = ["bytemuck"] argb = [] +grb = [] [badges] travis-ci = { repository = "kornelski/rust-rgb" } diff --git a/src/alt.rs b/src/alt.rs index fa09fd4..cd1043a 100644 --- a/src/alt.rs +++ b/src/alt.rs @@ -30,10 +30,10 @@ pub struct BGRA { pub a: AlphaComponentType, } +#[cfg(feature = "argb")] #[repr(C)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[cfg(feature = "argb")] /// A+BGR pub struct ABGR { /// Alpha first @@ -46,10 +46,10 @@ pub struct ABGR { pub r: ComponentType, } +#[cfg(feature = "argb")] #[repr(C)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[cfg(feature = "argb")] /// A+RGB pub struct ARGB { /// Alpha first @@ -90,6 +90,24 @@ pub type ABGR16 = ABGR; #[cfg(feature = "argb")] pub type ARGB16 = ARGB; +#[cfg(feature = "grb")] +#[repr(C)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] +/// RGB with red-green swapped (may be useful for LEDs) +pub struct GRB { + /// Green first + pub g: ComponentType, + /// Red + pub r: ComponentType, + /// Blue last + pub b: ComponentType, +} + +/// 8-bit GRB +#[cfg(feature = "grb")] +pub type GRB8 = GRB; + //////////////////////////////////////// #[repr(C)] diff --git a/src/internal/convert/mod.rs b/src/internal/convert/mod.rs index 79f9358..c5293cd 100644 --- a/src/internal/convert/mod.rs +++ b/src/internal/convert/mod.rs @@ -54,6 +54,8 @@ as_pixels_impl!{RGB, 3} as_pixels_impl!{RGBA, 4} as_pixels_impl!{BGR, 3} as_pixels_impl!{BGRA, 4} +#[cfg(feature = "grb")] +as_pixels_impl!{GRB, 3} as_pixels_impl!{Gray, 1} as_pixels_impl!{GrayAlpha, 2} #[cfg(feature = "argb")] @@ -288,6 +290,8 @@ reorder_impl_from!(@rgb RGB, BGR); reorder_impl_from!(@rgba BGRA, RGBA); #[cfg(feature = "argb")] reorder_impl_from!(@rgba ABGR, RGBA); +#[cfg(feature = "grb")] +reorder_impl_from!(@rgb RGB, GRB); impl From> for RGB { #[inline(always)] diff --git a/src/internal/ops.rs b/src/internal/ops.rs index 16b671a..6731d66 100644 --- a/src/internal/ops.rs +++ b/src/internal/ops.rs @@ -7,6 +7,8 @@ use core::ops::*; use core::iter::Sum; #[cfg(feature = "argb")] use crate::alt::ARGB; +#[cfg(feature = "grb")] +use crate::alt::GRB; macro_rules! impl_struct_ops_opaque { ($ty:ident => $($field:tt)+) => { @@ -268,10 +270,14 @@ impl_scalar!{RGB} impl_scalar!{RGBA} #[cfg(feature = "argb")] impl_scalar!{ARGB} +#[cfg(feature = "grb")] +impl_scalar!{GRB} impl_scalar!{Gray} impl_scalar!{GrayAlpha} impl_struct_ops_opaque! {RGB => r g b} +#[cfg(feature = "grb")] +impl_struct_ops_opaque! {GRB => g r b} impl_struct_ops_opaque! {Gray => 0} impl_struct_ops_alpha! {RGBA => r g b a} diff --git a/src/internal/rgb.rs b/src/internal/rgb.rs index 736f8e5..ad78dff 100644 --- a/src/internal/rgb.rs +++ b/src/internal/rgb.rs @@ -4,6 +4,8 @@ use crate::alt::BGRA; use crate::RGB; use crate::RGBA; use core::fmt; +#[cfg(feature = "grb")] +use crate::alt::GRB; impl RGB { /// Convenience function for creating a new pixel @@ -34,35 +36,13 @@ unsafe impl crate::Zeroable for RGB where T: crate::Zeroable {} unsafe impl crate::Zeroable for BGR where T: crate::Zeroable {} macro_rules! impl_rgb { - ($RGB:ident, $RGBA:ident) => { + ($RGB:ident) => { impl $RGB { /// Iterate over color components (R, G, and B) #[inline(always)] pub fn iter(&self) -> core::iter::Cloned> { self.as_slice().iter().cloned() } - - /// Convenience function for converting to RGBA - #[inline(always)] - pub fn alpha(&self, a: T) -> $RGBA { - $RGBA { - r: self.r.clone(), - g: self.g.clone(), - b: self.b.clone(), - a, - } - } - - /// Convenience function for converting to RGBA with alpha channel of a different type than type of the pixels - #[inline(always)] - pub fn new_alpha(&self, a: A) -> $RGBA { - $RGBA { - r: self.r.clone(), - g: self.g.clone(), - b: self.b.clone(), - a, - } - } } impl ComponentMap<$RGB, T, B> for $RGB { @@ -126,6 +106,35 @@ macro_rules! impl_rgb { } } +macro_rules! impl_rgb_to_alpha { + ($RGB:ident, $RGBA:ident) => { + impl $RGB { + /// Convenience function for converting to RGBA + #[inline(always)] + pub fn alpha(&self, a: T) -> $RGBA { + $RGBA { + r: self.r.clone(), + g: self.g.clone(), + b: self.b.clone(), + a, + } + } + + /// Convenience function for converting to RGBA with alpha channel of a different type than type of the pixels + #[inline(always)] + pub fn new_alpha(&self, a: A) -> $RGBA { + $RGBA { + r: self.r.clone(), + g: self.g.clone(), + b: self.b.clone(), + a, + } + } + } + } +} + + impl core::iter::FromIterator for RGB { /// Takes exactly 3 elements from the iterator and creates a new instance. /// Panics if there are fewer elements in the iterator. @@ -140,8 +149,12 @@ impl core::iter::FromIterator for RGB { } } -impl_rgb!{RGB, RGBA} -impl_rgb!{BGR, BGRA} +impl_rgb!{RGB} +impl_rgb_to_alpha!{RGB, RGBA} +impl_rgb!{BGR} +impl_rgb_to_alpha!{BGR, BGRA} +#[cfg(feature = "grb")] +impl_rgb!{GRB} impl fmt::Display for RGB { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -183,6 +196,15 @@ impl fmt::LowerHex for BGR { mod rgb_test { use super::*; use std; + + #[test] + #[cfg(feature = "grb")] + fn grb_test() { + let grb = GRB {g:1,r:2,b:3}.map(|c| c * 2) + 1; + let rgb: crate::RGB8 = grb.into(); + assert_eq!(rgb, RGB::new(5,3,7)); + } + #[test] fn sanity_check() { let neg = RGB::new(1,2,3i32).map(|x| -x);