|
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 4 `u8`'s laid out in the order defined by [`PixelFormat::default()`]. |
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. |
| 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`. |
| 9 | +/// |
| 10 | +/// [`PixelFormat::default()`]: crate::PixelFormat#default |
10 | 11 | /// |
11 | 12 | /// # Example |
12 | 13 | /// |
13 | 14 | /// Construct a new pixel. |
14 | 15 | /// |
15 | 16 | /// ``` |
16 | | -/// # use softbuffer::Pixel; |
17 | | -/// # |
| 17 | +/// use softbuffer::Pixel; |
| 18 | +/// |
18 | 19 | /// let red = Pixel::new_rgb(0xff, 0x80, 0); |
19 | 20 | /// assert_eq!(red.r, 255); |
20 | 21 | /// assert_eq!(red.g, 128); |
|
28 | 29 | /// Convert a pixel to an array of `u8`s. |
29 | 30 | /// |
30 | 31 | /// ``` |
31 | | -/// # use softbuffer::Pixel; |
32 | | -/// # |
| 32 | +/// use softbuffer::{Pixel, PixelFormat}; |
| 33 | +/// |
33 | 34 | /// let red = Pixel::new_rgb(0xff, 0, 0); |
34 | 35 | /// // SAFETY: `Pixel` can be reinterpreted as `[u8; 4]`. |
35 | 36 | /// let red = unsafe { core::mem::transmute::<Pixel, [u8; 4]>(red) }; |
36 | 37 | /// |
37 | | -/// // BGRX |
38 | | -/// assert_eq!(red[2], 255); |
| 38 | +/// match PixelFormat::default() { |
| 39 | +/// PixelFormat::Bgrx => assert_eq!(red[2], 255), |
| 40 | +/// PixelFormat::Rgbx => assert_eq!(red[0], 255), |
| 41 | +/// } |
39 | 42 | /// ``` |
40 | 43 | /// |
41 | 44 | /// Convert a pixel to an `u32`. |
42 | 45 | /// |
43 | 46 | /// ``` |
44 | | -/// # use softbuffer::Pixel; |
45 | | -/// # |
| 47 | +/// use softbuffer::{Pixel, PixelFormat}; |
| 48 | +/// |
46 | 49 | /// let red = Pixel::new_rgb(0xff, 0, 0); |
47 | 50 | /// // SAFETY: `Pixel` can be reinterpreted as `u32`. |
48 | 51 | /// let red = unsafe { core::mem::transmute::<Pixel, u32>(red) }; |
49 | 52 | /// |
50 | | -/// // BGRX |
51 | | -/// assert_eq!(red, u32::from_le_bytes([0x00, 0x00, 0xff, 0x00])); |
| 53 | +/// match PixelFormat::default() { |
| 54 | +/// PixelFormat::Bgrx => assert_eq!(red, u32::from_ne_bytes([0x00, 0x00, 0xff, 0x00])), |
| 55 | +/// PixelFormat::Rgbx => assert_eq!(red, u32::from_ne_bytes([0xff, 0x00, 0x00, 0x00])), |
| 56 | +/// } |
| 57 | +/// ``` |
| 58 | +/// |
| 59 | +/// Render to a slice of `[u8; 4]`s. This might be useful for library authors that want to provide a |
| 60 | +/// simple API that provides RGBX rendering. |
| 61 | +/// |
| 62 | +/// ```no_run |
| 63 | +/// use softbuffer::{Pixel, PixelFormat}; |
| 64 | +/// |
| 65 | +/// // Assume the user controls the following rendering function: |
| 66 | +/// fn render(pixels: &mut [[u8; 4]], width: u32, height: u32) { |
| 67 | +/// pixels.fill([0xff, 0xff, 0x00, 0x00]); // Yellow in RGBX |
| 68 | +/// } |
| 69 | +/// |
| 70 | +/// // Then we'd convert pixel data as follows: |
| 71 | +/// |
| 72 | +/// # let buffer: softbuffer::Buffer<'_> = todo!(); |
| 73 | +/// # #[cfg(false)] |
| 74 | +/// let buffer = surface.buffer_mut(); |
| 75 | +/// |
| 76 | +/// let width = buffer.width().get(); |
| 77 | +/// let height = buffer.height().get(); |
| 78 | +/// |
| 79 | +/// // Use fast, zero-copy implementation when possible, and fall back to slower version when not. |
| 80 | +/// if PixelFormat::Rgbx.is_default() { |
| 81 | +/// // SAFETY: `Pixel` can be reinterpreted as `[u8; 4]`. |
| 82 | +/// let pixels = unsafe { std::mem::transmute::<&mut [Pixel], &mut [[u8; 4]]>(buffer.pixels()) }; |
| 83 | +/// // CORRECTNESS: We just checked that the format is RGBX. |
| 84 | +/// render(pixels, width, height); |
| 85 | +/// } else { |
| 86 | +/// // Render into temporary buffer. |
| 87 | +/// let mut temporary = vec![[0; 4]; width as usize * height as usize]; |
| 88 | +/// render(&mut temporary, width, height); |
| 89 | +/// |
| 90 | +/// // And copy from temporary buffer to actual pixel data. |
| 91 | +/// for (tmp, actual) in temporary.iter_mut().zip(buffer.pixels()) { |
| 92 | +/// *actual = Pixel::new_rgb(tmp[0], tmp[1], tmp[2]); |
| 93 | +/// } |
| 94 | +/// } |
| 95 | +/// |
| 96 | +/// buffer.present(); |
52 | 97 | /// ``` |
53 | 98 | #[repr(C)] |
54 | 99 | #[repr(align(4))] // Help the compiler to see that this is a u32 |
55 | 100 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] |
56 | 101 | pub struct Pixel { |
| 102 | + #[cfg_attr(docsrs, doc(auto_cfg = false))] |
| 103 | + #[cfg(any(doc, target_family = "wasm", target_os = "android"))] |
| 104 | + /// The red component. |
| 105 | + pub r: u8, |
| 106 | + #[cfg_attr(docsrs, doc(auto_cfg = false))] |
| 107 | + #[cfg(any(doc, target_family = "wasm", target_os = "android"))] |
| 108 | + /// The green component. |
| 109 | + pub g: u8, |
| 110 | + #[cfg_attr(docsrs, doc(auto_cfg = false))] |
| 111 | + #[cfg(any(doc, target_family = "wasm", target_os = "android"))] |
| 112 | + /// The blue component. |
| 113 | + pub b: u8, |
| 114 | + |
| 115 | + #[cfg(not(any(doc, target_family = "wasm", target_os = "android")))] |
57 | 116 | /// The blue component. |
58 | 117 | pub b: u8, |
| 118 | + #[cfg(not(any(doc, target_family = "wasm", target_os = "android")))] |
59 | 119 | /// The green component. |
60 | 120 | pub g: u8, |
| 121 | + #[cfg(not(any(doc, target_family = "wasm", target_os = "android")))] |
61 | 122 | /// The red component. |
62 | 123 | pub r: u8, |
63 | 124 |
|
@@ -106,3 +167,6 @@ impl Pixel { |
106 | 167 | } |
107 | 168 |
|
108 | 169 | // TODO: Implement `Add`/`Mul`/similar `std::ops` like `rgb` does? |
| 170 | + |
| 171 | +// TODO: Implement `zerocopy` / `bytemuck` traits behind a feature flag? |
| 172 | +// May not be that useful, since the representation is platform-specific. |
0 commit comments