1use std::cmp::max;
3use std::ops::{Add, Mul};
4
5use num_traits::MulAdd;
6
7#[cfg(any(
8 all(
9 any(target_arch = "x86", target_arch = "x86_64"),
10 target_feature = "fma"
11 ),
12 all(target_arch = "aarch64", target_feature = "neon")
13))]
14#[inline(always)]
15pub(crate) fn multiply_accumulate<
22 T: Copy + Mul<T, Output = T> + Add<T, Output = T> + MulAdd<T, Output = T>,
23>(
24 acc: T,
25 a: T,
26 b: T,
27) -> T {
28 MulAdd::mul_add(a, b, acc)
29}
30
31#[inline(always)]
32#[cfg(not(any(
33 all(
34 any(target_arch = "x86", target_arch = "x86_64"),
35 target_feature = "fma"
36 ),
37 all(target_arch = "aarch64", target_feature = "neon")
38)))]
39pub(crate) fn multiply_accumulate<
40 T: Copy + Mul<T, Output = T> + Add<T, Output = T> + MulAdd<T, Output = T>,
41>(
42 acc: T,
43 a: T,
44 b: T,
45) -> T {
46 acc + a * b
47}
48
49pub(crate) fn resize_dimensions(
57 width: u32,
58 height: u32,
59 nwidth: u32,
60 nheight: u32,
61 fill: bool,
62) -> (u32, u32) {
63 let wratio = f64::from(nwidth) / f64::from(width);
64 let hratio = f64::from(nheight) / f64::from(height);
65
66 let ratio = if fill {
67 f64::max(wratio, hratio)
68 } else {
69 f64::min(wratio, hratio)
70 };
71
72 let nw = max((f64::from(width) * ratio).round() as u64, 1);
73 let nh = max((f64::from(height) * ratio).round() as u64, 1);
74
75 if nw > u64::from(u32::MAX) {
76 let ratio = f64::from(u32::MAX) / f64::from(width);
77 (u32::MAX, max((f64::from(height) * ratio).round() as u32, 1))
78 } else if nh > u64::from(u32::MAX) {
79 let ratio = f64::from(u32::MAX) / f64::from(height);
80 (max((f64::from(width) * ratio).round() as u32, 1), u32::MAX)
81 } else {
82 (nw as u32, nh as u32)
83 }
84}
85
86#[cfg(test)]
87mod test {
88 quickcheck! {
89 fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
90 if old_w == 0 || new_w == 0 { return true; }
91 if u64::from(new_w) * 400u64 >= u64::from(old_w) * u64::from(u32::MAX) { return true; }
94
95 let result = super::resize_dimensions(old_w, 400, new_w, u32::MAX, false);
96 let exact = (400_f64 * f64::from(new_w) / f64::from(old_w)).round() as u32;
97 result.0 == new_w && result.1 == exact.max(1)
98 }
99 }
100
101 quickcheck! {
102 fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
103 if old_h == 0 || new_h == 0 { return true; }
104 if 400u64 * u64::from(new_h) >= u64::from(old_h) * u64::from(u32::MAX) { return true; }
107
108 let result = super::resize_dimensions(400, old_h, u32::MAX, new_h, false);
109 let exact = (400_f64 * f64::from(new_h) / f64::from(old_h)).round() as u32;
110 result.1 == new_h && result.0 == exact.max(1)
111 }
112 }
113
114 #[test]
115 fn resize_handles_fill() {
116 let result = super::resize_dimensions(100, 200, 200, 500, true);
117 assert!(result.0 == 250);
118 assert!(result.1 == 500);
119
120 let result = super::resize_dimensions(200, 100, 500, 200, true);
121 assert!(result.0 == 500);
122 assert!(result.1 == 250);
123 }
124
125 #[test]
126 fn resize_never_rounds_to_zero() {
127 let result = super::resize_dimensions(1, 150, 128, 128, false);
128 assert!(result.0 > 0);
129 assert!(result.1 > 0);
130 }
131
132 #[test]
133 fn resize_handles_overflow() {
134 let result = super::resize_dimensions(100, u32::MAX, 200, u32::MAX, true);
135 assert!(result.0 == 100);
136 assert!(result.1 == u32::MAX);
137
138 let result = super::resize_dimensions(u32::MAX, 100, u32::MAX, 200, true);
139 assert!(result.0 == u32::MAX);
140 assert!(result.1 == 100);
141 }
142
143 #[test]
144 fn resize_rounds() {
145 let result = super::resize_dimensions(4264, 2476, 3840, 2160, true);
147 assert_eq!(result, (3840, 2230));
148
149 let result = super::resize_dimensions(2476, 4264, 2160, 3840, false);
150 assert_eq!(result, (2160, 3720));
151 }
152
153 #[test]
154 fn resize_handles_zero() {
155 let result = super::resize_dimensions(0, 100, 100, 100, false);
156 assert_eq!(result, (1, 100));
157
158 let result = super::resize_dimensions(100, 0, 100, 100, false);
159 assert_eq!(result, (100, 1));
160
161 let result = super::resize_dimensions(100, 100, 0, 100, false);
162 assert_eq!(result, (1, 1));
163
164 let result = super::resize_dimensions(100, 100, 100, 0, false);
165 assert_eq!(result, (1, 1));
166 }
167}