diff options
Diffstat (limited to 'opengl-bindings')
| -rw-r--r-- | opengl-bindings/src/data_types.rs | 14 | ||||
| -rw-r--r-- | opengl-bindings/src/texture.rs | 313 |
2 files changed, 272 insertions, 55 deletions
diff --git a/opengl-bindings/src/data_types.rs b/opengl-bindings/src/data_types.rs index 7ead0ab..a6c65fc 100644 --- a/opengl-bindings/src/data_types.rs +++ b/opengl-bindings/src/data_types.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use safer_ffi::derive_ReprC; use safer_ffi::layout::ReprC; @@ -20,7 +22,7 @@ pub struct Vec3<Value: ReprC> pub z: Value, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] #[derive_ReprC] #[repr(C)] pub struct Vec2<Value> @@ -29,9 +31,17 @@ pub struct Vec2<Value> pub y: Value, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, Default)] pub struct Dimens<Value> { pub width: Value, pub height: Value, } + +impl<Value: Display> Display for Dimens<Value> +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + write!(formatter, "{}x{}", self.width, self.height) + } +} diff --git a/opengl-bindings/src/texture.rs b/opengl-bindings/src/texture.rs index e0035b6..be52135 100644 --- a/opengl-bindings/src/texture.rs +++ b/opengl-bindings/src/texture.rs @@ -1,28 +1,113 @@ -use crate::data_types::Dimens; +use std::borrow::Cow; + +use crate::data_types::{Dimens, Vec2}; use crate::MaybeCurrentContextWithFns; #[derive(Debug)] -pub struct Texture +pub struct Builder<'image> { - texture: crate::sys::types::GLuint, + image: Cow<'image, [u8]>, + pixel_data_format: PixelDataFormat, + size: Dimens<u32>, + color_space: ColorSpace, + mipmap_levels: u16, } -impl Texture +impl<'image> Builder<'image> { + pub fn image( + mut self, + image: impl Into<Cow<'image, [u8]>>, + pixel_data_format: PixelDataFormat, + ) -> Self + { + self.image = image.into(); + self.pixel_data_format = pixel_data_format; + self + } + + pub fn size(mut self, size: Dimens<u32>) -> Self + { + self.size = size; + self + } + + pub fn color_space(mut self, color_space: ColorSpace) -> Self + { + self.color_space = color_space; + self + } + + pub fn mipmap_levels(mut self, mipmap_levels: u16) -> Self + { + self.mipmap_levels = mipmap_levels; + self + } + #[must_use] - pub fn new(current_context: &MaybeCurrentContextWithFns) -> Self + pub fn create( + &self, + current_context: &MaybeCurrentContextWithFns, + ) -> Result<Texture, Error> { - let mut texture = crate::sys::types::GLuint::default(); + check_image_buffer_len_correct_for_size( + self.image.as_ref(), + self.size, + self.pixel_data_format, + )?; - unsafe { - current_context.fns().CreateTextures( - crate::sys::TEXTURE_2D, - 1, - &raw mut texture, - ); - }; + let size = try_convert_size(self.size.clone())?; - Self { texture } + let texture = Texture::new(current_context); + + texture.alloc( + current_context, + self.mipmap_levels, + self.pixel_data_format, + self.color_space, + size, + ); + + texture.sub_image( + current_context, + 0, + Vec2 { x: 0, y: 0 }, + size, + self.pixel_data_format, + self.image.as_ref(), + ); + + texture.generate_mipmap(current_context); + + Ok(texture) + } +} + +impl<'image> Default for Builder<'image> +{ + fn default() -> Self + { + Self { + image: Cow::Borrowed(&[]), + pixel_data_format: PixelDataFormat::default(), + size: Dimens::default(), + color_space: ColorSpace::default(), + mipmap_levels: 1, + } + } +} + +#[derive(Debug)] +pub struct Texture +{ + texture: crate::sys::types::GLuint, +} + +impl Texture +{ + pub fn builder<'image>() -> Builder<'image> + { + Builder::default() } pub fn bind_to_texture_unit( @@ -38,39 +123,36 @@ impl Texture } } - /// Allocates the texture storage, stores pixel data & generates a mipmap. - /// - /// # Errors - /// Returns `Err` if any value in `size` does not fit into a `i32`. - pub fn generate( + pub fn generate_mipmap(&self, current_context: &MaybeCurrentContextWithFns) + { + unsafe { + current_context.fns().GenerateTextureMipmap(self.texture); + } + } + + pub fn store_image( &self, current_context: &MaybeCurrentContextWithFns, - size: &Dimens<u32>, - data: &[u8], + mipmap_level: u16, + offset: Vec2<u32>, + size: Dimens<u32>, pixel_data_format: PixelDataFormat, - color_space: ColorSpace, - ) -> Result<(), GenerateError> + image: &[u8], + ) -> Result<(), Error> { - let size = Dimens::<crate::sys::types::GLsizei> { - width: size.width.try_into().map_err(|_| { - GenerateError::SizeWidthValueTooLarge { - value: size.width, - max_value: crate::sys::types::GLsizei::MAX as u32, - } - })?, - height: size.height.try_into().map_err(|_| { - GenerateError::SizeHeightValueTooLarge { - value: size.height, - max_value: crate::sys::types::GLsizei::MAX as u32, - } - })?, - }; + check_image_buffer_len_correct_for_size(image, size, pixel_data_format)?; - self.alloc_image(current_context, pixel_data_format, color_space, &size, data); + let offset = try_convert_offset(offset)?; + let size = try_convert_size(size)?; - unsafe { - current_context.fns().GenerateTextureMipmap(self.texture); - } + self.sub_image( + current_context, + mipmap_level, + offset, + size, + pixel_data_format, + image, + ); Ok(()) } @@ -145,34 +227,62 @@ impl Texture self.texture } - fn alloc_image( + fn new(current_context: &MaybeCurrentContextWithFns) -> Self + { + let mut texture = crate::sys::types::GLuint::default(); + + unsafe { + current_context.fns().CreateTextures( + crate::sys::TEXTURE_2D, + 1, + &raw mut texture, + ); + }; + + Self { texture } + } + + fn alloc( &self, current_context: &MaybeCurrentContextWithFns, + mipmap_levels: u16, pixel_data_format: PixelDataFormat, color_space: ColorSpace, - size: &Dimens<crate::sys::types::GLsizei>, - data: &[u8], + size: Dimens<crate::sys::types::GLsizei>, ) { unsafe { current_context.fns().TextureStorage2D( self.texture, - 1, + mipmap_levels.into(), pixel_data_format.to_sized_internal_format(color_space), size.width, size.height, ); + } + } + fn sub_image( + &self, + current_context: &MaybeCurrentContextWithFns, + mipmap_level: u16, + offset: Vec2<crate::sys::types::GLint>, + size: Dimens<crate::sys::types::GLsizei>, + pixel_data_format: PixelDataFormat, + image: &[u8], + ) + { + unsafe { current_context.fns().TextureSubImage2D( self.texture, - 0, - 0, - 0, + mipmap_level.into(), + offset.x, + offset.y, size.width, size.height, pixel_data_format.to_format(), crate::sys::UNSIGNED_BYTE, - data.as_ptr().cast(), + image.as_ptr().cast(), ); } } @@ -205,10 +315,12 @@ pub enum Filtering } /// Texture pixel data format. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum PixelDataFormat { Rgb8, + + #[default] Rgba8, } @@ -234,19 +346,27 @@ impl PixelDataFormat Self::Rgba8 => crate::sys::RGBA, } } + + fn pixel_width(&self) -> usize + { + match self { + Self::Rgb8 => 3, + Self::Rgba8 => 4, + } + } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] #[non_exhaustive] pub enum ColorSpace { + #[default] Linear, Srgb, } -/// Error generating texture. #[derive(Debug, thiserror::Error)] -pub enum GenerateError +pub enum Error { #[error("Size width value ({value}) is too large. Must be < {max_value}")] SizeWidthValueTooLarge @@ -258,4 +378,91 @@ pub enum GenerateError { value: u32, max_value: u32 }, + + #[error("Offset X value ({value}) is too large. Must be < {max_value}")] + OffsetXValueTooLarge + { + value: u32, max_value: u32 + }, + #[error("Offset Y value ({value}) is too large. Must be < {max_value}")] + OffsetYValueTooLarge + { + value: u32, max_value: u32 + }, + + #[error( + "Incorrect image buffer length for size {size}. Expected {}, found {}", + expected_buffer_len, + buffer_len + )] + IncorrectImageBufferLengthForSize + { + expected_buffer_len: usize, + buffer_len: usize, + size: Dimens<u32>, + }, +} + +fn try_convert_size( + size: Dimens<u32>, +) -> Result<Dimens<crate::sys::types::GLsizei>, Error> +{ + Ok(Dimens::<crate::sys::types::GLsizei> { + width: size + .width + .try_into() + .map_err(|_| Error::SizeWidthValueTooLarge { + value: size.width, + max_value: crate::sys::types::GLsizei::MAX as u32, + })?, + height: size + .height + .try_into() + .map_err(|_| Error::SizeHeightValueTooLarge { + value: size.height, + max_value: crate::sys::types::GLsizei::MAX as u32, + })?, + }) +} + +fn try_convert_offset(offset: Vec2<u32>) + -> Result<Vec2<crate::sys::types::GLint>, Error> +{ + Ok(Vec2::<crate::sys::types::GLint> { + x: offset + .x + .try_into() + .map_err(|_| Error::OffsetXValueTooLarge { + value: offset.x, + max_value: crate::sys::types::GLint::MAX as u32, + })?, + y: offset + .y + .try_into() + .map_err(|_| Error::OffsetYValueTooLarge { + value: offset.y, + max_value: crate::sys::types::GLint::MAX as u32, + })?, + }) +} + +fn check_image_buffer_len_correct_for_size( + image: &[u8], + size: Dimens<u32>, + pixel_data_format: PixelDataFormat, +) -> Result<(), Error> +{ + let pixel_width = pixel_data_format.pixel_width(); + + let expected_buffer_len = size.width as usize * size.height as usize * pixel_width; + + if expected_buffer_len != image.len() { + return Err(Error::IncorrectImageBufferLengthForSize { + expected_buffer_len, + buffer_len: image.len(), + size, + }); + } + + Ok(()) } |
