1use crate::error::{OxiGdalError, Result};
23
24#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Mask {
42 width: usize,
44 height: usize,
46 data: Vec<u64>,
49}
50
51impl Mask {
54 #[inline]
56 fn words_for(pixel_count: usize) -> usize {
57 pixel_count.saturating_add(63) / 64
58 }
59
60 #[inline]
62 fn coords(i: usize) -> (usize, u32) {
63 (i / 64, (i % 64) as u32)
64 }
65
66 fn clear_tail_bits(&mut self) {
70 let pixel_count = self.width * self.height;
71 let tail = pixel_count % 64;
72 if tail != 0 {
73 if let Some(last) = self.data.last_mut() {
74 *last &= (1u64 << tail).wrapping_sub(1);
76 }
77 }
78 }
79
80 #[inline]
82 fn check_dims(&self, other: &Mask) -> Result<()> {
83 if self.width != other.width || self.height != other.height {
84 Err(OxiGdalError::InvalidParameter {
85 parameter: "other",
86 message: format!(
87 "Mask dimension mismatch: self={}×{}, other={}×{}",
88 self.width, self.height, other.width, other.height
89 ),
90 })
91 } else {
92 Ok(())
93 }
94 }
95}
96
97impl Mask {
100 #[must_use]
104 pub fn new(width: usize, height: usize) -> Self {
105 let words = Self::words_for(width * height);
106 Self {
107 width,
108 height,
109 data: vec![0u64; words],
110 }
111 }
112
113 #[must_use]
118 pub fn new_filled(width: usize, height: usize) -> Self {
119 let mut mask = Self {
120 width,
121 height,
122 data: vec![!0u64; Self::words_for(width * height)],
123 };
124 mask.clear_tail_bits();
125 mask
126 }
127
128 pub fn from_slice(width: usize, height: usize, values: &[bool]) -> Result<Self> {
138 let pixel_count = width * height;
139 if values.len() != pixel_count {
140 return Err(OxiGdalError::InvalidParameter {
141 parameter: "values",
142 message: format!(
143 "Slice length {} does not match {}×{} = {} pixels",
144 values.len(),
145 width,
146 height,
147 pixel_count
148 ),
149 });
150 }
151 let mut mask = Self::new(width, height);
152 for (i, &v) in values.iter().enumerate() {
153 if v {
154 let (word, bit) = Self::coords(i);
155 mask.data[word] |= 1u64 << bit;
156 }
157 }
158 Ok(mask)
161 }
162}
163
164impl Mask {
167 #[must_use]
169 #[inline]
170 pub const fn width(&self) -> usize {
171 self.width
172 }
173
174 #[must_use]
176 #[inline]
177 pub const fn height(&self) -> usize {
178 self.height
179 }
180
181 #[must_use]
183 #[inline]
184 pub fn pixel_count(&self) -> usize {
185 self.width * self.height
186 }
187
188 #[must_use]
197 pub fn get(&self, x: usize, y: usize) -> bool {
198 debug_assert!(
199 x < self.width,
200 "x={} out of bounds (width={})",
201 x,
202 self.width
203 );
204 debug_assert!(
205 y < self.height,
206 "y={} out of bounds (height={})",
207 y,
208 self.height
209 );
210 let i = y * self.width + x;
211 let (word, bit) = Self::coords(i);
212 if word < self.data.len() {
213 (self.data[word] >> bit) & 1 == 1
214 } else {
215 false
216 }
217 }
218
219 pub fn set(&mut self, x: usize, y: usize, value: bool) {
227 debug_assert!(
228 x < self.width,
229 "x={} out of bounds (width={})",
230 x,
231 self.width
232 );
233 debug_assert!(
234 y < self.height,
235 "y={} out of bounds (height={})",
236 y,
237 self.height
238 );
239 let i = y * self.width + x;
240 let (word, bit) = Self::coords(i);
241 if word < self.data.len() {
242 if value {
243 self.data[word] |= 1u64 << bit;
244 } else {
245 self.data[word] &= !(1u64 << bit);
246 }
247 }
248 }
249}
250
251impl Mask {
254 pub fn fill(&mut self, value: bool) {
258 let fill_word = if value { !0u64 } else { 0u64 };
259 for w in &mut self.data {
260 *w = fill_word;
261 }
262 if value {
263 self.clear_tail_bits();
264 }
265 }
266
267 pub fn fill_rect(&mut self, x: usize, y: usize, w: usize, h: usize, value: bool) {
272 let x_end = (x + w).min(self.width);
274 let y_end = (y + h).min(self.height);
275 for row in y..y_end {
276 for col in x..x_end {
277 self.set(col, row, value);
278 }
279 }
280 }
283}
284
285impl Mask {
288 pub fn and_assign(&mut self, other: &Mask) -> Result<()> {
294 self.check_dims(other)?;
295 for (a, b) in self.data.iter_mut().zip(other.data.iter()) {
296 *a &= b;
297 }
298 Ok(())
299 }
300
301 pub fn or_assign(&mut self, other: &Mask) -> Result<()> {
307 self.check_dims(other)?;
308 for (a, b) in self.data.iter_mut().zip(other.data.iter()) {
309 *a |= b;
310 }
311 Ok(())
314 }
315
316 pub fn not_in_place(&mut self) {
320 for w in &mut self.data {
321 *w = !*w;
322 }
323 self.clear_tail_bits();
324 }
325
326 pub fn xor_assign(&mut self, other: &Mask) -> Result<()> {
332 self.check_dims(other)?;
333 for (a, b) in self.data.iter_mut().zip(other.data.iter()) {
334 *a ^= b;
335 }
336 Ok(())
338 }
339
340 pub fn and(&self, other: &Mask) -> Result<Mask> {
346 self.check_dims(other)?;
347 let data: Vec<u64> = self
348 .data
349 .iter()
350 .zip(other.data.iter())
351 .map(|(a, b)| a & b)
352 .collect();
353 Ok(Mask {
354 width: self.width,
355 height: self.height,
356 data,
357 })
358 }
359
360 pub fn or(&self, other: &Mask) -> Result<Mask> {
366 self.check_dims(other)?;
367 let data: Vec<u64> = self
368 .data
369 .iter()
370 .zip(other.data.iter())
371 .map(|(a, b)| a | b)
372 .collect();
373 Ok(Mask {
374 width: self.width,
375 height: self.height,
376 data,
377 })
378 }
379
380 #[must_use]
384 pub fn not(&self) -> Mask {
385 let mut result = Mask {
386 width: self.width,
387 height: self.height,
388 data: self.data.iter().map(|w| !w).collect(),
389 };
390 result.clear_tail_bits();
391 result
392 }
393}
394
395impl Mask {
398 #[must_use]
400 pub fn count_set(&self) -> usize {
401 self.data.iter().map(|w| w.count_ones() as usize).sum()
402 }
403
404 #[must_use]
406 pub fn count_unset(&self) -> usize {
407 self.pixel_count() - self.count_set()
408 }
409
410 #[must_use]
412 pub fn all_set(&self) -> bool {
413 self.count_set() == self.pixel_count()
414 }
415
416 #[must_use]
418 pub fn all_unset(&self) -> bool {
419 self.data.iter().all(|&w| w == 0)
420 }
421
422 #[must_use]
424 pub fn any_set(&self) -> bool {
425 self.data.iter().any(|&w| w != 0)
426 }
427}
428
429impl Mask {
432 pub fn set_positions(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
437 SetPositions {
438 mask: self,
439 word_idx: 0,
440 current_word: self.data.first().copied().unwrap_or(0),
441 pixel_base: 0,
442 }
443 }
444}
445
446struct SetPositions<'a> {
448 mask: &'a Mask,
449 word_idx: usize,
451 current_word: u64,
453 pixel_base: usize,
455}
456
457impl<'a> Iterator for SetPositions<'a> {
458 type Item = (usize, usize);
459
460 fn next(&mut self) -> Option<Self::Item> {
461 loop {
462 if self.current_word != 0 {
463 let bit = self.current_word.trailing_zeros() as usize;
465 self.current_word &= self.current_word - 1;
467 let i = self.pixel_base + bit;
468 if i < self.mask.pixel_count() {
469 let x = i % self.mask.width;
470 let y = i / self.mask.width;
471 return Some((x, y));
472 }
473 continue;
475 }
476 self.word_idx += 1;
478 if self.word_idx >= self.mask.data.len() {
479 return None;
480 }
481 self.pixel_base = self.word_idx * 64;
482 self.current_word = self.mask.data[self.word_idx];
483 }
484 }
485}
486
487impl Mask {
490 pub fn from_nodata_f32(data: &[f32], width: usize, height: usize, nodata: f32) -> Result<Self> {
500 let pixel_count = width * height;
501 if data.len() != pixel_count {
502 return Err(OxiGdalError::InvalidParameter {
503 parameter: "data",
504 message: format!(
505 "Slice length {} ≠ {}×{} = {} pixels",
506 data.len(),
507 width,
508 height,
509 pixel_count
510 ),
511 });
512 }
513 let nodata_is_nan = nodata.is_nan();
514 let mut mask = Self::new(width, height);
515 for (i, &v) in data.iter().enumerate() {
516 let is_nodata = if nodata_is_nan {
517 v.is_nan()
518 } else {
519 v == nodata
520 };
521 if is_nodata {
522 let (word, bit) = Self::coords(i);
523 mask.data[word] |= 1u64 << bit;
524 }
525 }
526 Ok(mask)
527 }
528
529 pub fn from_nodata_f64(data: &[f64], width: usize, height: usize, nodata: f64) -> Result<Self> {
539 let pixel_count = width * height;
540 if data.len() != pixel_count {
541 return Err(OxiGdalError::InvalidParameter {
542 parameter: "data",
543 message: format!(
544 "Slice length {} ≠ {}×{} = {} pixels",
545 data.len(),
546 width,
547 height,
548 pixel_count
549 ),
550 });
551 }
552 let nodata_is_nan = nodata.is_nan();
553 let mut mask = Self::new(width, height);
554 for (i, &v) in data.iter().enumerate() {
555 let is_nodata = if nodata_is_nan {
556 v.is_nan()
557 } else {
558 v == nodata
559 };
560 if is_nodata {
561 let (word, bit) = Self::coords(i);
562 mask.data[word] |= 1u64 << bit;
563 }
564 }
565 Ok(mask)
566 }
567
568 pub fn from_nodata_i32(data: &[i32], width: usize, height: usize, nodata: i32) -> Result<Self> {
577 let pixel_count = width * height;
578 if data.len() != pixel_count {
579 return Err(OxiGdalError::InvalidParameter {
580 parameter: "data",
581 message: format!(
582 "Slice length {} ≠ {}×{} = {} pixels",
583 data.len(),
584 width,
585 height,
586 pixel_count
587 ),
588 });
589 }
590 let mut mask = Self::new(width, height);
591 for (i, &v) in data.iter().enumerate() {
592 if v == nodata {
593 let (word, bit) = Self::coords(i);
594 mask.data[word] |= 1u64 << bit;
595 }
596 }
597 Ok(mask)
598 }
599
600 pub fn from_nodata_u8(data: &[u8], width: usize, height: usize, nodata: u8) -> Result<Self> {
609 let pixel_count = width * height;
610 if data.len() != pixel_count {
611 return Err(OxiGdalError::InvalidParameter {
612 parameter: "data",
613 message: format!(
614 "Slice length {} ≠ {}×{} = {} pixels",
615 data.len(),
616 width,
617 height,
618 pixel_count
619 ),
620 });
621 }
622 let mut mask = Self::new(width, height);
623 for (i, &v) in data.iter().enumerate() {
624 if v == nodata {
625 let (word, bit) = Self::coords(i);
626 mask.data[word] |= 1u64 << bit;
627 }
628 }
629 Ok(mask)
630 }
631}
632
633impl Mask {
636 #[must_use]
641 pub fn to_bool_vec(&self) -> Vec<bool> {
642 let n = self.pixel_count();
643 let mut out = Vec::with_capacity(n);
644 for i in 0..n {
645 let (word, bit) = Self::coords(i);
646 out.push((self.data[word] >> bit) & 1 == 1);
647 }
648 out
649 }
650}
651
652impl Mask {
655 pub fn apply_to_f32(&self, data: &mut [f32], nodata_value: f32) {
661 let n = self.pixel_count().min(data.len());
662 for (i, elem) in data.iter_mut().enumerate().take(n) {
663 let (word, bit) = Self::coords(i);
664 if word < self.data.len() && (self.data[word] >> bit) & 1 == 1 {
665 *elem = nodata_value;
666 }
667 }
668 }
669
670 pub fn apply_to_f64(&self, data: &mut [f64], nodata_value: f64) {
674 let n = self.pixel_count().min(data.len());
675 for (i, elem) in data.iter_mut().enumerate().take(n) {
676 let (word, bit) = Self::coords(i);
677 if word < self.data.len() && (self.data[word] >> bit) & 1 == 1 {
678 *elem = nodata_value;
679 }
680 }
681 }
682
683 pub fn apply_to_u8(&self, data: &mut [u8], nodata_value: u8) {
687 let n = self.pixel_count().min(data.len());
688 for (i, elem) in data.iter_mut().enumerate().take(n) {
689 let (word, bit) = Self::coords(i);
690 if word < self.data.len() && (self.data[word] >> bit) & 1 == 1 {
691 *elem = nodata_value;
692 }
693 }
694 }
695
696 pub fn apply_to_i32(&self, data: &mut [i32], nodata_value: i32) {
700 let n = self.pixel_count().min(data.len());
701 for (i, elem) in data.iter_mut().enumerate().take(n) {
702 let (word, bit) = Self::coords(i);
703 if word < self.data.len() && (self.data[word] >> bit) & 1 == 1 {
704 *elem = nodata_value;
705 }
706 }
707 }
708}
709
710#[cfg(test)]
713mod tests {
714 #![allow(clippy::expect_used)]
715
716 use super::*;
717
718 #[test]
721 fn test_mask_basic_set_get() {
722 let mut mask = Mask::new(8, 8);
723 for y in 0..8 {
725 for x in 0..8 {
726 assert!(!mask.get(x, y), "({x},{y}) should be unset");
727 }
728 }
729 mask.set(0, 0, true);
730 mask.set(7, 7, true);
731 mask.set(3, 5, true);
732 assert!(mask.get(0, 0));
733 assert!(mask.get(7, 7));
734 assert!(mask.get(3, 5));
735 assert!(!mask.get(1, 1));
736 assert_eq!(mask.count_set(), 3);
737 }
738
739 #[test]
740 fn test_mask_fill_true() {
741 let mut mask = Mask::new(10, 10);
742 mask.fill(true);
743 assert_eq!(mask.count_set(), 100);
744 assert_eq!(mask.count_unset(), 0);
745 }
746
747 #[test]
748 fn test_mask_fill_false() {
749 let mut mask = Mask::new_filled(5, 5);
750 mask.fill(false);
751 assert_eq!(mask.count_set(), 0);
752 assert!(mask.all_unset());
753 }
754
755 #[test]
756 fn test_mask_fill_rect() {
757 let mut mask = Mask::new(10, 10);
758 mask.fill_rect(2, 2, 3, 3, true);
760 assert_eq!(mask.count_set(), 9);
761 for dy in 0..3 {
763 for dx in 0..3 {
764 assert!(mask.get(2 + dx, 2 + dy));
765 }
766 }
767 assert!(!mask.get(1, 2));
769 assert!(!mask.get(5, 5));
770 }
771
772 #[test]
775 fn test_mask_and_or_not() {
776 let mut a = Mask::new(4, 4); a.set(0, 0, true);
778 a.set(1, 1, true);
779
780 let mut b = Mask::new(4, 4); b.set(1, 1, true);
782 b.set(2, 2, true);
783
784 let and = a.and(&b).expect("and should succeed");
786 assert_eq!(and.count_set(), 1);
787 assert!(and.get(1, 1));
788
789 let or = a.or(&b).expect("or should succeed");
791 assert_eq!(or.count_set(), 3);
792
793 let not_a = a.not();
795 assert_eq!(not_a.count_set(), 14);
796 assert!(!not_a.get(0, 0));
797 assert!(!not_a.get(1, 1));
798 assert!(not_a.get(0, 1));
799 }
800
801 #[test]
802 fn test_mask_dimension_mismatch_err() {
803 let mut a = Mask::new(4, 4);
804 let b = Mask::new(4, 5);
805 assert!(a.and_assign(&b).is_err());
806 assert!(a.or_assign(&b).is_err());
807 assert!(a.xor_assign(&b).is_err());
808 assert!(a.and(&b).is_err());
809 assert!(a.or(&b).is_err());
810 }
811
812 #[test]
815 fn test_mask_from_slice_roundtrip() {
816 let values: Vec<bool> = (0..20usize).map(|i| i % 3 == 0).collect();
817 let mask = Mask::from_slice(4, 5, &values).expect("from_slice");
818 let back = mask.to_bool_vec();
819 assert_eq!(back, values);
820 }
821
822 #[test]
823 fn test_mask_from_slice_length_err() {
824 assert!(Mask::from_slice(4, 4, &[true; 10]).is_err());
826 }
827
828 #[test]
831 fn test_mask_from_nodata_f32() {
832 let nodata = -9999.0f32;
833 let data = vec![1.0f32, nodata, f32::NAN, 3.0, nodata];
834 let mask = Mask::from_nodata_f32(&data, 5, 1, nodata).expect("from_nodata_f32");
835 assert!(!mask.get(0, 0));
838 assert!(mask.get(1, 0));
839 assert!(!mask.get(2, 0)); assert!(!mask.get(3, 0));
841 assert!(mask.get(4, 0));
842 assert_eq!(mask.count_set(), 2);
843 }
844
845 #[test]
846 fn test_mask_from_nodata_f32_nan_nodata() {
847 let data = vec![1.0f32, f32::NAN, 2.0, f32::NAN];
849 let mask = Mask::from_nodata_f32(&data, 4, 1, f32::NAN).expect("from_nodata_f32 NaN");
850 assert!(!mask.get(0, 0));
851 assert!(mask.get(1, 0));
852 assert!(!mask.get(2, 0));
853 assert!(mask.get(3, 0));
854 }
855
856 #[test]
857 fn test_mask_from_nodata_u8_zero() {
858 let data: Vec<u8> = vec![0, 1, 0, 5, 0];
859 let mask = Mask::from_nodata_u8(&data, 5, 1, 0).expect("from_nodata_u8");
860 assert!(mask.get(0, 0));
861 assert!(!mask.get(1, 0));
862 assert!(mask.get(2, 0));
863 assert!(!mask.get(3, 0));
864 assert!(mask.get(4, 0));
865 assert_eq!(mask.count_set(), 3);
866 }
867
868 #[test]
871 fn test_mask_apply_to_f32() {
872 let mut mask = Mask::new(4, 1);
873 mask.set(1, 0, true);
874 mask.set(3, 0, true);
875 let mut data = vec![1.0f32, 2.0, 3.0, 4.0];
876 mask.apply_to_f32(&mut data, -9999.0);
877 assert!((data[0] - 1.0).abs() < 1e-7);
878 assert!((data[1] - (-9999.0)).abs() < 1e-1);
879 assert!((data[2] - 3.0).abs() < 1e-7);
880 assert!((data[3] - (-9999.0)).abs() < 1e-1);
881 }
882
883 #[test]
884 fn test_mask_apply_to_u8() {
885 let mut mask = Mask::new(3, 1);
886 mask.set(0, 0, true);
887 mask.set(2, 0, true);
888 let mut data: Vec<u8> = vec![10, 20, 30];
889 mask.apply_to_u8(&mut data, 0);
890 assert_eq!(data, vec![0, 20, 0]);
891 }
892
893 #[test]
896 fn test_mask_count_set_checkerboard() {
897 let values: Vec<bool> = (0..64usize).map(|i| i % 2 == 0).collect();
899 let mask = Mask::from_slice(8, 8, &values).expect("from_slice");
900 assert_eq!(mask.count_set(), 32);
901 assert_eq!(mask.count_unset(), 32);
902 }
903
904 #[test]
907 fn test_mask_set_positions_iterator() {
908 let mut mask = Mask::new(5, 5);
909 let positions = [(1usize, 0usize), (4, 2), (0, 4)];
910 for &(x, y) in &positions {
911 mask.set(x, y, true);
912 }
913 let mut collected: Vec<(usize, usize)> = mask.set_positions().collect();
914 collected.sort_unstable();
915 let mut expected = positions.to_vec();
916 expected.sort_unstable();
917 assert_eq!(collected, expected);
918 }
919
920 #[test]
923 fn test_mask_all_set_all_unset() {
924 let empty = Mask::new(0, 0);
925 assert!(empty.all_set());
927 assert!(empty.all_unset());
928
929 let zeros = Mask::new(10, 10);
930 assert!(zeros.all_unset());
931 assert!(!zeros.all_set());
932
933 let ones = Mask::new_filled(10, 10);
934 assert!(ones.all_set());
935 assert!(!ones.all_unset());
936 }
937
938 #[test]
941 fn test_mask_large_width_crosses_word_boundary() {
942 let mut mask = Mask::new(65, 1);
944 mask.set(63, 0, true); mask.set(64, 0, true); assert!(mask.get(63, 0));
947 assert!(mask.get(64, 0));
948 assert!(!mask.get(0, 0));
949 assert_eq!(mask.count_set(), 2);
950 }
951
952 #[test]
955 fn test_mask_single_pixel() {
956 let mut mask = Mask::new(1, 1);
957 assert!(!mask.get(0, 0));
958 assert!(mask.all_unset());
959 mask.set(0, 0, true);
960 assert!(mask.get(0, 0));
961 assert!(mask.all_set());
962 assert_eq!(mask.count_set(), 1);
963 mask.not_in_place();
964 assert!(!mask.get(0, 0));
965 assert!(mask.all_unset());
966 }
967
968 #[test]
971 fn test_mask_xor_self_is_zero() {
972 let mut mask = Mask::new(6, 6);
973 mask.fill_rect(0, 0, 3, 3, true);
974 let clone = mask.clone();
975 mask.xor_assign(&clone).expect("xor_assign");
976 assert!(mask.all_unset());
977 }
978
979 #[test]
982 fn test_mask_new_filled_all_set() {
983 let mask = Mask::new_filled(9, 7); assert_eq!(mask.pixel_count(), 63);
986 assert_eq!(mask.count_set(), 63);
987 assert!(mask.all_set());
988 }
989
990 #[test]
991 fn test_mask_not_in_place_tail_invariant() {
992 let mut mask = Mask::new(9, 7); mask.not_in_place(); assert_eq!(mask.count_set(), 63);
995 mask.not_in_place(); assert_eq!(mask.count_set(), 0);
997 }
998
999 #[test]
1002 fn test_mask_or_idempotent() {
1003 let mut a = Mask::new(4, 4);
1004 a.fill_rect(0, 0, 2, 2, true);
1005 let b = a.clone();
1006 a.or_assign(&b).expect("or_assign");
1007 assert_eq!(a.count_set(), 4);
1008 }
1009
1010 #[test]
1013 fn test_mask_to_bool_vec_roundtrip() {
1014 let mask = Mask::new_filled(5, 3);
1015 let bv = mask.to_bool_vec();
1016 assert_eq!(bv.len(), 15);
1017 assert!(bv.iter().all(|&b| b));
1018 }
1019}