1//! Basic struct for `RGB` and `RGBA` pixels. Packed, with red first, alpha last.
2//!
3//! This crate is intended to be the lowest common denominator for sharing `RGB`/`RGBA` bitmaps between other crates.
4//!
5//! The crate includes convenience functions for converting between the struct and bytes,
6//! and overloaded operators that work on all channels at once.
7//!
8//! This crate intentionally doesn't implement color management (due to complexity of the problem),
9//! but the structs can be parametrized to implement this if necessary. Other colorspaces are out of scope.
10//!
11//! ```rust
12//! # use rgb::*;
13//! let pixel = RGB8 {r:0, g:100, b:255};
14//!
15//! let pixel_rgba = pixel.alpha(255);
16//! let pixel = pixel_rgba.rgb();
17//!
18//! let pixels = vec![pixel; 100];
19//! use rgb::ComponentBytes; // Import byte conversion trait
20//! let bytes = pixels.as_bytes();
21//!
22//! use rgb::ComponentMap; // Import pixel map trait
23//! let half_bright = pixel.map(|channel| channel / 2);
24//! let doubled = half_bright * 2;
25//! # let _ = doubled;
26//! ```
27#![doc(html_logo_url = "https://kornel.ski/rgb-logo.png")]
28#![no_std]
29
30#![warn(missing_docs)]
31
32// std is required to run unit tests
33#[cfg(test)]
34#[macro_use] extern crate std;
35
36#[cfg(feature = "serde")]
37#[macro_use] extern crate serde;
38
39mod internal {
40 pub mod convert;
41 pub mod ops;
42 pub mod pixel;
43 pub mod rgb;
44 pub mod rgba;
45}
46
47/// BGR/BGRA alernative layouts & grayscale
48///
49/// BGR might be useful for some Windows or OpenGL APIs.
50pub mod alt;
51
52/// Re-export from `bytemuck` crate
53#[cfg(feature = "as-bytes")]
54pub use bytemuck::Pod;
55/// Re-export from `bytemuck` crate
56#[cfg(feature = "as-bytes")]
57pub use bytemuck::Zeroable;
58
59pub use crate::internal::convert::*;
60pub use crate::internal::ops::*;
61pub use crate::internal::pixel::*;
62pub use crate::internal::rgb::*;
63pub use crate::internal::rgba::*;
64
65#[repr(C)]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
68/// The RGB pixel
69///
70/// The component type can be `u8` (aliased as `RGB8`), `u16` (aliased as `RGB16`),
71/// or any other type (but simple copyable types are recommended.)
72pub struct RGB<ComponentType> {
73 /// Red
74 pub r: ComponentType,
75 /// Green
76 pub g: ComponentType,
77 /// Blue
78 pub b: ComponentType,
79}
80
81#[repr(C)]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
84/// The RGBA pixel
85///
86/// The component type can be `u8` (aliased as `RGBA8`), `u16` (aliased as `RGBA16`),
87/// or any other type (but simple copyable types are recommended.)
88///
89/// You can specify a different type for alpha, but it's only for special cases
90/// (e.g. if you use a newtype like `RGBA<LinearLight<u16>, u16>`).
91pub struct RGBA<ComponentType, AlphaComponentType = ComponentType> {
92 /// Red
93 pub r: ComponentType,
94 /// Green
95 pub g: ComponentType,
96 /// Blue
97 pub b: ComponentType,
98 /// Alpha
99 pub a: AlphaComponentType,
100}
101
102/// 8-bit RGB
103///
104/// The colorspace is technically undefined, but generally sRGB is assumed.
105pub type RGB8 = RGB<u8>;
106
107/// 16-bit RGB in machine's native endian
108///
109/// Be careful to perform byte-swapping when reading from files.
110pub type RGB16 = RGB<u16>;
111
112/// 8-bit RGBA, alpha is last. 0 = transparent, 255 = opaque.
113pub type RGBA8 = RGBA<u8>;
114
115/// 16-bit RGB in machine's native endian. 0 = transparent, 65535 = opaque.
116///
117/// Alpha is last.
118pub type RGBA16 = RGBA<u16>;
119
120#[test]
121fn rgb_works() {
122 let rgb = RGB{r:0u8,g:128,b:255}.clone();
123 assert_eq!(rgb.b, 255);
124
125 assert_eq!(rgb, rgb.iter().map(|ch| ch).collect());
126
127 #[cfg(feature = "as-bytes")]
128 {
129 assert_eq!(0, [rgb].as_bytes()[0]);
130 assert_eq!(128, [rgb].as_bytes()[1]);
131 assert_eq!(255, [rgb].as_bytes()[2]);
132 }
133
134 let rgb = RGB16{r:0u16,g:0x7F7F,b:65535};
135 assert_eq!(rgb.b, 65535);
136 assert_eq!(rgb.as_slice()[1], 0x7F7F);
137
138
139 #[cfg(feature = "as-bytes")]
140 {
141 assert_eq!(0, [rgb].as_bytes()[0]);
142 assert_eq!(0, [rgb].as_bytes()[1]);
143 assert_eq!(0x7F, [rgb].as_bytes()[2]);
144 assert_eq!(0x7F, [rgb].as_bytes()[3]);
145 assert_eq!(0xFF, [rgb].as_bytes()[4]);
146 assert_eq!(0xFF, [rgb].as_bytes()[5]);
147 }
148
149 assert_eq!("rgb(1,2,3)", format!("{}", RGB::new(1,2,3)));
150}
151
152#[test]
153fn sub_floats() {
154 assert_eq!(RGBA{r:2.5_f64, g:-1.5, b:0., a:5.}, RGBA{r:3.5_f64, g:-0.5, b:-2., a:0.} - RGBA{r:1.0_f64, g:1., b:-2., a:-5.});
155}
156
157#[test]
158fn into() {
159 let a:RGB8 = RGB{r:0,g:1,b:2};
160 let b:RGB<i16> = a.into();
161 let c:RGB<f32> = b.into();
162 let d:RGB<f32> = a.into();
163 assert_eq!(c, d);
164}
165
166#[test]
167fn rgba_works() {
168 let rgba: RGBA = RGBA{r:0u8,g:128,b:255,a:33}.clone();
169 assert_eq!(rgba.b, 255);
170 assert_eq!(rgba.a, 33);
171
172 assert_eq!(rgba, rgba.iter().map(|ch| ch).collect());
173
174 assert_eq!("rgba(1,2,3,4)", format!("{}", RGBA::new(1,2,3,4)));
175
176 assert_eq!(rgba - rgba, RGBA::new(0,0,0,0));
177}
178
179#[test]
180fn bytes() {
181 let rgb = RGB8::new(1,2,3);
182
183 #[cfg(feature = "as-bytes")]
184 {
185 let rgb_arr = [rgb];
186 let rgb_bytes = rgb_arr.as_bytes();
187 assert_eq!(&[1,2,3], rgb_bytes);
188 assert_eq!(rgb_bytes.as_rgba().len(), 0);
189 assert_eq!({let t: &[RGBA8] = rgb_bytes.as_pixels(); t}.len(), 0);
190 assert_eq!(rgb, rgb_bytes.into_iter().cloned().collect());
191 assert_eq!(&[rgb], rgb_bytes.as_rgb());
192 assert_eq!(&[rgb], rgb_bytes.as_pixels());
193 }
194 let mut rgb2 = [rgb];
195 assert_eq!(rgb2[..].as_mut_slice().as_rgb_mut(), &mut [rgb]);
196 assert_eq!(&mut [rgb], rgb2[..].as_mut_slice().as_pixels_mut());
197
198
199 #[cfg(feature = "as-bytes")]
200 {
201 let rgba = RGBA8::new(1,2,3,4);
202 let mut rgba_arr = [rgba];
203 let rgba_bytes = rgba_arr.as_bytes_mut();
204 assert_eq!(&[1,2,3,4], rgba_bytes);
205 assert_eq!(&[rgba], rgba_bytes.as_rgba());
206 rgba_bytes[3] = 99;
207 assert_eq!(RGBA8::new(1,2,3,99), rgba_arr.as_bytes().into_iter().cloned().collect());
208 }
209
210 let rgb = RGB16::new(1,2,3);
211 let rgb_slice = rgb.as_slice();
212 assert_eq!(&[1,2,3], rgb_slice);
213 assert_eq!(rgb_slice.as_rgba(), &[]);
214 assert_eq!(&[rgb], rgb_slice.as_rgb());
215 assert_eq!(rgb, rgb_slice.into_iter().cloned().collect());
216
217 let rgba = RGBA16::new(1,2,3,4);
218 let rgba_slice = rgba.as_slice();
219 assert_eq!(&[1,2,3,4], rgba_slice);
220 assert_eq!(&[1,2,3], rgba_slice.as_rgb()[0].as_slice());
221 assert_eq!(&[rgba], rgba_slice.as_rgba());
222 assert_eq!(rgba, rgba_slice.into_iter().cloned().collect());
223 let mut rgba2 = [rgba];
224 assert_eq!(rgba2[..].as_mut_slice().as_rgba_mut(), &mut [rgba]);
225
226 let mut foo = vec![0u8; 8];
227 foo.as_rgba_mut()[1] = RGBA::new(1,2,3,4);
228 assert_eq!(&[0u8,0,0,0,1,2,3,4], &foo[..]);
229}
230