2022-12-02 09:11:42 +11:00
|
|
|
pub use image::ImageError;
|
2022-12-22 13:39:31 +11:00
|
|
|
use librashader_common::Size;
|
|
|
|
use std::marker::PhantomData;
|
2022-12-02 09:11:42 +11:00
|
|
|
|
2022-11-17 16:08:11 +11:00
|
|
|
use std::path::Path;
|
|
|
|
|
2023-01-14 10:23:31 +11:00
|
|
|
/// An uncompressed raw image ready to upload to GPU buffers.
|
2022-12-22 13:13:35 +11:00
|
|
|
pub struct Image<P: PixelFormat = RGBA8> {
|
2023-01-14 10:23:31 +11:00
|
|
|
/// The raw bytes of the image.
|
2022-11-17 16:08:11 +11:00
|
|
|
pub bytes: Vec<u8>,
|
2023-01-14 10:23:31 +11:00
|
|
|
/// The size dimensions of the image.
|
2022-11-26 18:38:15 +11:00
|
|
|
pub size: Size<u32>,
|
2023-01-14 10:23:31 +11:00
|
|
|
/// The byte pitch of the image.
|
2022-11-26 18:38:15 +11:00
|
|
|
pub pitch: usize,
|
2022-12-22 13:39:31 +11:00
|
|
|
_pd: PhantomData<P>,
|
2022-12-22 13:13:35 +11:00
|
|
|
}
|
|
|
|
|
2023-01-14 10:23:31 +11:00
|
|
|
/// R8G8B8A8 pixel format.
|
|
|
|
///
|
|
|
|
/// Every RGB with alpha pixel is represented with 32 bits.
|
2022-12-22 13:13:35 +11:00
|
|
|
pub struct RGBA8;
|
2023-01-14 10:23:31 +11:00
|
|
|
|
|
|
|
/// B8G8R8A8 pixel format.
|
|
|
|
///
|
|
|
|
/// Every BGR with alpha pixel is represented with 32 bits.
|
2022-12-22 13:13:35 +11:00
|
|
|
pub struct BGRA8;
|
|
|
|
|
2023-01-14 10:23:31 +11:00
|
|
|
/// Represents an image pixel format to convert images into.
|
2022-12-22 13:13:35 +11:00
|
|
|
pub trait PixelFormat {
|
|
|
|
#[doc(hidden)]
|
|
|
|
fn convert(pixels: &mut Vec<u8>);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PixelFormat for RGBA8 {
|
2022-12-22 13:39:31 +11:00
|
|
|
fn convert(_pixels: &mut Vec<u8>) {}
|
2022-12-22 13:13:35 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PixelFormat for BGRA8 {
|
|
|
|
fn convert(pixels: &mut Vec<u8>) {
|
|
|
|
for [r, _g, b, _a] in pixels.array_chunks_mut::<4>() {
|
|
|
|
std::mem::swap(b, r)
|
|
|
|
}
|
|
|
|
}
|
2022-11-17 16:08:11 +11:00
|
|
|
}
|
|
|
|
|
2022-12-02 09:11:42 +11:00
|
|
|
/// The direction of UV coordinates to load the image for.
|
2022-12-01 14:50:57 +11:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum UVDirection {
|
2022-12-02 09:11:42 +11:00
|
|
|
/// Origin is at the top left (Direct3D, Vulkan)
|
2022-12-01 14:50:57 +11:00
|
|
|
TopLeft,
|
2022-12-02 09:11:42 +11:00
|
|
|
/// Origin is at the bottom left (OpenGL)
|
2022-12-01 14:50:57 +11:00
|
|
|
BottomLeft,
|
|
|
|
}
|
2022-12-02 09:11:42 +11:00
|
|
|
|
2022-12-22 13:13:35 +11:00
|
|
|
impl<P: PixelFormat> Image<P> {
|
2022-12-02 09:11:42 +11:00
|
|
|
/// Load the image from the path as RGBA8.
|
2022-12-01 14:50:57 +11:00
|
|
|
pub fn load(path: impl AsRef<Path>, direction: UVDirection) -> Result<Self, ImageError> {
|
|
|
|
let mut image = image::open(path.as_ref())?;
|
|
|
|
|
2022-12-02 09:11:42 +11:00
|
|
|
if direction == UVDirection::BottomLeft {
|
2022-12-01 14:50:57 +11:00
|
|
|
image = image.flipv();
|
|
|
|
}
|
|
|
|
|
|
|
|
let image = image.to_rgba8();
|
2022-11-21 18:13:22 +11:00
|
|
|
|
2022-11-17 16:08:11 +11:00
|
|
|
let height = image.height();
|
|
|
|
let width = image.width();
|
2022-11-30 17:38:05 +11:00
|
|
|
let pitch = image
|
|
|
|
.sample_layout()
|
|
|
|
.height_stride
|
|
|
|
.max(image.sample_layout().width_stride);
|
2022-11-22 08:21:50 +11:00
|
|
|
|
2022-12-22 13:13:35 +11:00
|
|
|
let mut bytes = image.into_raw();
|
|
|
|
P::convert(&mut bytes);
|
2022-11-17 16:08:11 +11:00
|
|
|
Ok(Image {
|
2022-12-22 13:13:35 +11:00
|
|
|
bytes,
|
2022-11-26 18:38:15 +11:00
|
|
|
pitch,
|
2022-11-30 17:38:05 +11:00
|
|
|
size: Size { height, width },
|
2022-12-22 13:13:35 +11:00
|
|
|
_pd: Default::default(),
|
2022-11-17 16:08:11 +11:00
|
|
|
})
|
|
|
|
}
|
2022-11-22 08:21:50 +11:00
|
|
|
}
|