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