jxl_grid/
mutable_subgrid.rs

1use std::{ops::RangeBounds, ptr::NonNull};
2
3use crate::{SharedSubgrid, SimdVector};
4
5/// A mutable subgrid of the underlying buffer.
6#[derive(Debug)]
7pub struct MutableSubgrid<'g, V = f32> {
8    ptr: NonNull<V>,
9    split_base: Option<NonNull<()>>,
10    width: usize,
11    height: usize,
12    stride: usize,
13    _marker: std::marker::PhantomData<&'g mut [V]>,
14}
15
16// SAFETY: All `MutableSubgrid`s are disjoint, so they're semantically identical as `&mut [V]`.
17unsafe impl<'g, V> Send for MutableSubgrid<'g, V> where &'g mut [V]: Send {}
18unsafe impl<'g, V> Sync for MutableSubgrid<'g, V> where &'g mut [V]: Sync {}
19
20impl<'g, V> From<&'g mut crate::AlignedGrid<V>> for MutableSubgrid<'g, V> {
21    fn from(grid: &'g mut crate::AlignedGrid<V>) -> Self {
22        let width = grid.width();
23        let height = grid.height();
24        Self::from_buf(grid.buf_mut(), width, height, width)
25    }
26}
27
28impl<'g, V> MutableSubgrid<'g, V> {
29    /// Create a mutable subgrid from raw pointer to the buffer, width, height and stride.
30    ///
31    /// # Safety
32    /// The area specified by `width`, `height` and `stride` must not overlap with other instances
33    /// of other subgrids, and the memory region in the area must be valid.
34    ///
35    /// # Panics
36    /// Panics if `width > stride`.
37    pub unsafe fn new(ptr: NonNull<V>, width: usize, height: usize, stride: usize) -> Self {
38        assert!(width == 0 || width <= stride);
39        Self {
40            ptr,
41            split_base: None,
42            width,
43            height,
44            stride,
45            _marker: Default::default(),
46        }
47    }
48
49    /// Creates a empty, zero-sized subgrid that points to nowhere.
50    pub fn empty() -> Self {
51        Self {
52            ptr: NonNull::dangling(),
53            split_base: None,
54            width: 0,
55            height: 0,
56            stride: 0,
57            _marker: Default::default(),
58        }
59    }
60
61    /// Create a `MutableSubgrid` from buffer slice, width, height and stride.
62    ///
63    /// # Panic
64    /// Panics if:
65    /// - `width` is greater than `stride`,
66    /// - or the area specified by `width`, `height` and `stride` is larger than `buf`.
67    pub fn from_buf(buf: &'g mut [V], width: usize, height: usize, stride: usize) -> Self {
68        assert!(width <= stride);
69        if width == 0 || height == 0 {
70            assert_eq!(buf.len(), 0);
71        } else {
72            assert!(buf.len() >= stride * (height - 1) + width);
73        }
74        // SAFETY: We have unique access to `buf`, and the area is in bounds.
75        unsafe {
76            Self::new(
77                NonNull::new(buf.as_mut_ptr()).unwrap(),
78                width,
79                height,
80                stride,
81            )
82        }
83    }
84
85    /// Returns the width of the subgrid.
86    #[inline]
87    pub fn width(&self) -> usize {
88        self.width
89    }
90
91    /// Returns the height of the subgrid.
92    #[inline]
93    pub fn height(&self) -> usize {
94        self.height
95    }
96
97    #[inline]
98    fn get_ptr(&self, x: usize, y: usize) -> *mut V {
99        let width = self.width;
100        let height = self.height;
101        let Some(ptr) = self.try_get_ptr(x, y) else {
102            panic!("coordinate out of range: ({x}, {y}) not in {width}x{height}");
103        };
104
105        ptr
106    }
107
108    #[inline]
109    fn try_get_ptr(&self, x: usize, y: usize) -> Option<*mut V> {
110        if x >= self.width || y >= self.height {
111            return None;
112        }
113
114        // SAFETY: (x, y) is checked above and is in bounds.
115        Some(unsafe { self.get_ptr_unchecked(x, y) })
116    }
117
118    #[inline]
119    unsafe fn get_ptr_unchecked(&self, x: usize, y: usize) -> *mut V {
120        let offset = y * self.stride + x;
121        unsafe { self.ptr.as_ptr().add(offset) }
122    }
123
124    /// Returns a reference to the sample at the given location.
125    ///
126    /// # Panics
127    /// Panics if the coordinate is out of bounds.
128    #[inline]
129    pub fn get_ref(&self, x: usize, y: usize) -> &V {
130        let width = self.width;
131        let height = self.height;
132        let Some(r) = self.try_get_ref(x, y) else {
133            panic!("coordinate out of range: ({x}, {y}) not in {width}x{height}");
134        };
135
136        r
137    }
138
139    /// Returns a reference to the sample at the given location, or `None` if it is out of bounds.
140    #[inline]
141    pub fn try_get_ref(&self, x: usize, y: usize) -> Option<&V> {
142        // SAFETY: try_get_ptr returns a valid pointer.
143        self.try_get_ptr(x, y).map(|ptr| unsafe { &*ptr })
144    }
145
146    /// Returns a slice of a row of samples.
147    ///
148    /// # Panics
149    /// Panics if the row index is out of bounds.
150    #[inline]
151    pub fn get_row(&self, row: usize) -> &[V] {
152        let height = self.height;
153        let Some(slice) = self.try_get_row(row) else {
154            panic!("row index out of range: height is {height} but index is {row}");
155        };
156
157        slice
158    }
159
160    /// Returns a slice of a row of samples, or `None` if it is out of bounds.
161    #[inline]
162    pub fn try_get_row(&self, row: usize) -> Option<&[V]> {
163        if row >= self.height {
164            return None;
165        }
166
167        // SAFETY: row is in bounds, `width` consecutive elements from the start of a row is valid.
168        Some(unsafe {
169            let offset = row * self.stride;
170            let ptr = self.ptr.as_ptr().add(offset);
171            std::slice::from_raw_parts(ptr as *const _, self.width)
172        })
173    }
174
175    /// Returns a mutable reference to the sample at the given location.
176    ///
177    /// # Panics
178    /// Panics if the coordinate is out of bounds.
179    #[inline]
180    pub fn get_mut(&mut self, x: usize, y: usize) -> &mut V {
181        let width = self.width;
182        let height = self.height;
183        let Some(r) = self.try_get_mut(x, y) else {
184            panic!("coordinate out of range: ({x}, {y}) not in {width}x{height}");
185        };
186
187        r
188    }
189
190    /// Returns a mutable reference to the sample at the given location, or `None` if it is out of
191    /// bounds.
192    #[inline]
193    pub fn try_get_mut(&mut self, x: usize, y: usize) -> Option<&mut V> {
194        // SAFETY: get_ptr returns a valid pointer, and mutable borrow of `self` makes sure that
195        // the access is exclusive.
196        self.try_get_ptr(x, y).map(|ptr| unsafe { &mut *ptr })
197    }
198
199    /// Returns a mutable slice of a row of samples.
200    ///
201    /// # Panics
202    /// Panics if the row index is out of bounds.
203    #[inline]
204    pub fn get_row_mut(&mut self, row: usize) -> &mut [V] {
205        let height = self.height;
206        let Some(slice) = self.try_get_row_mut(row) else {
207            panic!("row index out of range: height is {height} but index is {row}");
208        };
209
210        slice
211    }
212
213    /// Returns a mutable slice of a row of samples, or `None` if it is out of bounds.
214    #[inline]
215    pub fn try_get_row_mut(&mut self, row: usize) -> Option<&mut [V]> {
216        if row >= self.height {
217            return None;
218        }
219
220        // SAFETY: row is in bounds, `width` consecutive elements from the start of a row is valid.
221        Some(unsafe {
222            let offset = row * self.stride;
223            let ptr = self.ptr.as_ptr().add(offset);
224            std::slice::from_raw_parts_mut(ptr, self.width)
225        })
226    }
227
228    /// Swaps two samples at the given locations.
229    ///
230    /// # Panics
231    /// Panics if either one of the locations is out of bounds.
232    #[inline]
233    pub fn swap(&mut self, (ax, ay): (usize, usize), (bx, by): (usize, usize)) {
234        let a = self.get_ptr(ax, ay);
235        let b = self.get_ptr(bx, by);
236        if std::ptr::eq(a, b) {
237            return;
238        }
239
240        // SAFETY: `a` and `b` are valid and aligned.
241        unsafe {
242            std::ptr::swap(a, b);
243        }
244    }
245
246    /// Reborrows the mutable subgrid, and returns a mutable subgrid with shorter lifetime.
247    pub fn borrow_mut(&mut self) -> MutableSubgrid<V> {
248        // SAFETY: We have unique reference to the grid, and the new grid borrows it.
249        unsafe { MutableSubgrid::new(self.ptr, self.width, self.height, self.stride) }
250    }
251
252    /// Reborrows the mutable subgrid, and returns a shared subgrid.
253    pub fn as_shared(&self) -> SharedSubgrid<V> {
254        // SAFETY: We have unique reference to the grid.
255        unsafe { SharedSubgrid::new(self.ptr, self.width, self.height, self.stride) }
256    }
257
258    /// Creates a subgrid of this subgrid.
259    ///
260    /// # Panics
261    /// Panics if the range is out of bounds.
262    pub fn subgrid(
263        self,
264        range_x: impl RangeBounds<usize>,
265        range_y: impl RangeBounds<usize>,
266    ) -> MutableSubgrid<'g, V> {
267        use std::ops::Bound;
268
269        let left = match range_x.start_bound() {
270            Bound::Included(&v) => v,
271            Bound::Excluded(&v) => v + 1,
272            Bound::Unbounded => 0,
273        };
274        let right = match range_x.end_bound() {
275            Bound::Included(&v) => v + 1,
276            Bound::Excluded(&v) => v,
277            Bound::Unbounded => self.width,
278        };
279        let top = match range_y.start_bound() {
280            Bound::Included(&v) => v,
281            Bound::Excluded(&v) => v + 1,
282            Bound::Unbounded => 0,
283        };
284        let bottom = match range_y.end_bound() {
285            Bound::Included(&v) => v + 1,
286            Bound::Excluded(&v) => v,
287            Bound::Unbounded => self.height,
288        };
289
290        // Bounds checks.
291        assert!(left <= right);
292        assert!(top <= bottom);
293        assert!(right <= self.width);
294        assert!(bottom <= self.height);
295
296        // SAFETY: subgrid region is contained in `self`.
297        unsafe {
298            let base_ptr = NonNull::new(self.get_ptr_unchecked(left, top)).unwrap();
299            MutableSubgrid::new(base_ptr, right - left, bottom - top, self.stride)
300        }
301    }
302
303    /// Split the grid horizontally at an index.
304    ///
305    /// # Panics
306    /// Panics if `x > self.width()`.
307    pub fn split_horizontal(&mut self, x: usize) -> (MutableSubgrid<'_, V>, MutableSubgrid<'_, V>) {
308        assert!(x <= self.width);
309
310        let left_ptr = self.ptr;
311        let right_ptr = NonNull::new(unsafe { self.get_ptr_unchecked(x, 0) }).unwrap();
312        // SAFETY: two grids are contained in `self` and disjoint.
313        unsafe {
314            let split_base = self.split_base.unwrap_or(self.ptr.cast());
315            let mut left_grid = MutableSubgrid::new(left_ptr, x, self.height, self.stride);
316            let mut right_grid =
317                MutableSubgrid::new(right_ptr, self.width - x, self.height, self.stride);
318            left_grid.split_base = Some(split_base);
319            right_grid.split_base = Some(split_base);
320            (left_grid, right_grid)
321        }
322    }
323
324    /// Split the grid horizontally at an index in-place.
325    ///
326    /// # Panics
327    /// Panics if `x > self.width()`.
328    pub fn split_horizontal_in_place(&mut self, x: usize) -> MutableSubgrid<'g, V> {
329        assert!(x <= self.width);
330
331        let right_width = self.width - x;
332        let right_ptr = NonNull::new(unsafe { self.get_ptr_unchecked(x, 0) }).unwrap();
333        // SAFETY: two grids are contained in `self` and disjoint.
334        unsafe {
335            let split_base = self.split_base.unwrap_or(self.ptr.cast());
336            self.width = x;
337            self.split_base = Some(split_base);
338            let mut right_grid =
339                MutableSubgrid::new(right_ptr, right_width, self.height, self.stride);
340            right_grid.split_base = Some(split_base);
341            right_grid
342        }
343    }
344
345    /// Split the grid vertically at an index.
346    ///
347    /// # Panics
348    /// Panics if `y > self.height()`.
349    pub fn split_vertical(&mut self, y: usize) -> (MutableSubgrid<'_, V>, MutableSubgrid<'_, V>) {
350        assert!(y <= self.height);
351
352        let top_ptr = self.ptr;
353        let bottom_ptr = NonNull::new(unsafe { self.get_ptr_unchecked(0, y) }).unwrap();
354        // SAFETY: two grids are contained in `self` and disjoint.
355        unsafe {
356            let split_base = self.split_base.unwrap_or(self.ptr.cast());
357            let mut top_grid = MutableSubgrid::new(top_ptr, self.width, y, self.stride);
358            let mut bottom_grid =
359                MutableSubgrid::new(bottom_ptr, self.width, self.height - y, self.stride);
360            top_grid.split_base = Some(split_base);
361            bottom_grid.split_base = Some(split_base);
362            (top_grid, bottom_grid)
363        }
364    }
365
366    /// Split the grid vertically at an index in-place.
367    ///
368    /// # Panics
369    /// Panics if `y > self.height()`.
370    pub fn split_vertical_in_place(&mut self, y: usize) -> MutableSubgrid<'g, V> {
371        assert!(y <= self.height);
372
373        let bottom_height = self.height - y;
374        let bottom_ptr = NonNull::new(unsafe { self.get_ptr_unchecked(0, y) }).unwrap();
375        // SAFETY: two grids are contained in `self` and disjoint.
376        unsafe {
377            let split_base = self.split_base.unwrap_or(self.ptr.cast());
378            self.height = y;
379            self.split_base = Some(split_base);
380            let mut bottom_grid =
381                MutableSubgrid::new(bottom_ptr, self.width, bottom_height, self.stride);
382            bottom_grid.split_base = Some(split_base);
383            bottom_grid
384        }
385    }
386
387    /// Merges the subgrid with another subgrid horizontally.
388    ///
389    /// Two subgrids must be previously split from a subgrid using [`split_horizontal`] or
390    /// [`split_horizontal_in_place`].
391    ///
392    /// # Panics
393    /// Panics if two subgrids have not been split from the same subgrid.
394    ///
395    /// [`split_horizontal`]: Self::split_horizontal
396    /// [`split_horizontal_in_place`]: Self::split_horizontal_in_place
397    pub fn merge_horizontal_in_place(&mut self, right: Self) {
398        assert!(self.split_base.is_some());
399        assert_eq!(self.split_base, right.split_base);
400        assert_eq!(self.stride, right.stride);
401        assert_eq!(self.height, right.height);
402        assert!(self.stride >= self.width + right.width);
403        unsafe {
404            assert!(std::ptr::eq(
405                self.get_ptr_unchecked(self.width, 0) as *const _,
406                right.ptr.as_ptr() as *const _,
407            ));
408        }
409
410        let right_width = right.width;
411        self.width += right_width;
412    }
413
414    /// Merges the subgrid with another subgrid vertically.
415    ///
416    /// Two subgrids must be previously split from a subgrid using [`split_vertical`] or
417    /// [`split_vertical_in_place`].
418    ///
419    /// # Panics
420    /// Panics if two subgrids have not been split from the same subgrid.
421    ///
422    /// [`split_vertical`]: Self::split_vertical
423    /// [`split_vertical_in_place`]: Self::split_vertical_in_place
424    pub fn merge_vertical_in_place(&mut self, bottom: Self) {
425        assert!(self.split_base.is_some());
426        assert_eq!(self.split_base, bottom.split_base);
427        assert_eq!(self.stride, bottom.stride);
428        assert_eq!(self.width, bottom.width);
429        unsafe {
430            assert!(std::ptr::eq(
431                self.get_ptr_unchecked(0, self.height) as *const _,
432                bottom.ptr.as_ptr() as *const _,
433            ));
434        }
435
436        let bottom_height = bottom.height;
437        self.height += bottom_height;
438    }
439}
440
441impl<V: Copy> MutableSubgrid<'_, V> {
442    /// Returns a copy of sample at the given location.
443    ///
444    /// # Panics
445    /// Panics if the coordinate is out of range.
446    #[inline]
447    pub fn get(&self, x: usize, y: usize) -> V {
448        *self.get_ref(x, y)
449    }
450}
451
452impl<'g, V> MutableSubgrid<'g, V> {
453    /// Returns a list of subgrids split into groups of given size, in row-first order.
454    ///
455    /// Groups at the edge of the grid may have smaller sizes.
456    ///
457    /// # Panics
458    /// Panics if either `group_width` or `group_height` is zero.
459    pub fn into_groups(
460        self,
461        group_width: usize,
462        group_height: usize,
463    ) -> Vec<MutableSubgrid<'g, V>> {
464        assert!(
465            group_width > 0 && group_height > 0,
466            "expected group width and height to be nonzero, got width = {group_width}, height = {group_height}"
467        );
468
469        let num_cols = self.width.div_ceil(group_width);
470        let num_rows = self.height.div_ceil(group_height);
471        self.into_groups_with_fixed_count(group_width, group_height, num_cols, num_rows)
472    }
473
474    /// Returns a list of subgrids split into groups of given size, with `num_rows` rows and
475    /// `num_cols` columns, in row-first order.
476    ///
477    /// Unlike `into_groups`, this method will always return vector of length
478    /// `num_cols * num_rows`. Remaining rows or columns will be truncated, and groups out of
479    /// bounds will be zero-sized.
480    pub fn into_groups_with_fixed_count(
481        self,
482        group_width: usize,
483        group_height: usize,
484        num_cols: usize,
485        num_rows: usize,
486    ) -> Vec<MutableSubgrid<'g, V>> {
487        let MutableSubgrid {
488            ptr,
489            split_base,
490            width,
491            height,
492            stride,
493            ..
494        } = self;
495        let split_base = split_base.unwrap_or(ptr.cast());
496
497        let mut groups = Vec::with_capacity(num_cols * num_rows);
498        for gy in 0..num_rows {
499            let y = (gy * group_height).min(height);
500            let gh = (height - y).min(group_height);
501            let row_ptr = unsafe { ptr.as_ptr().add(y * stride) };
502            for gx in 0..num_cols {
503                let x = (gx * group_width).min(width);
504                let gw = (width - x).min(group_width);
505                let ptr = unsafe { row_ptr.add(x) };
506
507                let mut grid =
508                    unsafe { MutableSubgrid::new(NonNull::new(ptr).unwrap(), gw, gh, stride) };
509                grid.split_base = Some(split_base);
510                groups.push(grid);
511            }
512        }
513
514        groups
515    }
516}
517
518impl MutableSubgrid<'_, f32> {
519    /// Converts the grid to a grid of SIMD vectors, or `None` if the grid is not aligned to the
520    /// SIMD vector type.
521    ///
522    /// # Panics
523    /// Panics if given SIMD vector type is not supported.
524    pub fn as_vectored<V: SimdVector>(&mut self) -> Option<MutableSubgrid<'_, V>> {
525        assert!(
526            V::available(),
527            "Vector type `{}` is not supported by current CPU",
528            std::any::type_name::<V>()
529        );
530
531        let mask = V::SIZE - 1;
532        let align_mask = std::mem::align_of::<V>() - 1;
533
534        (self.ptr.as_ptr() as usize & align_mask == 0
535            && self.width & mask == 0
536            && self.stride & mask == 0)
537            .then(|| MutableSubgrid {
538                ptr: self.ptr.cast::<V>(),
539                split_base: self.split_base,
540                width: self.width / V::SIZE,
541                height: self.height,
542                stride: self.stride / V::SIZE,
543                _marker: Default::default(),
544            })
545    }
546}
547
548impl<'g> MutableSubgrid<'g, f32> {
549    /// Converts this subgrid into `i32` subgrid.
550    pub fn into_i32(self) -> MutableSubgrid<'g, i32> {
551        // Safe because `f32` and `i32` has same size and align, and all bit patterns are valid.
552        MutableSubgrid {
553            ptr: self.ptr.cast(),
554            split_base: self.split_base,
555            width: self.width,
556            height: self.height,
557            stride: self.stride,
558            _marker: Default::default(),
559        }
560    }
561}