1 //! Shared mathematical utility functions.
2
3 /// Cut value to be inside given range
4 ///
5 /// ```
6 /// use image::math::utils;
7 ///
8 /// assert_eq!(utils::clamp(-5, 0, 10), 0);
9 /// assert_eq!(utils::clamp( 6, 0, 10), 6);
10 /// assert_eq!(utils::clamp(15, 0, 10), 10);
11 /// ```
12 #[inline]
13 #[deprecated]
clamp<N>(a: N, min: N, max: N) -> N where N: PartialOrd,14 pub fn clamp<N>(a: N, min: N, max: N) -> N
15 where
16 N: PartialOrd,
17 {
18 if a < min {
19 return min;
20 }
21 if a > max {
22 return max;
23 }
24 a
25 }
26
27 /// Calculates the width and height an image should be resized to.
28 /// This preserves aspect ratio, and based on the `fill` parameter
29 /// will either fill the dimensions to fit inside the smaller constraint
30 /// (will overflow the specified bounds on one axis to preserve
31 /// aspect ratio), or will shrink so that both dimensions are
32 /// completely contained with in the given `width` and `height`,
33 /// with empty space on one axis.
resize_dimensions(width: u32, height: u32, nwidth: u32, nheight: u32, fill: bool) -> (u32, u32)34 pub(crate) fn resize_dimensions(width: u32, height: u32, nwidth: u32, nheight: u32, fill: bool) -> (u32, u32) {
35 let ratio = u64::from(width) * u64::from(nheight);
36 let nratio = u64::from(nwidth) * u64::from(height);
37
38 let use_width = if fill {
39 nratio > ratio
40 } else {
41 nratio <= ratio
42 };
43 let intermediate = if use_width {
44 u64::from(height) * u64::from(nwidth) / u64::from(width)
45 } else {
46 u64::from(width) * u64::from(nheight) / u64::from(height)
47 };
48 let intermediate = std::cmp::max(1, intermediate);
49 if use_width {
50 if intermediate <= u64::from(::std::u32::MAX) {
51 (nwidth, intermediate as u32)
52 } else {
53 (
54 (u64::from(nwidth) * u64::from(::std::u32::MAX) / intermediate) as u32,
55 ::std::u32::MAX,
56 )
57 }
58 } else if intermediate <= u64::from(::std::u32::MAX) {
59 (intermediate as u32, nheight)
60 } else {
61 (
62 ::std::u32::MAX,
63 (u64::from(nheight) * u64::from(::std::u32::MAX) / intermediate) as u32,
64 )
65 }
66 }
67
68 #[cfg(test)]
69 mod test {
70 quickcheck! {
71 fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
72 if old_w == 0 || new_w == 0 { return true; }
73 let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
74 result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64) as u32
75 }
76 }
77
78 quickcheck! {
79 fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
80 if old_h == 0 || new_h == 0 { return true; }
81 let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
82 result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64) as u32
83 }
84 }
85
86 #[test]
resize_handles_fill()87 fn resize_handles_fill() {
88 let result = super::resize_dimensions(100, 200, 200, 500, true);
89 assert!(result.0 == 250);
90 assert!(result.1 == 500);
91
92 let result = super::resize_dimensions(200, 100, 500, 200, true);
93 assert!(result.0 == 500);
94 assert!(result.1 == 250);
95 }
96
97 #[test]
resize_never_rounds_to_zero()98 fn resize_never_rounds_to_zero() {
99 let result = super::resize_dimensions(1, 150, 128, 128, false);
100 assert!(result.0 > 0);
101 assert!(result.1 > 0);
102 }
103
104 #[test]
resize_handles_overflow()105 fn resize_handles_overflow() {
106 let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
107 assert!(result.0 == 100);
108 assert!(result.1 == ::std::u32::MAX);
109
110 let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
111 assert!(result.0 == ::std::u32::MAX);
112 assert!(result.1 == 100);
113 }
114 }
115