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