librashader_runtime/
image.rs

1pub use image::ImageError;
2use librashader_common::Size;
3use std::marker::PhantomData;
4
5use image::error::{LimitError, LimitErrorKind};
6use image::DynamicImage;
7use librashader_pack::{TextureBuffer, TextureResource};
8use librashader_presets::TextureMeta;
9use std::path::Path;
10
11/// An uncompressed raw image ready to upload to GPU buffers.
12pub struct Image<P: PixelFormat = RGBA8> {
13    /// The raw bytes of the image.
14    pub bytes: Vec<u8>,
15    /// The size dimensions of the image.
16    pub size: Size<u32>,
17    /// The byte pitch of the image.
18    pub pitch: usize,
19    _pd: PhantomData<P>,
20}
21
22/// R8G8B8A8 pixel format.
23///
24/// Every RGB with alpha pixel is represented with 32 bits.
25pub struct RGBA8;
26
27/// B8G8R8A8 pixel format.
28///
29/// Every BGR with alpha pixel is represented with 32 bits.
30pub struct BGRA8;
31
32/// A8R8G8B8 pixel format.
33///
34/// Every BGR with alpha pixel is represented with 32 bits.
35pub struct ARGB8;
36
37/// Represents an image pixel format to convert images into.
38pub trait PixelFormat {
39    #[doc(hidden)]
40    fn convert(pixels: &mut Vec<u8>);
41}
42
43impl PixelFormat for RGBA8 {
44    fn convert(_pixels: &mut Vec<u8>) {}
45}
46
47impl PixelFormat for BGRA8 {
48    fn convert(pixels: &mut Vec<u8>) {
49        const BGRA_SWIZZLE: &[usize; 32] = &generate_swizzle([2, 1, 0, 3]);
50        swizzle_pixels(pixels, BGRA_SWIZZLE);
51    }
52}
53
54impl PixelFormat for ARGB8 {
55    fn convert(pixels: &mut Vec<u8>) {
56        const ARGB_SWIZZLE: &[usize; 32] = &generate_swizzle([3, 0, 1, 2]);
57        swizzle_pixels(pixels, ARGB_SWIZZLE);
58    }
59}
60
61/// The direction of UV coordinates to load the image for.
62#[derive(Copy, Clone, Debug, Eq, PartialEq)]
63pub enum UVDirection {
64    /// Origin is at the top left (Direct3D, Vulkan)
65    TopLeft,
66    /// Origin is at the bottom left (OpenGL)
67    BottomLeft,
68}
69
70impl<P: PixelFormat> Image<P> {
71    /// Load the image from the path as RGBA8.
72    pub fn load(path: impl AsRef<Path>, direction: UVDirection) -> Result<Self, ImageError> {
73        let image = image::open(path.as_ref())?;
74        Ok(Self::convert(image, direction))
75    }
76
77    /// Load te image from a [`TextureBuffer`] from a [`ShaderPresetPack`](librashader_pack::ShaderPresetPack).
78    pub fn load_from_buffer(
79        buffer: TextureBuffer,
80        direction: UVDirection,
81    ) -> Result<Self, ImageError> {
82        let Some(image) = buffer.into() else {
83            return Err(ImageError::Limits(LimitError::from_kind(
84                LimitErrorKind::InsufficientMemory,
85            )));
86        };
87        let image = DynamicImage::ImageRgba8(image);
88        Ok(Self::convert(image, direction))
89    }
90
91    fn convert(mut image: DynamicImage, direction: UVDirection) -> Self {
92        if direction == UVDirection::BottomLeft {
93            image = image.flipv();
94        }
95
96        let image = if let DynamicImage::ImageRgba8(image) = image {
97            image
98        } else {
99            image.to_rgba8()
100        };
101
102        let height = image.height();
103        let width = image.width();
104        let pitch = image
105            .sample_layout()
106            .height_stride
107            .max(image.sample_layout().width_stride);
108
109        let mut bytes = image.into_raw();
110        P::convert(&mut bytes);
111        Image {
112            bytes,
113            pitch,
114            size: Size { height, width },
115            _pd: Default::default(),
116        }
117    }
118}
119
120/// Loaded texture data in the proper pixel format from a [`TextureResource`].
121pub struct LoadedTexture<P: PixelFormat = RGBA8> {
122    /// The loaded image data
123    pub image: Image<P>,
124    /// Meta information about the texture
125    pub meta: TextureMeta,
126}
127
128impl<P: PixelFormat> LoadedTexture<P> {
129    /// Load the texture with the given UV direction and subpixel ordering.
130    pub fn from_texture(
131        texture: TextureResource,
132        direction: UVDirection,
133    ) -> Result<Self, ImageError> {
134        Ok(LoadedTexture {
135            meta: texture.meta,
136            image: Image::load_from_buffer(texture.data, direction)?,
137        })
138    }
139}
140
141// load-bearing #[inline(always)], without it llvm will not vectorize.
142#[inline(always)]
143fn swizzle_pixels(pixels: &mut Vec<u8>, swizzle: &'static [usize; 32]) {
144    assert!(pixels.len() % 4 == 0);
145    let mut chunks = pixels.chunks_exact_mut(32);
146
147    // This should vectorize faster than a naive mem swap
148    for chunk in &mut chunks {
149        let tmp = swizzle.map(|i| chunk[i]);
150        chunk.copy_from_slice(&tmp[..])
151    }
152
153    let remainder = chunks.into_remainder();
154    for chunk in remainder.chunks_exact_mut(4) {
155        let argb = [
156            chunk[swizzle[0]],
157            chunk[swizzle[1]],
158            chunk[swizzle[2]],
159            chunk[swizzle[3]],
160        ];
161        chunk.copy_from_slice(&argb[..])
162    }
163}
164
165const fn generate_swizzle<const LEN: usize>(swizzle: [usize; 4]) -> [usize; LEN] {
166    assert!(LEN % 4 == 0, "length of swizzle must be divisible by 4");
167    let mut out: [usize; LEN] = [0; LEN];
168
169    let mut index = 0;
170    while index < LEN {
171        let chunk = [index, index + 1, index + 2, index + 3];
172        out[index + 0] = chunk[swizzle[0]];
173        out[index + 1] = chunk[swizzle[1]];
174        out[index + 2] = chunk[swizzle[2]];
175        out[index + 3] = chunk[swizzle[3]];
176
177        index += 4;
178    }
179
180    out
181}
182
183#[cfg(test)]
184mod test {
185    use crate::image::generate_swizzle;
186
187    #[test]
188    pub fn generate_normal_swizzle() {
189        let swizzle = generate_swizzle::<32>([0, 1, 2, 3]);
190        assert_eq!(
191            swizzle,
192            #[rustfmt::skip]
193            [
194                0, 1, 2, 3,
195                4, 5, 6, 7,
196                8, 9, 10, 11,
197                12, 13, 14, 15,
198                16, 17, 18, 19,
199                20, 21, 22, 23,
200                24, 25, 26, 27,
201                28, 29, 30, 31
202            ]
203        )
204    }
205
206    #[test]
207    pub fn generate_argb_swizzle() {
208        let swizzle = generate_swizzle::<32>([3, 0, 1, 2]);
209        assert_eq!(
210            swizzle,
211            #[rustfmt::skip]
212            [
213                3, 0, 1, 2,
214                7, 4, 5, 6,
215                11, 8, 9, 10,
216                15, 12, 13, 14,
217                19, 16, 17, 18,
218                23, 20, 21, 22,
219                27, 24, 25, 26,
220                31, 28, 29, 30
221            ]
222        )
223    }
224}