rgb/
lib.rs

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#![cfg_attr(feature = "as-bytes", doc = "```rust")]
12#![cfg_attr(not(feature = "as-bytes"), doc = "```ignore")]
13//! # use rgb::*;
14//! let pixel = RGB8 {r:0, g:100, b:255};
15//!
16//! let pixel_rgba = pixel.alpha(255);
17//! let pixel = pixel_rgba.rgb();
18//!
19//! let pixels = vec![pixel; 100];
20//! /// Can be converted to a type-less slice without copying
21//! let bytes: &[u8] = rgb::bytemuck::cast_slice(&pixels);
22//!
23//! use rgb::prelude::*; // Import pixel map trait
24//! let half_bright = pixel.map(|channel| channel / 2);
25//! let doubled = half_bright * 2;
26//! # let _ = doubled;
27//! ```
28#![doc(html_logo_url = "https://kornel.ski/rgb-logo.png")]
29#![warn(missing_docs)]
30#![cfg_attr(docsrs, feature(doc_auto_cfg))]
31#![no_std]
32
33// std is required to run unit tests
34#[cfg(test)]
35#[macro_use] extern crate std;
36/// Re-export of the [`bytemuck` crate](https://lib.rs/bytemuck). [See docs](https://docs.rs/bytemuck).
37///
38/// Use [`::bytemuck::cast_slice()`] or [`::bytemuck::from_bytes()`] to convert
39/// pixels to/from `&[u8]`.
40#[cfg(feature = "bytemuck")]
41#[doc(alias = "ComponentSlice")]
42#[doc(alias = "as_bytes")]
43#[doc(alias = "Pod")]
44pub use ::bytemuck;
45
46pub(crate) mod formats {
47    pub mod abgr;
48    pub mod argb;
49    pub mod bgr;
50    pub mod bgra;
51    pub mod gray;
52    pub mod gray_a;
53    pub mod gray_alpha;
54    pub mod grb;
55    pub mod rgb;
56    pub mod rgba;
57}
58
59/// Use as `use rgb::prelude::*`
60///
61/// The prelude will contain commonly used traits, and will be expanded in the future.
62///
63/// Using a glob import is recommended for forward compatibility with the next major version of the crate.
64pub mod prelude {
65    pub use crate::legacy::internal::pixel::ComponentMap;
66    pub use crate::legacy::internal::pixel::ColorComponentMap;
67}
68
69pub use formats::abgr::Abgr;
70pub use formats::argb::Argb;
71pub use formats::bgr::Bgr;
72pub use formats::bgra::Bgra;
73#[cfg(not(feature = "unstable-experimental"))]
74pub use formats::gray_alpha::GrayAlpha_v08 as GrayAlpha;
75#[cfg(not(feature = "unstable-experimental"))]
76pub use formats::gray::Gray_v08 as Gray;
77pub use formats::grb::Grb;
78pub use formats::rgb::Rgb;
79pub use formats::rgba::Rgba;
80
81mod inherent_impls;
82
83pub(crate) mod legacy {
84    pub(crate) mod internal {
85        pub mod convert;
86        pub mod ops;
87        pub mod pixel;
88        pub mod rgb;
89        pub mod rgba;
90    }
91    /// BGR/BGRA alernative layouts & grayscale
92    ///
93    /// BGR might be useful for some Windows or OpenGL APIs.
94    pub mod alt;
95}
96
97pub use legacy::alt;
98
99#[cfg(all(feature = "bytemuck", not(feature = "as-bytes")))]
100mod bytemuck_impl;
101#[cfg(feature = "as-bytes")]
102mod as_bytes;
103
104/// Re-export from `bytemuck` crate
105#[cfg(feature = "as-bytes")]
106pub use ::bytemuck::Pod;
107/// Re-export from `bytemuck` crate
108#[cfg(feature = "as-bytes")]
109pub use ::bytemuck::Zeroable;
110
111pub use crate::legacy::internal::convert::*;
112pub use crate::legacy::internal::pixel::*;
113
114#[doc(hidden)]
115/// Renamed to `Rgb`
116pub use formats::rgb::Rgb as RGB;
117#[doc(hidden)]
118/// Renamed to `Rgba`
119pub use formats::rgba::Rgba as RGBA;
120
121#[doc(hidden)]
122/// Incompatible replacement for the `GrayAlpha` type
123pub use formats::gray_a::GrayA;
124
125#[cfg(feature = "unstable-experimental")]
126pub use formats::gray::Gray_v09 as Gray;
127
128/// 8-bit RGB
129///
130/// The colorspace is technically undefined, but generally sRGB is assumed.
131pub type RGB8 = RGB<u8>;
132
133/// 16-bit RGB in machine's native endian
134///
135/// Be careful to perform byte-swapping when reading from files.
136pub type RGB16 = RGB<u16>;
137
138/// 8-bit RGBA, alpha is last. 0 = transparent, 255 = opaque.
139pub type RGBA8 = RGBA<u8>;
140
141/// 16-bit RGB in machine's native endian. 0 = transparent, 65535 = opaque.
142///
143/// Alpha is last.
144pub type RGBA16 = RGBA<u16>;
145
146#[test]
147fn rgb_works() {
148    let rgb = RGB{r:0u8,g:128,b:255}.clone();
149    assert_eq!(rgb.b, 255);
150
151    assert_eq!(rgb, rgb.iter().map(|ch| ch).collect());
152
153    #[cfg(feature = "as-bytes")]
154    {
155        assert_eq!(0, [rgb].as_bytes()[0]);
156        assert_eq!(128, [rgb].as_bytes()[1]);
157        assert_eq!(255, [rgb].as_bytes()[2]);
158    }
159
160    let rgb = RGB16{r:0u16,g:0x7F7F,b:65535};
161    assert_eq!(rgb.b, 65535);
162    assert_eq!(rgb.as_slice()[1], 0x7F7F);
163
164    #[cfg(feature = "as-bytes")]
165    {
166        assert_eq!(0, [rgb].as_bytes()[0]);
167        assert_eq!(0, [rgb].as_bytes()[1]);
168        assert_eq!(0x7F, [rgb].as_bytes()[2]);
169        assert_eq!(0x7F, [rgb].as_bytes()[3]);
170        assert_eq!(0xFF, [rgb].as_bytes()[4]);
171        assert_eq!(0xFF, [rgb].as_bytes()[5]);
172    }
173
174    assert_eq!("rgb(1,2,3)", format!("{}", RGB::new(1,2,3)));
175}
176
177#[test]
178fn sub_floats() {
179    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.});
180}
181
182#[test]
183fn into() {
184    let a:RGB8 = RGB{r:0,g:1,b:2};
185    let b:RGB<i16> = a.into();
186    let c:RGB<f32> = b.into();
187    let d:RGB<f32> = a.into();
188    assert_eq!(c, d);
189}
190
191#[test]
192fn rgba_works() {
193    let rgba = RGBA{r:0u8,g:128,b:255,a:33}.clone();
194    assert_eq!(rgba.b, 255);
195    assert_eq!(rgba.a, 33);
196
197    assert_eq!(rgba, rgba.iter().map(|ch| ch).collect());
198
199    assert_eq!("rgba(1,2,3,4)", format!("{}", RGBA::new(1,2,3,4)));
200
201    assert_eq!(rgba - rgba, RGBA::new(0,0,0,0));
202}
203
204#[test]
205fn bytes() {
206    let rgb = RGB8::new(1,2,3);
207
208    #[cfg(feature = "as-bytes")]
209    {
210        let rgb_arr = [rgb];
211        let rgb_bytes = rgb_arr.as_bytes();
212        assert_eq!(&[1,2,3], rgb_bytes);
213        assert_eq!(rgb_bytes.as_rgba().len(), 0);
214        assert_eq!({let t: &[RGBA8] = rgb_bytes.as_pixels(); t}.len(), 0);
215        assert_eq!(rgb, rgb_bytes.into_iter().cloned().collect());
216        assert_eq!(&[rgb], rgb_bytes.as_rgb());
217        assert_eq!(&[rgb], rgb_bytes.as_pixels());
218    }
219    let mut rgb2 = [rgb];
220    assert_eq!(rgb2[..].as_mut_slice().as_rgb_mut(), &mut [rgb]);
221    assert_eq!(&mut [rgb], rgb2[..].as_mut_slice().as_pixels_mut());
222
223
224    #[cfg(feature = "as-bytes")]
225    {
226        let rgba = RGBA8::new(1,2,3,4);
227        let mut rgba_arr = [rgba];
228        let rgba_bytes = rgba_arr.as_bytes_mut();
229        assert_eq!(&[1,2,3,4], rgba_bytes);
230        assert_eq!(&[rgba], rgba_bytes.as_rgba());
231        rgba_bytes[3] = 99;
232        assert_eq!(RGBA8::new(1,2,3,99), rgba_arr.as_bytes().into_iter().cloned().collect());
233    }
234
235    let rgb = RGB16::new(1,2,3);
236    let rgb_slice = rgb.as_slice();
237    assert_eq!(&[1,2,3], rgb_slice);
238    assert_eq!(rgb_slice.as_rgba(), &[]);
239    assert_eq!(&[rgb], rgb_slice.as_rgb());
240    assert_eq!(rgb, rgb_slice.into_iter().cloned().collect());
241
242    let rgba = RGBA16::new(1,2,3,4);
243    let rgba_slice = rgba.as_slice();
244    assert_eq!(&[1,2,3,4], rgba_slice);
245    assert_eq!(&[1,2,3], rgba_slice.as_rgb()[0].as_slice());
246    assert_eq!(&[rgba], rgba_slice.as_rgba());
247    assert_eq!(rgba, rgba_slice.iter().copied().collect());
248    let mut rgba2 = [rgba];
249    assert_eq!(rgba2[..].as_mut_slice().as_rgba_mut(), &mut [rgba]);
250
251    let mut foo = vec![0u8; 8];
252    foo.as_rgba_mut()[1] = RGBA::new(1,2,3,4);
253    assert_eq!(&[0u8,0,0,0,1,2,3,4], &foo[..]);
254}