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
11pub struct Image<P: PixelFormat = RGBA8> {
13 pub bytes: Vec<u8>,
15 pub size: Size<u32>,
17 pub pitch: usize,
19 _pd: PhantomData<P>,
20}
21
22pub struct RGBA8;
26
27pub struct BGRA8;
31
32pub struct ARGB8;
36
37pub 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
63pub enum UVDirection {
64 TopLeft,
66 BottomLeft,
68}
69
70impl<P: PixelFormat> Image<P> {
71 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 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
120pub struct LoadedTexture<P: PixelFormat = RGBA8> {
122 pub image: Image<P>,
124 pub meta: TextureMeta,
126}
127
128impl<P: PixelFormat> LoadedTexture<P> {
129 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#[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 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}