1use crate::math::vec::{Vec2, Vector};
4use crate::util::buf::{AsSlice2, Buf2, Slice2};
5
6#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
8pub struct Tex;
9
10pub type TexCoord = Vec2<Tex>;
15
16impl TexCoord {
17 pub const fn u(&self) -> f32 {
19 self.0[0]
20 }
21 pub const fn v(&self) -> f32 {
23 self.0[1]
24 }
25}
26
27#[inline]
29pub const fn uv(u: f32, v: f32) -> TexCoord {
30 Vector::new([u, v])
31}
32
33#[derive(Copy, Clone)]
46pub struct Texture<D> {
47 w: f32,
48 h: f32,
49 data: D,
50}
51
52impl<D> Texture<D> {
53 #[inline]
55 pub fn width(&self) -> f32 {
56 self.w
57 }
58 #[inline]
60 pub fn height(&self) -> f32 {
61 self.h
62 }
63}
64
65impl<C> From<Buf2<C>> for Texture<Buf2<C>> {
66 fn from(data: Buf2<C>) -> Self {
68 Self {
69 w: data.width() as f32,
70 h: data.height() as f32,
71 data,
72 }
73 }
74}
75
76impl<'a, C> From<Slice2<'a, C>> for Texture<Slice2<'a, C>> {
77 fn from(data: Slice2<'a, C>) -> Self {
79 Self {
80 w: data.width() as f32,
81 h: data.height() as f32,
82 data,
83 }
84 }
85}
86
87#[derive(Copy, Clone, Debug)]
91pub struct SamplerRepeatPot {
92 w_mask: u32,
93 h_mask: u32,
94}
95
96impl SamplerRepeatPot {
97 pub fn new<C>(tex: &Texture<impl AsSlice2<C>>) -> Self {
101 let w = tex.width() as u32;
102 let h = tex.height() as u32;
103 assert!(w.is_power_of_two(), "width must be 2^n, was {w}");
104 assert!(h.is_power_of_two(), "height must be 2^n, was {h}");
105 Self { w_mask: w - 1, h_mask: h - 1 }
106 }
107
108 pub fn sample<C: Copy>(
113 &self,
114 tex: &Texture<impl AsSlice2<C>>,
115 tc: TexCoord,
116 ) -> C {
117 let scaled_uv = uv(tex.width() * tc.u(), tex.height() * tc.v());
118 self.sample_abs(tex, scaled_uv)
119 }
120
121 pub fn sample_abs<C: Copy>(
127 &self,
128 tex: &Texture<impl AsSlice2<C>>,
129 tc: TexCoord,
130 ) -> C {
131 use crate::math::float::f32;
132 let u = f32::floor(tc.u()) as i32 as u32 & self.w_mask;
134 let v = f32::floor(tc.v()) as i32 as u32 & self.h_mask;
135
136 tex.data.as_slice2()[[u, v]]
137 }
138}
139
140#[derive(Copy, Clone, Debug)]
143pub struct SamplerClamp;
144
145#[cfg(feature = "fp")]
146impl SamplerClamp {
147 pub fn sample<C: Copy>(
152 &self,
153 tex: &Texture<impl AsSlice2<C>>,
154 tc: TexCoord,
155 ) -> C {
156 self.sample_abs(tex, uv(tc.u() * tex.w, tc.v() * tex.h))
157 }
158
159 pub fn sample_abs<C: Copy>(
165 &self,
166 tex: &Texture<impl AsSlice2<C>>,
167 tc: TexCoord,
168 ) -> C {
169 use crate::math::float::f32;
170 let u = f32::floor(tc.u().clamp(0.0, tex.w - 1.0)) as u32;
171 let v = f32::floor(tc.v().clamp(0.0, tex.h - 1.0)) as u32;
172 tex.data.as_slice2()[[u, v]]
173 }
174}
175
176#[derive(Copy, Clone, Debug)]
183pub struct SamplerOnce;
184
185impl SamplerOnce {
186 pub fn sample<C: Copy>(
195 &self,
196 tex: &Texture<impl AsSlice2<C>>,
197 tc: TexCoord,
198 ) -> C {
199 let scaled_uv = uv(tex.width() * tc.u(), tex.height() * tc.v());
200 self.sample_abs(tex, scaled_uv)
201 }
202 pub fn sample_abs<C: Copy>(
212 &self,
213 tex: &Texture<impl AsSlice2<C>>,
214 tc: TexCoord,
215 ) -> C {
216 let u = tc.u() as u32;
217 let v = tc.v() as u32;
218
219 let d = tex.data.as_slice2();
220 debug_assert!(u < d.width(), "u={u}");
221 debug_assert!(v < d.height(), "v={v}");
222
223 d[[u, v]]
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use alloc::vec;
230
231 use crate::math::color::{rgb, Color3};
232 use crate::util::buf::Buf2;
233
234 use super::*;
235
236 #[rustfmt::skip]
237 fn tex() -> Texture<Buf2<Color3>> {
238 Texture::from(Buf2::new_from(
239 (2, 2), vec![
240 rgb(0xFF, 0, 0),
241 rgb(0, 0xFF, 0),
242 rgb(0, 0, 0xFF),
243 rgb(0xFF, 0xFF, 0),
244 ]
245 ))
246 }
247
248 #[test]
249 #[cfg(feature = "fp")]
250 fn sampler_repeat_pot() {
251 let tex = tex();
252 let s = SamplerRepeatPot::new(&tex);
253
254 assert_eq!(s.sample(&tex, uv(-0.1, 0.0)), rgb(0, 0xFF, 0));
255 assert_eq!(s.sample(&tex, uv(0.0, -0.1)), rgb(0, 0, 0xFF));
256
257 assert_eq!(s.sample(&tex, uv(1.0, 0.0)), rgb(0xFF, 0, 0));
258 assert_eq!(s.sample(&tex, uv(0.0, 1.0)), rgb(0xFF, 0, 0));
259
260 assert_eq!(s.sample(&tex, uv(4.8, 0.2)), rgb(0, 0xFF, 0));
261 assert_eq!(s.sample(&tex, uv(0.2, 4.8)), rgb(0, 0, 0xFF));
262 }
263
264 #[test]
265 #[cfg(feature = "fp")]
266 fn sampler_clamp() {
267 let tex = tex();
268 let s = SamplerClamp;
269
270 assert_eq!(s.sample(&tex, uv(-1.0, 0.0)), rgb(0xFF, 0, 0));
271 assert_eq!(s.sample(&tex, uv(0.0, -1.0)), rgb(0xFF, 0, 0));
272
273 assert_eq!(s.sample(&tex, uv(1.5, 0.0)), rgb(0, 0xFF, 0));
274 assert_eq!(s.sample(&tex, uv(0.0, 1.5)), rgb(0, 0, 0xFF));
275
276 assert_eq!(s.sample(&tex, uv(1.5, 1.5)), rgb(0xFF, 0xFF, 0));
277 }
278
279 #[test]
280 fn sampler_once() {
281 let tex = tex();
282 let s = SamplerOnce;
283
284 assert_eq!(s.sample(&tex, uv(0.0, 0.0)), rgb(0xFF, 0, 0));
285 assert_eq!(s.sample(&tex, uv(0.5, 0.0)), rgb(0, 0xFF, 0));
286 assert_eq!(s.sample(&tex, uv(0.0, 0.5)), rgb(0, 0, 0xFF));
287 assert_eq!(s.sample(&tex, uv(0.5, 0.5)), rgb(0xFF, 0xFF, 0));
288 }
289}