|
| 1 | +use crate::PixelFormat; |
| 2 | + |
1 | 3 | /// A RGBA pixel. |
2 | 4 | /// |
3 | 5 | /// # Representation |
4 | 6 | /// |
5 | | -/// This is a set of `u8`'s in the order BGRX (first component blue, second green, third red and |
6 | | -/// last unset). |
| 7 | +/// This is a set of 4 `u8`'s laid out in the order defined by [`DEFAULT_PIXEL_FORMAT`]. |
| 8 | +/// |
| 9 | +/// This type has an alignment of `4` for performance reasons, as that makes copies faster on many |
| 10 | +/// platforms, and makes this type have the same in-memory representation as `u32`. |
7 | 11 | /// |
8 | | -/// If you're familiar with [the `rgb` crate](https://docs.rs/rgb/), you can treat this mostly as-if |
9 | | -/// it is `rgb::Bgra<u8>`, except that this type has an alignment of `4` for performance reasons. |
| 12 | +/// [`DEFAULT_PIXEL_FORMAT`]: crate::DEFAULT_PIXEL_FORMAT |
10 | 13 | /// |
11 | 14 | /// # Example |
12 | 15 | /// |
13 | 16 | /// Construct a new pixel. |
14 | 17 | /// |
15 | 18 | /// ``` |
16 | | -/// # use softbuffer::Pixel; |
17 | | -/// # |
| 19 | +/// use softbuffer::Pixel; |
| 20 | +/// |
18 | 21 | /// let red = Pixel::new_rgb(0xff, 0x80, 0); |
19 | 22 | /// assert_eq!(red.r, 255); |
20 | 23 | /// assert_eq!(red.g, 128); |
|
28 | 31 | /// Convert a pixel to an array of `u8`s. |
29 | 32 | /// |
30 | 33 | /// ``` |
31 | | -/// # use softbuffer::Pixel; |
32 | | -/// # |
| 34 | +/// use softbuffer::{Pixel, PixelFormat, DEFAULT_PIXEL_FORMAT}; |
| 35 | +/// |
33 | 36 | /// let red = Pixel::new_rgb(0xff, 0, 0); |
34 | 37 | /// // SAFETY: `Pixel` can be reinterpreted as `[u8; 4]`. |
35 | 38 | /// let red = unsafe { core::mem::transmute::<Pixel, [u8; 4]>(red) }; |
36 | 39 | /// |
37 | | -/// // BGRX |
38 | | -/// assert_eq!(red[2], 255); |
| 40 | +/// match DEFAULT_PIXEL_FORMAT { |
| 41 | +/// PixelFormat::Rgbx => assert_eq!(red[0], 255), |
| 42 | +/// PixelFormat::Bgrx => assert_eq!(red[2], 255), |
| 43 | +/// } |
39 | 44 | /// ``` |
40 | 45 | /// |
41 | 46 | /// Convert a pixel to an `u32`. |
42 | 47 | /// |
43 | 48 | /// ``` |
44 | | -/// # use softbuffer::Pixel; |
45 | | -/// # |
| 49 | +/// use softbuffer::{Pixel, PixelFormat, DEFAULT_PIXEL_FORMAT}; |
| 50 | +/// |
46 | 51 | /// let red = Pixel::new_rgb(0xff, 0, 0); |
47 | 52 | /// // SAFETY: `Pixel` can be reinterpreted as `u32`. |
48 | 53 | /// let red = unsafe { core::mem::transmute::<Pixel, u32>(red) }; |
49 | 54 | /// |
50 | | -/// // BGRX |
51 | | -/// assert_eq!(red, u32::from_le_bytes([0x00, 0x00, 0xff, 0x00])); |
| 55 | +/// match DEFAULT_PIXEL_FORMAT { |
| 56 | +/// PixelFormat::Rgbx => assert_eq!(red, u32::from_le_bytes([0xff, 0x00, 0x00, 0x00])), |
| 57 | +/// PixelFormat::Bgrx => assert_eq!(red, u32::from_le_bytes([0x00, 0x00, 0xff, 0x00])), |
| 58 | +/// } |
| 59 | +/// ``` |
| 60 | +/// |
| 61 | +/// Convert a slice of pixels to a slice of `u32`s. This might be useful for library authors that |
| 62 | +/// want to keep rendering using BGRX. |
| 63 | +/// |
| 64 | +/// ``` |
| 65 | +/// // Assume the user controls the following rendering function: |
| 66 | +/// fn render(pixels: &mut [u32]) { |
| 67 | +/// pixels.fill(u32::from_le_bytes([0xff, 0xff, 0x00, 0x00])); // Yellow in BGRX |
| 68 | +/// } |
| 69 | +/// |
| 70 | +/// // Then we'd convert pixel data as follows: |
| 71 | +/// use softbuffer::{Pixel, PixelFormat, DEFAULT_PIXEL_FORMAT}; |
| 72 | +/// |
| 73 | +/// # let mut pixel_data = [Pixel::new_rgb(0, 0xff, 0xff)]; |
| 74 | +/// let pixels: &mut [Pixel]; |
| 75 | +/// # pixels = &mut pixel_data; |
| 76 | +/// |
| 77 | +/// if DEFAULT_PIXEL_FORMAT == PixelFormat::Bgrx { |
| 78 | +/// // Fast implementation when the pixel format is BGRX. |
| 79 | +/// |
| 80 | +/// // SAFETY: `Pixel` can be reinterpreted as `u32`. |
| 81 | +/// let pixels = unsafe { std::mem::transmute::<&mut [Pixel], &mut [u32]>(pixels) }; |
| 82 | +/// // CORRECTNESS: We just checked that the format is BGRX. |
| 83 | +/// render(pixels); |
| 84 | +/// } else { |
| 85 | +/// // Fall back to slower implementation when the format is RGBX. |
| 86 | +/// |
| 87 | +/// // Render into temporary buffer. |
| 88 | +/// let mut buffer = vec![0u32; pixels.len()]; |
| 89 | +/// render(&mut buffer); |
| 90 | +/// |
| 91 | +/// // And copy from temporary buffer to actual pixel data. |
| 92 | +/// for (old, new) in pixels.iter_mut().zip(buffer) { |
| 93 | +/// let new = new.to_le_bytes(); |
| 94 | +/// *old = Pixel::new_bgr(new[0], new[1], new[2]); |
| 95 | +/// } |
| 96 | +/// } |
| 97 | +/// # |
| 98 | +/// # assert_eq!(pixel_data, [Pixel::new_rgb(0, 0xff, 0xff)]); |
52 | 99 | /// ``` |
53 | 100 | #[repr(C)] |
54 | 101 | #[repr(align(4))] // Help the compiler to see that this is a u32 |
55 | 102 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] |
56 | 103 | pub struct Pixel { |
| 104 | + #[cfg_attr(docsrs, doc(auto_cfg = false))] |
| 105 | + #[cfg(any(doc, target_family = "wasm", target_os = "android"))] |
| 106 | + /// The red component. |
| 107 | + pub r: u8, |
| 108 | + #[cfg_attr(docsrs, doc(auto_cfg = false))] |
| 109 | + #[cfg(any(doc, target_family = "wasm", target_os = "android"))] |
| 110 | + /// The green component. |
| 111 | + pub g: u8, |
| 112 | + #[cfg_attr(docsrs, doc(auto_cfg = false))] |
| 113 | + #[cfg(any(doc, target_family = "wasm", target_os = "android"))] |
57 | 114 | /// The blue component. |
58 | 115 | pub b: u8, |
| 116 | + |
| 117 | + #[cfg(not(any(doc, target_family = "wasm", target_os = "android")))] |
| 118 | + /// The blue component. |
| 119 | + pub b: u8, |
| 120 | + #[cfg(not(any(doc, target_family = "wasm", target_os = "android")))] |
59 | 121 | /// The green component. |
60 | 122 | pub g: u8, |
| 123 | + #[cfg(not(any(doc, target_family = "wasm", target_os = "android")))] |
61 | 124 | /// The red component. |
62 | 125 | pub r: u8, |
63 | 126 |
|
@@ -106,3 +169,24 @@ impl Pixel { |
106 | 169 | } |
107 | 170 |
|
108 | 171 | // TODO: Implement `Add`/`Mul`/similar `std::ops` like `rgb` does? |
| 172 | + |
| 173 | +// TODO: Implement `zerocopy` / `bytemuck` traits behind a feature flag? |
| 174 | +// May not be that useful, since the representation is platform-specific. |
| 175 | + |
| 176 | +/// The pixel format that `softbuffer` uses for the current target platform. |
| 177 | +/// |
| 178 | +/// Currently, this is BGRX (first component blue, second green, third red and last unset) on all |
| 179 | +/// platforms except WebAssembly and Android, where it is RGBX, since the API on these platforms |
| 180 | +/// does not support BGRX. |
| 181 | +/// |
| 182 | +/// The format for a given platform may change in a non-breaking release if found to be more |
| 183 | +/// performant. |
| 184 | +/// |
| 185 | +/// This distinction should only be relevant if you're bitcasting `Pixel` to/from a `u32`, to e.g. |
| 186 | +/// avoid unnecessary copying, see the documentation for [`Pixel`] for examples. |
| 187 | +pub const DEFAULT_PIXEL_FORMAT: PixelFormat = |
| 188 | + if cfg!(any(doc, target_family = "wasm", target_os = "android")) { |
| 189 | + PixelFormat::Rgbx |
| 190 | + } else { |
| 191 | + PixelFormat::Bgrx |
| 192 | + }; |
0 commit comments