1use std::ops::Index;
29
30#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
31pub struct EdgeMode2D {
36 pub horizontal: EdgeMode,
38 pub vertical: EdgeMode,
40}
41
42impl EdgeMode2D {
43 pub const fn new(mode: EdgeMode) -> Self {
44 Self {
45 horizontal: mode,
46 vertical: mode,
47 }
48 }
49
50 pub const fn anisotropy(horizontal: EdgeMode, vertical: EdgeMode) -> Self {
51 Self {
52 horizontal,
53 vertical,
54 }
55 }
56}
57
58#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
59pub enum EdgeMode {
61 #[default]
63 Clamp = 0,
64 Wrap = 1,
66 Reflect = 2,
68 Reflect101 = 3,
70 Constant = 4,
73}
74
75impl From<usize> for EdgeMode {
76 fn from(value: usize) -> Self {
77 match value {
78 0 => EdgeMode::Clamp,
79 1 => EdgeMode::Wrap,
80 2 => EdgeMode::Reflect,
81 3 => EdgeMode::Reflect101,
82 4 => EdgeMode::Constant,
83 _ => {
84 unreachable!("Unknown edge mode for value: {value}");
85 }
86 }
87 }
88}
89
90impl EdgeMode {
91 pub const fn as_2d(self) -> EdgeMode2D {
92 EdgeMode2D::new(self)
93 }
94}
95
96#[inline(always)]
97pub(crate) fn reflect_index(i: isize, n: isize) -> usize {
98 (n - i.rem_euclid(n) - 1) as usize
99}
100
101#[inline(always)]
102#[allow(dead_code)]
103pub(crate) fn reflect_index_101(i: isize, n: isize) -> usize {
104 let n_r = n - 1;
105 if n_r == 0 {
106 return 0;
107 }
108 (n_r - i.rem_euclid(n_r)) as usize
109}
110
111#[allow(clippy::int_plus_one)]
112macro_rules! clamp_edge {
113 ($edge_mode:expr, $value:expr, $min:expr, $max:expr) => {{
114 use crate::edge_mode::EdgeMode;
115 match $edge_mode {
116 EdgeMode::Clamp | EdgeMode::Constant => $value.max($min).min($max - 1) as usize,
117 EdgeMode::Wrap => {
118 if $value < $min || $value >= $max {
119 $value.rem_euclid($max) as usize
120 } else {
121 $value as usize
122 }
123 }
124 EdgeMode::Reflect => {
125 if $value < $min || $value >= $max {
126 use crate::edge_mode::reflect_index;
127 let cx = reflect_index($value as isize, $max as isize);
128 cx as usize
129 } else {
130 $value as usize
131 }
132 }
133 EdgeMode::Reflect101 => {
134 if $value < $min || $value >= $max {
135 use crate::edge_mode::reflect_index_101;
136 reflect_index_101($value as isize, $max as isize)
137 } else {
138 $value as usize
139 }
140 }
141 }
142 }};
143}
144
145#[derive(Clone, Copy)]
146pub struct BorderHandle {
147 pub edge_mode: EdgeMode,
148 pub scalar: Scalar,
149}
150
151macro_rules! border_interpolate {
152 ($slice: expr, $edge_mode:expr, $value:expr, $min:expr, $max:expr, $scale: expr, $cn: expr) => {{
153 use crate::edge_mode::EdgeMode;
154 use num_traits::AsPrimitive;
155 match $edge_mode.edge_mode {
156 EdgeMode::Constant => {
157 if $value < $min || $value >= $max {
158 $edge_mode.scalar[$cn].as_()
159 } else {
160 *$slice.get_unchecked($value as usize * $scale + $cn)
161 }
162 }
163 EdgeMode::Clamp => {
164 *$slice.get_unchecked($value.max($min).min($max - 1) as usize * $scale + $cn)
165 }
166 EdgeMode::Wrap => {
167 if $value < $min || $value >= $max {
168 *$slice.get_unchecked($value.rem_euclid($max) as usize * $scale + $cn)
169 } else {
170 *$slice.get_unchecked($value as usize * $scale + $cn)
171 }
172 }
173 EdgeMode::Reflect => {
174 if $value < $min || $value >= $max {
175 use crate::edge_mode::reflect_index;
176 let cx = reflect_index($value as isize, $max as isize);
177 *$slice.get_unchecked(cx as usize * $scale + $cn)
178 } else {
179 *$slice.get_unchecked($value as usize * $scale + $cn)
180 }
181 }
182 EdgeMode::Reflect101 => {
183 if $value < $min || $value >= $max {
184 use crate::edge_mode::reflect_index_101;
185 let cx = reflect_index_101($value as isize, $max as isize);
186 *$slice.get_unchecked(cx as usize * $scale + $cn)
187 } else {
188 *$slice.get_unchecked($value as usize * $scale + $cn)
189 }
190 }
191 }
192 }};
193}
194
195pub(crate) use border_interpolate;
196pub(crate) use clamp_edge;
197
198#[repr(C)]
199#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
200pub struct Scalar {
201 pub v0: f64,
202 pub v1: f64,
203 pub v2: f64,
204 pub v3: f64,
205}
206
207impl Scalar {
208 pub fn new(v0: f64, v1: f64, v2: f64, v3: f64) -> Self {
209 Self { v0, v1, v2, v3 }
210 }
211
212 pub fn dup(v: f64) -> Self {
213 Scalar::new(v, v, v, v)
214 }
215}
216
217impl Default for Scalar {
218 fn default() -> Self {
219 Self::new(0.0, 0.0, 0.0, 0.0)
220 }
221}
222
223impl Index<usize> for Scalar {
224 type Output = f64;
225 fn index(&self, index: usize) -> &Self::Output {
226 match index {
227 0 => &self.v0,
228 1 => &self.v1,
229 2 => &self.v2,
230 3 => &self.v3,
231 _ => {
232 unimplemented!("Index out of bounds: {}", index);
233 }
234 }
235 }
236}