# HG changeset patch # User alfadur # Date 1605213773 -10800 # Node ID ff1432e873bd5e20ff7a9c51dafee7caf5b9064e # Parent c929e25a7da2791de8768d4e7c95df8bbc7efd58 safetize texture interface diff -r c929e25a7da2 -r ff1432e873bd rust/lib-hedgewars-engine/src/render/gear.rs --- a/rust/lib-hedgewars-engine/src/render/gear.rs Thu Nov 12 00:24:58 2020 +0300 +++ b/rust/lib-hedgewars-engine/src/render/gear.rs Thu Nov 12 23:42:53 2020 +0300 @@ -1,7 +1,7 @@ use crate::render::{ atlas::{AtlasCollection, SpriteIndex, SpriteLocation}, camera::Camera, - gl::Texture2D, + gl::{Texture2D, TextureDataType, TextureFilter, TextureFormat, TextureInternalFormat}, }; use integral_geometry::{Rect, Size}; @@ -18,7 +18,7 @@ }; #[derive(PartialEq, Debug, Clone, Copy)] -enum SpriteId { +pub enum SpriteId { Mine = 0, Grenade, @@ -54,7 +54,11 @@ pub fn new() -> Self { let mut atlas = AtlasCollection::new(ATLAS_SIZE); - let texture = Texture2D::new(ATLAS_SIZE, gl::RGBA8, gl::LINEAR); + let texture = Texture2D::new( + ATLAS_SIZE, + TextureInternalFormat::Rgba8, + TextureFilter::Linear, + ); let mut allocation = Box::new([(0, Rect::at_origin(Size::EMPTY)); MAX_SPRITES]); @@ -73,9 +77,9 @@ texture.update( rect, mapgen::theme::slice_u32_to_u8_mut(&mut pixels[..]), - 0, - gl::RGBA, - gl::UNSIGNED_BYTE, + None, + TextureFormat::Rgba, + TextureDataType::UnsignedByte, ); allocation[*sprite as usize] = (texture_index, rect); diff -r c929e25a7da2 -r ff1432e873bd rust/lib-hedgewars-engine/src/render/gl.rs --- a/rust/lib-hedgewars-engine/src/render/gl.rs Thu Nov 12 00:24:58 2020 +0300 +++ b/rust/lib-hedgewars-engine/src/render/gl.rs Thu Nov 12 23:42:53 2020 +0300 @@ -32,7 +32,8 @@ #[derive(Debug)] pub struct Texture2D { - pub handle: Option, + handle: Option, + size: Size, } impl Drop for Texture2D { @@ -53,7 +54,7 @@ NonZeroU32::new(handle) } -fn tex_params(filter: u32) { +fn tex_params(filter: TextureFilter) { unsafe { gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); @@ -62,8 +63,42 @@ } } +#[derive(Clone, Copy, Debug)] +pub enum TextureFormat { + Rgba = gl::RGBA as isize, +} + +#[derive(Clone, Copy, Debug)] +pub enum TextureInternalFormat { + Rgba8 = gl::RGBA as isize, +} + +#[derive(Clone, Copy, Debug)] +pub enum TextureDataType { + UnsignedByte = gl::UNSIGNED_BYTE as isize, +} + +#[derive(Clone, Copy, Debug)] +pub enum TextureFilter { + Nearest = gl::NEAREST as isize, + Linear = gl::LINEAR as isize, +} + +#[inline] +fn get_u32(value: Option) -> u32 { + value.map_or(0, |v| v.get()) +} + +fn is_out_of_bounds(data: &[u8], data_stride: Option, texture_size: Size) -> bool { + let data_stride = get_u32(data_stride); + data_stride == 0 && texture_size.area() * 4 > data.len() + || data_stride != 0 + && texture_size.width > data_stride as usize + && (texture_size.height * data_stride as usize) * 4 > data.len() +} + impl Texture2D { - pub fn new(size: Size, internal_format: u32, filter: u32) -> Self { + pub fn new(size: Size, internal_format: TextureInternalFormat, filter: TextureFilter) -> Self { if let Some(handle) = new_texture() { unsafe { gl::BindTexture(gl::TEXTURE_2D, handle.get()); @@ -74,8 +109,8 @@ size.width as i32, size.height as i32, 0, - gl::RGBA, - gl::UNSIGNED_BYTE, + TextureFormat::Rgba as u32, + TextureDataType::UnsignedByte as u32, std::ptr::null(), ) } @@ -83,25 +118,30 @@ tex_params(filter); Self { handle: Some(handle), + size, } } else { - Self { handle: None } + Self { handle: None, size } } } pub fn with_data( data: &[u8], - data_stride: u32, + data_stride: Option, size: Size, - internal_format: u32, - format: u32, - ty: u32, - filter: u32, + internal_format: TextureInternalFormat, + format: TextureFormat, + ty: TextureDataType, + filter: TextureFilter, ) -> Self { + if is_out_of_bounds(data, data_stride, size) { + return Self { handle: None, size }; + } + if let Some(handle) = new_texture() { unsafe { gl::BindTexture(gl::TEXTURE_2D, handle.get()); - gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32); + gl::PixelStorei(gl::UNPACK_ROW_LENGTH, get_u32(data_stride) as i32); gl::TexImage2D( gl::TEXTURE_2D, 0, @@ -110,7 +150,7 @@ size.height as i32, 0, format as u32, - ty, + ty as u32, data.as_ptr() as *const _, ) } @@ -118,42 +158,58 @@ tex_params(filter); Self { handle: Some(handle), + size, } } else { - Self { handle: None } + Self { handle: None, size } } } - pub fn update(&self, region: Rect, data: &[u8], data_stride: u32, format: u32, ty: u32) { + pub fn update( + &self, + region: Rect, + data: &[u8], + data_stride: Option, + format: TextureFormat, + ty: TextureDataType, + ) { + if is_out_of_bounds(data, data_stride, self.size) { + return; + } + if let Some(handle) = self.handle { unsafe { gl::BindTexture(gl::TEXTURE_2D, handle.get()); - gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32); + gl::PixelStorei(gl::UNPACK_ROW_LENGTH, get_u32(data_stride) as i32); gl::TexSubImage2D( gl::TEXTURE_2D, - 0, // texture level - region.left(), // texture region + 0, + region.left(), region.top(), region.width() as i32, region.height() as i32, - format, // data format - ty, // data type - data.as_ptr() as *const _, // data ptr + format as u32, + ty as u32, + data.as_ptr() as *const _, ); } } } pub fn retrieve(&self, data: &mut [u8]) { + if self.size.area() * 4 > data.len() { + return; + } + if let Some(handle) = self.handle { unsafe { gl::BindTexture(gl::TEXTURE_2D, handle.get()); gl::GetTexImage( gl::TEXTURE_2D, - 0, // texture level - gl::RGBA, // data format - gl::UNSIGNED_BYTE, // data type - data.as_mut_ptr() as *mut _, // data ptr + 0, + TextureFormat::Rgba as u32, + TextureDataType::UnsignedByte as u32, + data.as_mut_ptr() as *mut _, ); } } diff -r c929e25a7da2 -r ff1432e873bd rust/lib-hedgewars-engine/src/render/map.rs --- a/rust/lib-hedgewars-engine/src/render/map.rs Thu Nov 12 00:24:58 2020 +0300 +++ b/rust/lib-hedgewars-engine/src/render/map.rs Thu Nov 12 23:42:53 2020 +0300 @@ -6,10 +6,12 @@ camera::Camera, gl::{ Buffer, InputElement, InputFormat, InputLayout, PipelineState, Shader, Texture2D, - VariableBinding, + TextureDataType, TextureFilter, TextureFormat, TextureInternalFormat, VariableBinding, }, }; +use std::num::NonZeroU32; + // TODO: temp const VERTEX_SHADER: &'static str = r#" #version 150 @@ -164,7 +166,7 @@ let data = unsafe { &land.as_bytes()[offset..] }; let stride = land.width(); - (data, stride as u32) + (data, NonZeroU32::new(stride as u32)) }; let texture_index = if idx >= self.textures.len() { @@ -172,10 +174,10 @@ data, stride, self.tile_size, - gl::RGBA8, - gl::RGBA, - gl::UNSIGNED_BYTE, - gl::NEAREST, + TextureInternalFormat::Rgba8, + TextureFormat::Rgba, + TextureDataType::UnsignedByte, + TextureFilter::Nearest, ); let texture_index = self.textures.len(); @@ -189,8 +191,8 @@ texture_region, data, stride, - gl::RGBA, - gl::UNSIGNED_BYTE, + TextureFormat::Rgba, + TextureDataType::UnsignedByte, ); idx };