sdfer/
img.rs

1// NOTE(eddyb) this is a separate module so that privacy affects sibling modules.
2// FIXME(eddyb) deduplicate with `image` crate?
3
4use std::marker::PhantomData;
5use std::ops::{Index, IndexMut};
6
7// HACK(eddyb) only exists to allow toggling precision for testing purposes.
8#[cfg(sdfer_use_f64_instead_of_f32)]
9type f32 = f64;
10
11/// `[0, 1]` represented by uniformly spaced `u8` values (`0..=255`),
12/// i.e. `Unorm8(byte)` corresponds to the `f32` value `byte as f32 / 255.0`.
13#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[repr(transparent)]
15pub struct Unorm8(u8);
16
17impl Unorm8 {
18    pub const MIN: Self = Self::from_bits(0);
19    pub const MAX: Self = Self::from_bits(u8::MAX);
20
21    #[inline(always)]
22    pub fn encode(x: f32) -> Self {
23        // NOTE(eddyb) manual `clamp` not needed, `(_: f32) as u8` will saturate:
24        // https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
25        Self((x * 255.0).round() as u8)
26    }
27
28    #[inline(always)]
29    pub fn decode(self) -> f32 {
30        self.0 as f32 / 255.0
31    }
32
33    #[inline(always)]
34    pub const fn from_bits(bits: u8) -> Self {
35        Self(bits)
36    }
37
38    #[inline(always)]
39    pub const fn to_bits(self) -> u8 {
40        self.0
41    }
42}
43
44#[derive(Default, Copy, Clone)]
45pub struct Image2d<T, Storage: AsRef<[T]> = Vec<T>> {
46    width: usize,
47    height: usize,
48    data: Storage,
49    _marker: PhantomData<T>,
50}
51
52impl<T, Storage: AsRef<[T]>> Image2d<T, Storage> {
53    pub fn new(width: usize, height: usize) -> Self
54    where
55        T: Default,
56        Storage: FromIterator<T>,
57    {
58        Self::from_fn(width, height, |_, _| T::default())
59    }
60
61    pub fn from_fn(width: usize, height: usize, mut f: impl FnMut(usize, usize) -> T) -> Self
62    where
63        Storage: FromIterator<T>,
64    {
65        Self::from_storage(
66            width,
67            height,
68            (0..height)
69                .flat_map(|y| (0..width).map(move |x| (x, y)))
70                .map(|(x, y)| f(x, y))
71                .collect(),
72        )
73    }
74
75    pub fn from_storage(width: usize, height: usize, storage: Storage) -> Self {
76        assert_eq!(storage.as_ref().len(), width * height);
77        Self {
78            width,
79            height,
80            data: storage,
81            _marker: PhantomData,
82        }
83    }
84
85    pub fn width(&self) -> usize {
86        self.width
87    }
88
89    pub fn height(&self) -> usize {
90        self.height
91    }
92
93    pub fn reborrow(&self) -> Image2d<T, &[T]> {
94        Image2d {
95            width: self.width,
96            height: self.height,
97            data: self.data.as_ref(),
98            _marker: PhantomData,
99        }
100    }
101
102    pub fn reborrow_mut(&mut self) -> Image2d<T, &mut [T]>
103    where
104        Storage: AsMut<[T]>,
105    {
106        Image2d {
107            width: self.width,
108            height: self.height,
109            data: self.data.as_mut(),
110            _marker: PhantomData,
111        }
112    }
113
114    pub fn cursor_at(&mut self, x: usize, y: usize) -> Image2dCursor<'_, T>
115    where
116        Storage: AsMut<[T]>,
117    {
118        let mut cursor = Image2dCursor {
119            image: self.reborrow_mut(),
120            xy_offset: 0,
121        };
122        cursor.reset((x, y));
123        cursor
124    }
125}
126
127impl<T, Storage: AsRef<[T]>> Index<(usize, usize)> for Image2d<T, Storage> {
128    type Output = T;
129
130    fn index(&self, (x, y): (usize, usize)) -> &T {
131        &self.data.as_ref()[y * self.width..][..self.width][x]
132    }
133}
134
135impl<T, Storage: AsMut<[T]> + AsRef<[T]>> IndexMut<(usize, usize)> for Image2d<T, Storage> {
136    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
137        &mut self.data.as_mut()[y * self.width..][..self.width][x]
138    }
139}
140
141#[cfg(feature = "image")]
142impl From<image::GrayImage> for Image2d<Unorm8> {
143    fn from(img: image::GrayImage) -> Self {
144        Self {
145            width: img.width().try_into().unwrap(),
146            height: img.height().try_into().unwrap(),
147            // HACK(eddyb) this should be a noop if the right specializations
148            // all kick in, and LLVM optimizes out the in-place transformation.
149            data: img.into_vec().into_iter().map(Unorm8::from_bits).collect(),
150            _marker: PhantomData,
151        }
152    }
153}
154
155#[cfg(feature = "image")]
156impl From<Image2d<Unorm8>> for image::GrayImage {
157    fn from(img: Image2d<Unorm8>) -> Self {
158        image::GrayImage::from_vec(
159            img.width().try_into().unwrap(),
160            img.height().try_into().unwrap(),
161            // HACK(eddyb) this should be a noop if the right specializations
162            // all kick in, and LLVM optimizes out the in-place transformation.
163            img.data.into_iter().map(Unorm8::to_bits).collect(),
164        )
165        .unwrap()
166    }
167}
168
169impl<T: Copy> Image2d<T> {
170    fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: T) {
171        self.width = width;
172        self.height = height;
173        self.data.clear();
174        self.data.resize(width * height, initial);
175    }
176}
177
178#[derive(Default)]
179pub struct Bitmap {
180    width: usize,
181    height: usize,
182    bit_8x8_blocks: Image2d<u64>,
183}
184
185pub struct BitmapEntry<'a> {
186    bit_8x8_block: &'a mut u64,
187    mask: u64,
188}
189
190impl Bitmap {
191    #[inline(always)]
192    pub fn new(width: usize, height: usize) -> Self {
193        let mut r = Self::default();
194        r.resize_and_fill_with(width, height, false);
195        r
196    }
197
198    #[inline(always)]
199    pub(crate) fn resize_and_fill_with(&mut self, width: usize, height: usize, initial: bool) {
200        self.width = width;
201        self.height = height;
202        self.bit_8x8_blocks.resize_and_fill_with(
203            width.div_ceil(8),
204            height.div_ceil(8),
205            if initial { !0 } else { 0 },
206        );
207    }
208
209    #[inline(always)]
210    pub fn width(&self) -> usize {
211        self.width
212    }
213
214    #[inline(always)]
215    pub fn height(&self) -> usize {
216        self.height
217    }
218
219    const BW: usize = 8;
220    const BH: usize = 8;
221
222    #[inline(always)]
223    const fn bit_8x8_block_xy_and_mask(x: usize, y: usize) -> ((usize, usize), u64) {
224        (
225            (x / Self::BW, y / Self::BH),
226            1 << ((y % Self::BH) * Self::BW + x % Self::BW),
227        )
228    }
229
230    #[inline(always)]
231    pub fn get(&self, x: usize, y: usize) -> bool {
232        let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
233        (self.bit_8x8_blocks[block_xy] & mask) != 0
234    }
235
236    #[inline(always)]
237    pub fn at(&mut self, x: usize, y: usize) -> BitmapEntry<'_> {
238        let (block_xy, mask) = Self::bit_8x8_block_xy_and_mask(x, y);
239        BitmapEntry {
240            bit_8x8_block: &mut self.bit_8x8_blocks[block_xy],
241            mask,
242        }
243    }
244
245    #[inline(always)]
246    pub fn cursor_at(&mut self, x: usize, y: usize) -> BitmapCursor<'_> {
247        let mut cursor = BitmapCursor {
248            bit_8x8_blocks: self.bit_8x8_blocks.cursor_at(0, 0),
249            intra_block_xy: (0, 0),
250        };
251        cursor.reset((x, y));
252        cursor
253    }
254}
255
256impl BitmapEntry<'_> {
257    #[inline(always)]
258    pub fn get(&self) -> bool {
259        (*self.bit_8x8_block & self.mask) != 0
260    }
261
262    #[inline(always)]
263    pub fn set(&mut self, value: bool) {
264        if value {
265            *self.bit_8x8_block |= self.mask;
266        } else {
267            *self.bit_8x8_block &= !self.mask;
268        }
269    }
270}
271
272// FIXME(eddyb) this doesn't really belong here, and should use GATs.
273pub trait NDCursor<'a, P> {
274    type RefMut;
275    fn reset(&'a mut self, position: P);
276    fn get_mut(&'a mut self) -> Self::RefMut;
277    fn advance(&'a mut self, delta: P);
278}
279
280pub trait NDCursorExt<P>: for<'a> NDCursor<'a, P> {
281    fn zip<C2: NDCursorExt<P>>(self, other: C2) -> NDCursorZip<Self, C2>
282    where
283        Self: Sized,
284    {
285        NDCursorZip(self, other)
286    }
287
288    // FIXME(eddyb) this is a really bad API but a whole coordinate system would be overkill.
289    fn map_abs_and_rel<P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P>(
290        self,
291        fa: FA,
292        fr: FR,
293    ) -> NDCursorMapPos<Self, FA, FR>
294    where
295        Self: Sized,
296    {
297        NDCursorMapPos(self, fa, fr)
298    }
299}
300impl<P, C: for<'a> NDCursor<'a, P>> NDCursorExt<P> for C {}
301
302pub struct NDCursorZip<C1, C2>(C1, C2);
303impl<'a, P: Copy, C1: NDCursor<'a, P>, C2: NDCursor<'a, P>> NDCursor<'a, P>
304    for NDCursorZip<C1, C2>
305{
306    type RefMut = (C1::RefMut, C2::RefMut);
307    #[inline(always)]
308    fn reset(&'a mut self, position: P) {
309        self.0.reset(position);
310        self.1.reset(position);
311    }
312    #[inline(always)]
313    fn get_mut(&'a mut self) -> Self::RefMut {
314        (self.0.get_mut(), self.1.get_mut())
315    }
316    #[inline(always)]
317    fn advance(&'a mut self, delta: P) {
318        self.0.advance(delta);
319        self.1.advance(delta);
320    }
321}
322
323pub struct NDCursorMapPos<C, FA, FR>(C, FA, FR);
324impl<'a, C: NDCursor<'a, P>, P, P2, FA: Fn(P2) -> P, FR: Fn(P2) -> P> NDCursor<'a, P2>
325    for NDCursorMapPos<C, FA, FR>
326{
327    type RefMut = C::RefMut;
328    #[inline(always)]
329    fn reset(&'a mut self, position: P2) {
330        self.0.reset((self.1)(position));
331    }
332    #[inline(always)]
333    fn get_mut(&'a mut self) -> Self::RefMut {
334        self.0.get_mut()
335    }
336    #[inline(always)]
337    fn advance(&'a mut self, delta: P2) {
338        self.0.advance((self.2)(delta));
339    }
340}
341
342pub struct Image2dCursor<'a, T> {
343    // FIXME(eddyb) find a way to use something closer to `slice::IterMut` here.
344    image: Image2d<T, &'a mut [T]>,
345    xy_offset: usize,
346}
347
348impl<'a, T: 'a> NDCursor<'a, (usize, usize)> for Image2dCursor<'_, T> {
349    type RefMut = &'a mut T;
350    #[inline(always)]
351    fn reset(&'a mut self, (x, y): (usize, usize)) {
352        self.xy_offset = y * self.image.width + x;
353    }
354    #[inline(always)]
355    fn get_mut(&'a mut self) -> Self::RefMut {
356        &mut self.image.data[self.xy_offset]
357    }
358    #[inline(always)]
359    fn advance(&'a mut self, (dx, dy): (usize, usize)) {
360        // FIXME(eddyb) check for edge conditions? (should be more like an iterator)
361        self.xy_offset += dy * self.image.width + dx;
362    }
363}
364
365pub struct BitmapCursor<'a> {
366    bit_8x8_blocks: Image2dCursor<'a, u64>,
367    // FIXME(eddyb) because of this we can't just use `bit_8x8_block_xy_and_mask`.
368    intra_block_xy: (u8, u8),
369}
370
371impl<'a> NDCursor<'a, (usize, usize)> for BitmapCursor<'_> {
372    type RefMut = BitmapEntry<'a>;
373    #[inline(always)]
374    fn reset(&'a mut self, (x, y): (usize, usize)) {
375        self.bit_8x8_blocks.reset((x / Bitmap::BW, y / Bitmap::BH));
376        self.intra_block_xy = ((x % Bitmap::BW) as u8, (y % Bitmap::BH) as u8);
377    }
378    #[inline(always)]
379    fn get_mut(&'a mut self) -> Self::RefMut {
380        let bxy = self.intra_block_xy;
381        let (_, mask) = Bitmap::bit_8x8_block_xy_and_mask(bxy.0 as usize, bxy.1 as usize);
382        BitmapEntry {
383            bit_8x8_block: self.bit_8x8_blocks.get_mut(),
384            mask,
385        }
386    }
387    #[inline(always)]
388    fn advance(&'a mut self, (dx, dy): (usize, usize)) {
389        // FIXME(eddyb) check for edge conditions? (should be more like an iterator)
390        let bxy = self.intra_block_xy;
391        let new_bxy = (bxy.0 as usize + dx, bxy.1 as usize + dy);
392
393        let whole_block_dxy = (new_bxy.0 / Bitmap::BW, new_bxy.1 / Bitmap::BH);
394        if whole_block_dxy != (0, 0) {
395            self.bit_8x8_blocks.advance(whole_block_dxy);
396        }
397
398        self.intra_block_xy = (
399            (new_bxy.0 % Bitmap::BW) as u8,
400            (new_bxy.1 % Bitmap::BH) as u8,
401        );
402    }
403}