Skip to main content

singe_npp/image/
compare.rs

1use singe_npp_sys as sys;
2
3use crate::{
4    context::StreamContext,
5    error::{Error, Result},
6    image::view::{AC4, C1, C3, C4, ImageView, MaskViewMut},
7    try_ffi,
8    types::{ComparisonOperation, DataTypeLike, Size},
9};
10
11macro_rules! impl_generic_compare {
12    ($trait:ident, $method:ident, $function:ident, $layout:ty, [$($ty:ty => $direct:ident),* $(,)?]) => {
13        pub trait $trait<Layout>: DataTypeLike + Sized {
14            fn $method(
15                stream_context: &StreamContext,
16                source1: &ImageView<'_, Self, Layout>,
17                source2: &ImageView<'_, Self, Layout>,
18                destination: &mut MaskViewMut<'_>,
19                operation: ComparisonOperation,
20            ) -> Result<()>;
21        }
22
23        pub fn $function<T>(
24            stream_context: &StreamContext,
25            source1: &ImageView<'_, T, $layout>,
26            source2: &ImageView<'_, T, $layout>,
27            destination: &mut MaskViewMut<'_>,
28            operation: ComparisonOperation,
29        ) -> Result<()>
30        where
31            T: $trait<$layout>,
32        {
33            T::$method(stream_context, source1, source2, destination, operation)
34        }
35
36        $(
37            impl $trait<$layout> for $ty {
38                fn $method(
39                    stream_context: &StreamContext,
40                    source1: &ImageView<'_, Self, $layout>,
41                    source2: &ImageView<'_, Self, $layout>,
42                    destination: &mut MaskViewMut<'_>,
43                    operation: ComparisonOperation,
44                ) -> Result<()> {
45                    $direct(stream_context, source1, source2, destination, operation)
46                }
47            }
48        )*
49    };
50}
51
52macro_rules! impl_generic_compare_constant_scalar {
53    ($trait:ident, $method:ident, $function:ident, $layout:ty, [$($ty:ty => $direct:ident),* $(,)?]) => {
54        pub trait $trait<Layout>: DataTypeLike + Sized {
55            fn $method(
56                stream_context: &StreamContext,
57                source: &ImageView<'_, Self, Layout>,
58                constant: Self,
59                destination: &mut MaskViewMut<'_>,
60                operation: ComparisonOperation,
61            ) -> Result<()>;
62        }
63
64        pub fn $function<T>(
65            stream_context: &StreamContext,
66            source: &ImageView<'_, T, $layout>,
67            constant: T,
68            destination: &mut MaskViewMut<'_>,
69            operation: ComparisonOperation,
70        ) -> Result<()>
71        where
72            T: $trait<$layout>,
73        {
74            T::$method(stream_context, source, constant, destination, operation)
75        }
76
77        $(
78            impl $trait<$layout> for $ty {
79                fn $method(
80                    stream_context: &StreamContext,
81                    source: &ImageView<'_, Self, $layout>,
82                    constant: Self,
83                    destination: &mut MaskViewMut<'_>,
84                    operation: ComparisonOperation,
85                ) -> Result<()> {
86                    $direct(stream_context, source, constant, destination, operation)
87                }
88            }
89        )*
90    };
91}
92
93macro_rules! impl_generic_compare_constant_array {
94    ($trait:ident, $method:ident, $function:ident, $layout:ty, $channels:literal, [$($ty:ty => $direct:ident),* $(,)?]) => {
95        pub trait $trait<Layout>: DataTypeLike + Sized {
96            fn $method(
97                stream_context: &StreamContext,
98                source: &ImageView<'_, Self, Layout>,
99                constants: [Self; $channels],
100                destination: &mut MaskViewMut<'_>,
101                operation: ComparisonOperation,
102            ) -> Result<()>;
103        }
104
105        pub fn $function<T>(
106            stream_context: &StreamContext,
107            source: &ImageView<'_, T, $layout>,
108            constants: [T; $channels],
109            destination: &mut MaskViewMut<'_>,
110            operation: ComparisonOperation,
111        ) -> Result<()>
112        where
113            T: $trait<$layout>,
114        {
115            T::$method(stream_context, source, constants, destination, operation)
116        }
117
118        $(
119            impl $trait<$layout> for $ty {
120                fn $method(
121                    stream_context: &StreamContext,
122                    source: &ImageView<'_, Self, $layout>,
123                    constants: [Self; $channels],
124                    destination: &mut MaskViewMut<'_>,
125                    operation: ComparisonOperation,
126                ) -> Result<()> {
127                    $direct(stream_context, source, constants, destination, operation)
128                }
129            }
130        )*
131    };
132}
133
134macro_rules! impl_generic_compare_equal_epsilon {
135    ($trait:ident, $method:ident, $function:ident, $layout:ty, [$($ty:ty => $direct:ident),* $(,)?]) => {
136        pub trait $trait<Layout>: DataTypeLike + Sized {
137            fn $method(
138                stream_context: &StreamContext,
139                source1: &ImageView<'_, Self, Layout>,
140                source2: &ImageView<'_, Self, Layout>,
141                destination: &mut MaskViewMut<'_>,
142                epsilon: Self,
143            ) -> Result<()>;
144        }
145
146        pub fn $function<T>(
147            stream_context: &StreamContext,
148            source1: &ImageView<'_, T, $layout>,
149            source2: &ImageView<'_, T, $layout>,
150            destination: &mut MaskViewMut<'_>,
151            epsilon: T,
152        ) -> Result<()>
153        where
154            T: $trait<$layout>,
155        {
156            T::$method(stream_context, source1, source2, destination, epsilon)
157        }
158
159        $(
160            impl $trait<$layout> for $ty {
161                fn $method(
162                    stream_context: &StreamContext,
163                    source1: &ImageView<'_, Self, $layout>,
164                    source2: &ImageView<'_, Self, $layout>,
165                    destination: &mut MaskViewMut<'_>,
166                    epsilon: Self,
167                ) -> Result<()> {
168                    $direct(stream_context, source1, source2, destination, epsilon)
169                }
170            }
171        )*
172    };
173}
174
175macro_rules! impl_generic_compare_equal_epsilon_constant_scalar {
176    ($trait:ident, $method:ident, $function:ident, $layout:ty, [$($ty:ty => $direct:ident),* $(,)?]) => {
177        pub trait $trait<Layout>: DataTypeLike + Sized {
178            fn $method(
179                stream_context: &StreamContext,
180                source: &ImageView<'_, Self, Layout>,
181                constant: Self,
182                destination: &mut MaskViewMut<'_>,
183                epsilon: Self,
184            ) -> Result<()>;
185        }
186
187        pub fn $function<T>(
188            stream_context: &StreamContext,
189            source: &ImageView<'_, T, $layout>,
190            constant: T,
191            destination: &mut MaskViewMut<'_>,
192            epsilon: T,
193        ) -> Result<()>
194        where
195            T: $trait<$layout>,
196        {
197            T::$method(stream_context, source, constant, destination, epsilon)
198        }
199
200        $(
201            impl $trait<$layout> for $ty {
202                fn $method(
203                    stream_context: &StreamContext,
204                    source: &ImageView<'_, Self, $layout>,
205                    constant: Self,
206                    destination: &mut MaskViewMut<'_>,
207                    epsilon: Self,
208                ) -> Result<()> {
209                    $direct(stream_context, source, constant, destination, epsilon)
210                }
211            }
212        )*
213    };
214}
215
216macro_rules! impl_generic_compare_equal_epsilon_constant_array {
217    ($trait:ident, $method:ident, $function:ident, $layout:ty, $channels:literal, [$($ty:ty => $direct:ident),* $(,)?]) => {
218        pub trait $trait<Layout>: DataTypeLike + Sized {
219            fn $method(
220                stream_context: &StreamContext,
221                source: &ImageView<'_, Self, Layout>,
222                constants: [Self; $channels],
223                destination: &mut MaskViewMut<'_>,
224                epsilon: Self,
225            ) -> Result<()>;
226        }
227
228        pub fn $function<T>(
229            stream_context: &StreamContext,
230            source: &ImageView<'_, T, $layout>,
231            constants: [T; $channels],
232            destination: &mut MaskViewMut<'_>,
233            epsilon: T,
234        ) -> Result<()>
235        where
236            T: $trait<$layout>,
237        {
238            T::$method(stream_context, source, constants, destination, epsilon)
239        }
240
241        $(
242            impl $trait<$layout> for $ty {
243                fn $method(
244                    stream_context: &StreamContext,
245                    source: &ImageView<'_, Self, $layout>,
246                    constants: [Self; $channels],
247                    destination: &mut MaskViewMut<'_>,
248                    epsilon: Self,
249                ) -> Result<()> {
250                    $direct(stream_context, source, constants, destination, epsilon)
251                }
252            }
253        )*
254    };
255}
256
257macro_rules! impl_compare_c1 {
258    ($name:ident, $ty:ty, $ffi:ident) => {
259        pub fn $name(
260            stream_context: &StreamContext,
261            source1: &ImageView<'_, $ty, C1>,
262            source2: &ImageView<'_, $ty, C1>,
263            destination: &mut MaskViewMut<'_>,
264            operation: ComparisonOperation,
265        ) -> Result<()> {
266            validate_same_size(source1.size(), source2.size())?;
267            validate_same_size(source1.size(), destination.size())?;
268
269            unsafe {
270                try_ffi!(sys::$ffi(
271                    source1.as_ptr().cast(),
272                    source1.step(),
273                    source2.as_ptr().cast(),
274                    source2.step(),
275                    destination.as_mut_ptr().cast(),
276                    destination.step(),
277                    source1.size().into(),
278                    operation.into(),
279                    stream_context.as_raw(),
280                ))?;
281            }
282            Ok(())
283        }
284    };
285}
286
287impl_compare_c1!(compare_u8_c1, u8, nppiCompare_8u_C1R_Ctx);
288impl_compare_c1!(compare_u16_c1, u16, nppiCompare_16u_C1R_Ctx);
289impl_compare_c1!(compare_i16_c1, i16, nppiCompare_16s_C1R_Ctx);
290impl_compare_c1!(compare_f32_c1, f32, nppiCompare_32f_C1R_Ctx);
291
292macro_rules! impl_compare_packed {
293    ($name:ident, $ty:ty, $layout:ty, $ffi:ident) => {
294        pub fn $name(
295            stream_context: &StreamContext,
296            source1: &ImageView<'_, $ty, $layout>,
297            source2: &ImageView<'_, $ty, $layout>,
298            destination: &mut MaskViewMut<'_>,
299            operation: ComparisonOperation,
300        ) -> Result<()> {
301            validate_same_size(source1.size(), source2.size())?;
302            validate_same_size(source1.size(), destination.size())?;
303
304            unsafe {
305                try_ffi!(sys::$ffi(
306                    source1.as_ptr().cast(),
307                    source1.step(),
308                    source2.as_ptr().cast(),
309                    source2.step(),
310                    destination.as_mut_ptr().cast(),
311                    destination.step(),
312                    source1.size().into(),
313                    operation.into(),
314                    stream_context.as_raw(),
315                ))?;
316            }
317            Ok(())
318        }
319    };
320}
321
322impl_compare_packed!(compare_u8_c3, u8, C3, nppiCompare_8u_C3R_Ctx);
323impl_compare_packed!(compare_u8_c4, u8, C4, nppiCompare_8u_C4R_Ctx);
324impl_compare_packed!(compare_u8_ac4, u8, AC4, nppiCompare_8u_AC4R_Ctx);
325impl_compare_packed!(compare_u16_c3, u16, C3, nppiCompare_16u_C3R_Ctx);
326impl_compare_packed!(compare_u16_c4, u16, C4, nppiCompare_16u_C4R_Ctx);
327impl_compare_packed!(compare_u16_ac4, u16, AC4, nppiCompare_16u_AC4R_Ctx);
328impl_compare_packed!(compare_i16_c3, i16, C3, nppiCompare_16s_C3R_Ctx);
329impl_compare_packed!(compare_i16_c4, i16, C4, nppiCompare_16s_C4R_Ctx);
330impl_compare_packed!(compare_i16_ac4, i16, AC4, nppiCompare_16s_AC4R_Ctx);
331impl_compare_packed!(compare_f32_c3, f32, C3, nppiCompare_32f_C3R_Ctx);
332impl_compare_packed!(compare_f32_c4, f32, C4, nppiCompare_32f_C4R_Ctx);
333impl_compare_packed!(compare_f32_ac4, f32, AC4, nppiCompare_32f_AC4R_Ctx);
334
335impl_generic_compare!(CompareC1, compare, compare_c1, C1, [
336    u8 => compare_u8_c1,
337    u16 => compare_u16_c1,
338    i16 => compare_i16_c1,
339    f32 => compare_f32_c1,
340]);
341impl_generic_compare!(CompareC3, compare, compare_c3, C3, [
342    u8 => compare_u8_c3,
343    u16 => compare_u16_c3,
344    i16 => compare_i16_c3,
345    f32 => compare_f32_c3,
346]);
347impl_generic_compare!(CompareC4, compare, compare_c4, C4, [
348    u8 => compare_u8_c4,
349    u16 => compare_u16_c4,
350    i16 => compare_i16_c4,
351    f32 => compare_f32_c4,
352]);
353impl_generic_compare!(CompareAc4, compare, compare_ac4, AC4, [
354    u8 => compare_u8_ac4,
355    u16 => compare_u16_ac4,
356    i16 => compare_i16_ac4,
357    f32 => compare_f32_ac4,
358]);
359
360macro_rules! impl_compare_constant_c1 {
361    ($name:ident, $ty:ty, $ffi:ident) => {
362        pub fn $name(
363            stream_context: &StreamContext,
364            source: &ImageView<'_, $ty, C1>,
365            constant: $ty,
366            destination: &mut MaskViewMut<'_>,
367            operation: ComparisonOperation,
368        ) -> Result<()> {
369            validate_same_size(source.size(), destination.size())?;
370
371            unsafe {
372                try_ffi!(sys::$ffi(
373                    source.as_ptr().cast(),
374                    source.step(),
375                    constant,
376                    destination.as_mut_ptr().cast(),
377                    destination.step(),
378                    source.size().into(),
379                    operation.into(),
380                    stream_context.as_raw(),
381                ))?;
382            }
383            Ok(())
384        }
385    };
386}
387
388impl_compare_constant_c1!(compare_constant_u8_c1, u8, nppiCompareC_8u_C1R_Ctx);
389impl_compare_constant_c1!(compare_constant_u16_c1, u16, nppiCompareC_16u_C1R_Ctx);
390impl_compare_constant_c1!(compare_constant_i16_c1, i16, nppiCompareC_16s_C1R_Ctx);
391impl_compare_constant_c1!(compare_constant_f32_c1, f32, nppiCompareC_32f_C1R_Ctx);
392
393macro_rules! impl_compare_constant_packed {
394    ($name:ident, $ty:ty, $layout:ty, $channels:expr, $ffi:ident) => {
395        pub fn $name(
396            stream_context: &StreamContext,
397            source: &ImageView<'_, $ty, $layout>,
398            constants: [$ty; $channels],
399            destination: &mut MaskViewMut<'_>,
400            operation: ComparisonOperation,
401        ) -> Result<()> {
402            validate_same_size(source.size(), destination.size())?;
403
404            unsafe {
405                try_ffi!(sys::$ffi(
406                    source.as_ptr().cast(),
407                    source.step(),
408                    constants.as_ptr().cast(),
409                    destination.as_mut_ptr().cast(),
410                    destination.step(),
411                    source.size().into(),
412                    operation.into(),
413                    stream_context.as_raw(),
414                ))?;
415            }
416            Ok(())
417        }
418    };
419}
420
421impl_compare_constant_packed!(compare_constant_u8_c3, u8, C3, 3, nppiCompareC_8u_C3R_Ctx);
422impl_compare_constant_packed!(compare_constant_u8_c4, u8, C4, 4, nppiCompareC_8u_C4R_Ctx);
423impl_compare_constant_packed!(
424    compare_constant_u8_ac4,
425    u8,
426    AC4,
427    3,
428    nppiCompareC_8u_AC4R_Ctx
429);
430impl_compare_constant_packed!(
431    compare_constant_u16_c3,
432    u16,
433    C3,
434    3,
435    nppiCompareC_16u_C3R_Ctx
436);
437impl_compare_constant_packed!(
438    compare_constant_u16_c4,
439    u16,
440    C4,
441    4,
442    nppiCompareC_16u_C4R_Ctx
443);
444impl_compare_constant_packed!(
445    compare_constant_u16_ac4,
446    u16,
447    AC4,
448    3,
449    nppiCompareC_16u_AC4R_Ctx
450);
451impl_compare_constant_packed!(
452    compare_constant_i16_c3,
453    i16,
454    C3,
455    3,
456    nppiCompareC_16s_C3R_Ctx
457);
458impl_compare_constant_packed!(
459    compare_constant_i16_c4,
460    i16,
461    C4,
462    4,
463    nppiCompareC_16s_C4R_Ctx
464);
465impl_compare_constant_packed!(
466    compare_constant_i16_ac4,
467    i16,
468    AC4,
469    3,
470    nppiCompareC_16s_AC4R_Ctx
471);
472impl_compare_constant_packed!(
473    compare_constant_f32_c3,
474    f32,
475    C3,
476    3,
477    nppiCompareC_32f_C3R_Ctx
478);
479impl_compare_constant_packed!(
480    compare_constant_f32_c4,
481    f32,
482    C4,
483    4,
484    nppiCompareC_32f_C4R_Ctx
485);
486impl_compare_constant_packed!(
487    compare_constant_f32_ac4,
488    f32,
489    AC4,
490    3,
491    nppiCompareC_32f_AC4R_Ctx
492);
493
494impl_generic_compare_constant_scalar!(
495    CompareConstantC1,
496    compare_constant,
497    compare_constant_c1,
498    C1,
499    [
500        u8 => compare_constant_u8_c1,
501        u16 => compare_constant_u16_c1,
502        i16 => compare_constant_i16_c1,
503        f32 => compare_constant_f32_c1,
504    ]
505);
506impl_generic_compare_constant_array!(
507    CompareConstantC3,
508    compare_constant,
509    compare_constant_c3,
510    C3,
511    3,
512    [
513        u8 => compare_constant_u8_c3,
514        u16 => compare_constant_u16_c3,
515        i16 => compare_constant_i16_c3,
516        f32 => compare_constant_f32_c3,
517    ]
518);
519impl_generic_compare_constant_array!(
520    CompareConstantC4,
521    compare_constant,
522    compare_constant_c4,
523    C4,
524    4,
525    [
526        u8 => compare_constant_u8_c4,
527        u16 => compare_constant_u16_c4,
528        i16 => compare_constant_i16_c4,
529        f32 => compare_constant_f32_c4,
530    ]
531);
532impl_generic_compare_constant_array!(
533    CompareConstantAc4,
534    compare_constant,
535    compare_constant_ac4,
536    AC4,
537    3,
538    [
539        u8 => compare_constant_u8_ac4,
540        u16 => compare_constant_u16_ac4,
541        i16 => compare_constant_i16_ac4,
542        f32 => compare_constant_f32_ac4,
543    ]
544);
545
546macro_rules! impl_compare_equal_epsilon {
547    ($name:ident, $layout:ty, $ffi:ident) => {
548        pub fn $name(
549            stream_context: &StreamContext,
550            source1: &ImageView<'_, f32, $layout>,
551            source2: &ImageView<'_, f32, $layout>,
552            destination: &mut MaskViewMut<'_>,
553            epsilon: f32,
554        ) -> Result<()> {
555            validate_same_size(source1.size(), source2.size())?;
556            validate_same_size(source1.size(), destination.size())?;
557
558            unsafe {
559                try_ffi!(sys::$ffi(
560                    source1.as_ptr().cast(),
561                    source1.step(),
562                    source2.as_ptr().cast(),
563                    source2.step(),
564                    destination.as_mut_ptr().cast(),
565                    destination.step(),
566                    source1.size().into(),
567                    epsilon,
568                    stream_context.as_raw(),
569                ))?;
570            }
571            Ok(())
572        }
573    };
574}
575
576impl_compare_equal_epsilon!(
577    compare_equal_epsilon_f32_c1,
578    C1,
579    nppiCompareEqualEps_32f_C1R_Ctx
580);
581impl_compare_equal_epsilon!(
582    compare_equal_epsilon_f32_c3,
583    C3,
584    nppiCompareEqualEps_32f_C3R_Ctx
585);
586impl_compare_equal_epsilon!(
587    compare_equal_epsilon_f32_c4,
588    C4,
589    nppiCompareEqualEps_32f_C4R_Ctx
590);
591impl_compare_equal_epsilon!(
592    compare_equal_epsilon_f32_ac4,
593    AC4,
594    nppiCompareEqualEps_32f_AC4R_Ctx
595);
596
597impl_generic_compare_equal_epsilon!(
598    CompareEqualEpsilonC1,
599    compare_equal_epsilon,
600    compare_equal_epsilon_c1,
601    C1,
602    [f32 => compare_equal_epsilon_f32_c1]
603);
604impl_generic_compare_equal_epsilon!(
605    CompareEqualEpsilonC3,
606    compare_equal_epsilon,
607    compare_equal_epsilon_c3,
608    C3,
609    [f32 => compare_equal_epsilon_f32_c3]
610);
611impl_generic_compare_equal_epsilon!(
612    CompareEqualEpsilonC4,
613    compare_equal_epsilon,
614    compare_equal_epsilon_c4,
615    C4,
616    [f32 => compare_equal_epsilon_f32_c4]
617);
618impl_generic_compare_equal_epsilon!(
619    CompareEqualEpsilonAc4,
620    compare_equal_epsilon,
621    compare_equal_epsilon_ac4,
622    AC4,
623    [f32 => compare_equal_epsilon_f32_ac4]
624);
625
626macro_rules! impl_compare_equal_epsilon_constant_c1 {
627    ($name:ident, $ffi:ident) => {
628        pub fn $name(
629            stream_context: &StreamContext,
630            source: &ImageView<'_, f32, C1>,
631            constant: f32,
632            destination: &mut MaskViewMut<'_>,
633            epsilon: f32,
634        ) -> Result<()> {
635            validate_same_size(source.size(), destination.size())?;
636
637            unsafe {
638                try_ffi!(sys::$ffi(
639                    source.as_ptr().cast(),
640                    source.step(),
641                    constant,
642                    destination.as_mut_ptr().cast(),
643                    destination.step(),
644                    source.size().into(),
645                    epsilon,
646                    stream_context.as_raw(),
647                ))?;
648            }
649            Ok(())
650        }
651    };
652}
653
654macro_rules! impl_compare_equal_epsilon_constant_packed {
655    ($name:ident, $layout:ty, $channels:expr, $ffi:ident) => {
656        pub fn $name(
657            stream_context: &StreamContext,
658            source: &ImageView<'_, f32, $layout>,
659            constants: [f32; $channels],
660            destination: &mut MaskViewMut<'_>,
661            epsilon: f32,
662        ) -> Result<()> {
663            validate_same_size(source.size(), destination.size())?;
664
665            unsafe {
666                try_ffi!(sys::$ffi(
667                    source.as_ptr().cast(),
668                    source.step(),
669                    constants.as_ptr().cast(),
670                    destination.as_mut_ptr().cast(),
671                    destination.step(),
672                    source.size().into(),
673                    epsilon,
674                    stream_context.as_raw(),
675                ))?;
676            }
677            Ok(())
678        }
679    };
680}
681
682impl_compare_equal_epsilon_constant_c1!(
683    compare_equal_epsilon_constant_f32_c1,
684    nppiCompareEqualEpsC_32f_C1R_Ctx
685);
686impl_compare_equal_epsilon_constant_packed!(
687    compare_equal_epsilon_constant_f32_c3,
688    C3,
689    3,
690    nppiCompareEqualEpsC_32f_C3R_Ctx
691);
692impl_compare_equal_epsilon_constant_packed!(
693    compare_equal_epsilon_constant_f32_c4,
694    C4,
695    4,
696    nppiCompareEqualEpsC_32f_C4R_Ctx
697);
698impl_compare_equal_epsilon_constant_packed!(
699    compare_equal_epsilon_constant_f32_ac4,
700    AC4,
701    3,
702    nppiCompareEqualEpsC_32f_AC4R_Ctx
703);
704
705impl_generic_compare_equal_epsilon_constant_scalar!(
706    CompareEqualEpsilonConstantC1,
707    compare_equal_epsilon_constant,
708    compare_equal_epsilon_constant_c1,
709    C1,
710    [f32 => compare_equal_epsilon_constant_f32_c1]
711);
712impl_generic_compare_equal_epsilon_constant_array!(
713    CompareEqualEpsilonConstantC3,
714    compare_equal_epsilon_constant,
715    compare_equal_epsilon_constant_c3,
716    C3,
717    3,
718    [f32 => compare_equal_epsilon_constant_f32_c3]
719);
720impl_generic_compare_equal_epsilon_constant_array!(
721    CompareEqualEpsilonConstantC4,
722    compare_equal_epsilon_constant,
723    compare_equal_epsilon_constant_c4,
724    C4,
725    4,
726    [f32 => compare_equal_epsilon_constant_f32_c4]
727);
728impl_generic_compare_equal_epsilon_constant_array!(
729    CompareEqualEpsilonConstantAc4,
730    compare_equal_epsilon_constant,
731    compare_equal_epsilon_constant_ac4,
732    AC4,
733    3,
734    [f32 => compare_equal_epsilon_constant_f32_ac4]
735);
736
737fn validate_same_size(source: Size, destination: Size) -> Result<()> {
738    if source == destination {
739        return Ok(());
740    }
741    Err(Error::SizeMismatch {
742        name: "image size".into(),
743        expected: source,
744        actual: destination,
745    })
746}