use crate::data_types::Dimens; use crate::CurrentContextWithFns; #[derive(Debug)] pub struct Texture { texture: crate::sys::types::GLuint, } impl Texture { #[must_use] pub fn new(current_context: &CurrentContextWithFns<'_>) -> Self { let mut texture = crate::sys::types::GLuint::default(); unsafe { current_context.fns().CreateTextures( crate::sys::TEXTURE_2D, 1, &raw mut texture, ); }; Self { texture } } pub fn bind_to_texture_unit( &self, current_context: &CurrentContextWithFns<'_>, texture_unit: u32, ) { unsafe { current_context .fns() .BindTextureUnit(texture_unit, self.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( &self, current_context: &CurrentContextWithFns<'_>, size: &Dimens, data: &[u8], pixel_data_format: PixelDataFormat, ) -> Result<(), GenerateError> { let size = Dimens:: { 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, } })?, }; self.alloc_image(current_context, pixel_data_format, &size, data); unsafe { current_context.fns().GenerateTextureMipmap(self.texture); } Ok(()) } pub fn set_wrap( &self, current_context: &CurrentContextWithFns<'_>, wrapping: Wrapping, ) { unsafe { current_context.fns().TextureParameteri( self.texture, crate::sys::TEXTURE_WRAP_S, wrapping as i32, ); current_context.fns().TextureParameteri( self.texture, crate::sys::TEXTURE_WRAP_T, wrapping as i32, ); } } pub fn set_magnifying_filter( &self, current_context: &CurrentContextWithFns<'_>, filtering: Filtering, ) { unsafe { current_context.fns().TextureParameteri( self.texture, crate::sys::TEXTURE_MAG_FILTER, filtering as i32, ); } } pub fn set_minifying_filter( &self, current_context: &CurrentContextWithFns<'_>, filtering: Filtering, ) { unsafe { current_context.fns().TextureParameteri( self.texture, crate::sys::TEXTURE_MIN_FILTER, filtering as i32, ); } } pub fn delete(self, current_context: &CurrentContextWithFns<'_>) { unsafe { current_context .fns() .DeleteTextures(1, &raw const self.texture); } } fn alloc_image( &self, current_context: &CurrentContextWithFns<'_>, pixel_data_format: PixelDataFormat, size: &Dimens, data: &[u8], ) { unsafe { current_context.fns().TextureStorage2D( self.texture, 1, pixel_data_format.to_sized_internal_format(), size.width, size.height, ); current_context.fns().TextureSubImage2D( self.texture, 0, 0, 0, size.width, size.height, pixel_data_format.to_format(), crate::sys::UNSIGNED_BYTE, data.as_ptr().cast(), ); } } } const fn try_cast_u32_to_i32(val: u32) -> i32 { assert!(val <= i32::MAX as u32); val.cast_signed() } /// Texture wrapping. #[derive(Debug, Clone, Copy)] #[repr(i32)] pub enum Wrapping { Repeat = const { try_cast_u32_to_i32(crate::sys::REPEAT) }, MirroredRepeat = const { try_cast_u32_to_i32(crate::sys::MIRRORED_REPEAT) }, ClampToEdge = const { try_cast_u32_to_i32(crate::sys::CLAMP_TO_EDGE) }, ClampToBorder = const { try_cast_u32_to_i32(crate::sys::CLAMP_TO_BORDER) }, } #[derive(Debug, Clone, Copy)] #[repr(i32)] pub enum Filtering { Nearest = const { try_cast_u32_to_i32(crate::sys::NEAREST) }, Linear = const { try_cast_u32_to_i32(crate::sys::LINEAR) }, } /// Texture pixel data format. #[derive(Debug, Clone, Copy)] pub enum PixelDataFormat { Rgb8, Rgba8, } impl PixelDataFormat { fn to_sized_internal_format(self) -> crate::sys::types::GLenum { match self { Self::Rgb8 => crate::sys::RGB8, Self::Rgba8 => crate::sys::RGBA8, } } fn to_format(self) -> crate::sys::types::GLenum { match self { Self::Rgb8 => crate::sys::RGB, Self::Rgba8 => crate::sys::RGBA, } } } /// Error generating texture. #[derive(Debug, thiserror::Error)] pub enum GenerateError { #[error("Size width value ({value}) is too large. Must be < {max_value}")] SizeWidthValueTooLarge { value: u32, max_value: u32 }, #[error("Size height value ({value}) is too large. Must be < {max_value}")] SizeHeightValueTooLarge { value: u32, max_value: u32 }, }