safetize texture interface
authoralfadur
Thu, 12 Nov 2020 23:42:53 +0300
changeset 15782 ff1432e873bd
parent 15781 c929e25a7da2
child 15783 e7eb0cd5b0e4
safetize texture interface
rust/lib-hedgewars-engine/src/render/gear.rs
rust/lib-hedgewars-engine/src/render/gl.rs
rust/lib-hedgewars-engine/src/render/map.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);
--- 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<NonZeroU32>,
+    handle: Option<NonZeroU32>,
+    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<NonZeroU32>) -> u32 {
+    value.map_or(0, |v| v.get())
+}
+
+fn is_out_of_bounds(data: &[u8], data_stride: Option<NonZeroU32>, 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<NonZeroU32>,
         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<NonZeroU32>,
+        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 _,
                 );
             }
         }
--- 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
                 };