| 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 | |