use crate::err::Error; pub use image::{ColorType, ImageFormat}; use image::{DynamicImage, GenericImageView}; use nalgebra_glm::Vec4; use std::io::{BufRead, Seek}; use std::os::raw::c_void; #[repr(u32)] #[derive(Copy, Clone, Debug)] pub enum WrapStyle { Repeat = gl::REPEAT, MirroredRepeat = gl::MIRRORED_REPEAT, ClampToEdge = gl::CLAMP_TO_EDGE, ClampToBorder = gl::CLAMP_TO_BORDER, } #[repr(u32)] #[derive(Copy, Clone, Debug)] pub enum FilterStyle { Nearest = gl::NEAREST, Linear = gl::LINEAR, } impl FilterStyle { fn get_gl_enum(filter: FilterStyle, mipmap: Option) -> i32 { let gl_enum = match (filter, mipmap) { (FilterStyle::Nearest, None) => gl::NEAREST, (FilterStyle::Linear, None) => gl::LINEAR, (FilterStyle::Linear, Some(FilterStyle::Linear)) => gl::LINEAR_MIPMAP_LINEAR, (FilterStyle::Linear, Some(FilterStyle::Nearest)) => gl::LINEAR_MIPMAP_NEAREST, (FilterStyle::Nearest, Some(FilterStyle::Linear)) => gl::NEAREST_MIPMAP_LINEAR, (FilterStyle::Nearest, Some(FilterStyle::Nearest)) => gl::NEAREST_MIPMAP_NEAREST, }; gl_enum as i32 } } #[derive(Clone, Debug)] pub struct Texture { image_id: u32, options: TextureOptions, } pub struct Image { pub image: DynamicImage, } impl Image { pub fn load(r: R, format: ImageFormat) -> Result { Ok(Image { image: image::load(r, format)?, }) } pub fn into_texture(mut self, options: TextureOptions) -> Result { let mut image_id = 0; unsafe { gl::GenTextures(1, &mut image_id); gl::BindTexture(gl::TEXTURE_2D, image_id); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, options.wrap_s as i32); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, options.wrap_t as i32); gl::TexParameteri( gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, FilterStyle::get_gl_enum(options.mag_filter, options.mag_mipmap_filter), ); gl::TexParameteri( gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, FilterStyle::get_gl_enum(options.min_filter, options.min_mipmap_filter), ); if let Some((_, ref border)) = options.border { gl::TexParameterfv(gl::TEXTURE_2D, gl::TEXTURE_BORDER_COLOR, border.as_ptr()) } if options.flip_vertically { self.image = self.image.flipv() } let pixels = self .image .pixels() .into_iter() .flat_map(|x| vec![x.2[0], x.2[1], x.2[2], x.2[3]]) .collect::>(); let width = self.image.width(); let height = self.image.height(); gl::TexImage2D( gl::TEXTURE_2D, 0, gl::RGBA as i32, width as i32, height as i32, 0, gl::RGBA, gl::UNSIGNED_BYTE, pixels.as_ptr() as *const c_void, ); gl::GenerateMipmap(gl::TEXTURE_2D); } Ok(Texture { image_id, options }) } } #[derive(Clone, Debug)] pub struct TextureOptions { wrap_s: WrapStyle, wrap_t: WrapStyle, border: Option<(i32, Vec4)>, mag_filter: FilterStyle, min_filter: FilterStyle, min_mipmap_filter: Option, mag_mipmap_filter: Option, flip_vertically: bool, } impl Default for TextureOptions { fn default() -> Self { TextureOptions { wrap_s: WrapStyle::Repeat, wrap_t: WrapStyle::Repeat, border: None, mag_filter: FilterStyle::Linear, min_filter: FilterStyle::Linear, min_mipmap_filter: None, mag_mipmap_filter: None, flip_vertically: true, } } } #[repr(u32)] #[derive(Debug, Copy, Clone)] pub enum TextureSlot { Slot0 = gl::TEXTURE0, Slot1 = gl::TEXTURE1, Slot2 = gl::TEXTURE2, Slot3 = gl::TEXTURE3, Slot4 = gl::TEXTURE4, Slot5 = gl::TEXTURE5, Slot6 = gl::TEXTURE6, Slot7 = gl::TEXTURE7, Slot8 = gl::TEXTURE8, Slot9 = gl::TEXTURE9, Slot10 = gl::TEXTURE10, Slot11 = gl::TEXTURE11, Slot12 = gl::TEXTURE12, Slot13 = gl::TEXTURE13, Slot14 = gl::TEXTURE14, Slot15 = gl::TEXTURE15, Slot16 = gl::TEXTURE16, Slot17 = gl::TEXTURE17, Slot18 = gl::TEXTURE18, Slot19 = gl::TEXTURE19, Slot20 = gl::TEXTURE20, Slot21 = gl::TEXTURE21, Slot22 = gl::TEXTURE22, Slot23 = gl::TEXTURE23, Slot24 = gl::TEXTURE24, Slot25 = gl::TEXTURE25, Slot26 = gl::TEXTURE26, Slot27 = gl::TEXTURE27, Slot28 = gl::TEXTURE28, Slot29 = gl::TEXTURE29, Slot30 = gl::TEXTURE30, Slot31 = gl::TEXTURE31, } impl Texture { pub fn enable(&self, slot: TextureSlot) { unsafe { gl::ActiveTexture(slot as u32); gl::BindTexture(gl::TEXTURE_2D, self.image_id); } } }