1use super::*;
2
3pub unsafe trait TexturePixel {
6 const INTERNAL_FORMAT: raw::Enum;
7 const FORMAT: raw::Enum;
8 const TYPE: raw::Enum;
9}
10
11unsafe impl TexturePixel for Rgba<f32> {
12 const INTERNAL_FORMAT: raw::Enum = raw::RGBA;
13 const FORMAT: raw::Enum = raw::RGBA;
14 const TYPE: raw::Enum = raw::UNSIGNED_BYTE;
15}
16
17unsafe impl TexturePixel for u8 {
18 #[cfg(target_arch = "wasm32")]
19 const INTERNAL_FORMAT: raw::Enum = raw::ALPHA;
20 #[cfg(not(target_arch = "wasm32"))]
21 const INTERNAL_FORMAT: raw::Enum = raw::RGBA;
22 const FORMAT: raw::Enum = raw::ALPHA;
23 const TYPE: raw::Enum = raw::UNSIGNED_BYTE;
24}
25
26#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
27pub enum WrapMode {
28 Repeat = raw::REPEAT as _,
29 Clamp = raw::CLAMP_TO_EDGE as _,
30}
31
32#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
33pub enum Filter {
34 Nearest = raw::NEAREST as _,
35 Linear = raw::LINEAR as _,
36}
37
38pub struct Texture2d<P: TexturePixel> {
39 pub(crate) ugli: Ugli,
40 pub(crate) handle: raw::Texture,
41 size: Cell<vec2<usize>>,
42 phantom_data: PhantomData<*mut P>,
43}
44
45impl<P: TexturePixel> Drop for Texture2d<P> {
46 fn drop(&mut self) {
47 let gl = &self.ugli.inner.raw;
48 gl.delete_texture(&self.handle);
49 }
50}
51
52pub type Texture = Texture2d<Rgba<f32>>;
53
54impl<P: TexturePixel> Texture2d<P> {
55 fn new_raw(ugli: &Ugli, size: vec2<usize>) -> Self {
56 let gl = &ugli.inner.raw;
57 let handle = gl.create_texture().unwrap();
58 gl.bind_texture(raw::TEXTURE_2D, &handle);
59 gl.tex_parameteri(
60 raw::TEXTURE_2D,
61 raw::TEXTURE_MIN_FILTER,
62 raw::LINEAR as raw::Int,
63 );
64 let mut texture = Self {
65 ugli: ugli.clone(),
66 handle,
67 size: Cell::new(size),
68 phantom_data: PhantomData,
69 };
70 texture.set_filter(Filter::Linear);
71 texture.set_wrap_mode(WrapMode::Clamp);
72 ugli.debug_check();
73 texture
74 }
75
76 pub fn is_pot(&self) -> bool {
77 let size = self.size.get();
78 size.x & (size.x - 1) == 0 && size.y & (size.y - 1) == 0
79 }
80
81 pub fn new_uninitialized(ugli: &Ugli, size: vec2<usize>) -> Self {
82 let texture = Self::new_raw(ugli, size);
83 let gl = &ugli.inner.raw;
84 gl.tex_image_2d::<u8>(
85 raw::TEXTURE_2D,
86 0,
87 P::INTERNAL_FORMAT as raw::Int,
88 size.x as raw::SizeI,
89 size.y as raw::SizeI,
90 0,
91 P::FORMAT,
92 P::TYPE,
93 None,
94 );
95 ugli.debug_check();
96 texture
97 }
98 pub fn set_wrap_mode(&mut self, wrap_mode: WrapMode) {
99 self.set_wrap_mode_separate(wrap_mode, wrap_mode);
100 }
101
102 pub fn set_wrap_mode_separate(&mut self, wrap_mode_x: WrapMode, wrap_mode_y: WrapMode) {
103 if wrap_mode_x == WrapMode::Repeat || wrap_mode_y == WrapMode::Repeat {
104 assert!(
105 self.is_pot(),
106 "Repeat wrap mode only supported for power of two textures"
107 ); }
109 let gl = &self.ugli.inner.raw;
110 gl.bind_texture(raw::TEXTURE_2D, &self.handle);
111 gl.tex_parameteri(
112 raw::TEXTURE_2D,
113 raw::TEXTURE_WRAP_S,
114 wrap_mode_x as raw::Int,
115 );
116 gl.tex_parameteri(
117 raw::TEXTURE_2D,
118 raw::TEXTURE_WRAP_T,
119 wrap_mode_y as raw::Int,
120 );
121 self.ugli.debug_check();
122 }
123
124 pub fn set_filter(&mut self, filter: Filter) {
125 assert!(self.is_pot() || filter == Filter::Nearest || filter == Filter::Linear);
126 let gl = &self.ugli.inner.raw;
127 gl.bind_texture(raw::TEXTURE_2D, &self.handle);
128 gl.tex_parameteri(raw::TEXTURE_2D, raw::TEXTURE_MAG_FILTER, filter as raw::Int);
129 gl.tex_parameteri(raw::TEXTURE_2D, raw::TEXTURE_MIN_FILTER, filter as raw::Int);
130 self.ugli.debug_check();
131 }
132
133 pub fn size(&self) -> vec2<usize> {
134 self.size.get()
135 }
136
137 pub fn sub_image(&mut self, pos: vec2<usize>, size: vec2<usize>, data: &[u8]) {
139 assert_eq!(
140 size.x
141 * size.y
142 * match P::FORMAT {
143 raw::RGBA => 4,
144 raw::ALPHA => 1,
145 _ => unreachable!(),
146 },
147 data.len()
148 );
149 let gl = &self.ugli.inner.raw;
150 gl.pixel_store_flip_y(false);
151 gl.bind_texture(raw::TEXTURE_2D, &self.handle);
152 gl.tex_sub_image_2d(
153 raw::TEXTURE_2D,
154 0,
155 pos.x as raw::Int,
156 pos.y as raw::Int,
157 size.x as raw::SizeI,
158 size.y as raw::SizeI,
159 P::FORMAT,
160 P::TYPE,
161 data,
162 );
163 self.ugli.debug_check();
164 }
165}
166
167impl Texture {
168 pub fn gen_mipmaps(&mut self) {
169 assert!(self.is_pot());
170 let gl = &self.ugli.inner.raw;
171 gl.bind_texture(raw::TEXTURE_2D, &self.handle);
172 gl.generate_mipmap(raw::TEXTURE_2D);
173 gl.tex_parameteri(
174 raw::TEXTURE_2D,
175 raw::TEXTURE_MIN_FILTER,
176 raw::LINEAR_MIPMAP_LINEAR as raw::Int,
177 );
178 self.ugli.debug_check();
179 }
180
181 pub fn new_with<F: FnMut(vec2<usize>) -> Rgba<f32>>(
182 ugli: &Ugli,
183 size: vec2<usize>,
184 mut f: F,
185 ) -> Self {
186 let texture = Texture2d::new_raw(ugli, size);
187 let mut data: Vec<u8> = Vec::with_capacity(size.x * size.y * 4);
188 for y in 0..size.y {
189 for x in 0..size.x {
190 let color = f(vec2(x, y));
191 data.push((color.r * 255.0) as u8);
192 data.push((color.g * 255.0) as u8);
193 data.push((color.b * 255.0) as u8);
194 data.push((color.a * 255.0) as u8);
195 }
196 }
197 let gl = &ugli.inner.raw;
198 gl.pixel_store_flip_y(false);
199 gl.tex_image_2d(
200 raw::TEXTURE_2D,
201 0,
202 raw::RGBA as raw::Int,
203 size.x as raw::SizeI,
204 size.y as raw::SizeI,
205 0,
206 raw::RGBA as raw::Enum,
207 raw::UNSIGNED_BYTE,
208 Some(&data),
209 );
210 ugli.debug_check();
211 texture
212 }
213
214 pub fn from_image_image(ugli: &Ugli, mut image: image::RgbaImage) -> Self {
215 let size = vec2(image.width() as usize, image.height() as usize);
216 let mut texture = Texture2d::new_raw(ugli, size);
217 let gl = &ugli.inner.raw;
218 image::imageops::flip_vertical_in_place(&mut image);
219 gl.pixel_store_flip_y(false);
220 gl.tex_image_2d(
221 raw::TEXTURE_2D,
222 0,
223 raw::RGBA as raw::Int,
224 size.x as raw::SizeI,
225 size.y as raw::SizeI,
226 0,
227 raw::RGBA as raw::Enum,
228 raw::UNSIGNED_BYTE,
229 Some(&image.into_raw()),
230 );
231 if texture.is_pot() {
232 texture.gen_mipmaps();
233 }
234 ugli.debug_check();
235 texture
236 }
237
238 #[cfg(target_arch = "wasm32")]
239 pub fn from_html_image_element(
240 ugli: &Ugli,
241 image: &web_sys::HtmlImageElement,
242 premultiply_alpha: bool,
243 ) -> Self {
244 let mut texture =
245 Texture2d::new_raw(ugli, vec2(image.width() as usize, image.height() as usize));
246 let gl = &ugli.inner.raw;
247 gl.pixel_store_flip_y(true);
248 gl.pixel_store_premultiply_alpha(premultiply_alpha);
249 gl.tex_image_2d_image(
250 raw::TEXTURE_2D,
251 0,
252 raw::RGBA as raw::Int,
253 raw::RGBA,
254 raw::UNSIGNED_BYTE,
255 image,
256 );
257 if texture.is_pot() {
258 texture.gen_mipmaps();
259 }
260 ugli.debug_check();
261 texture
262 }
263}