1use crate::media::{Stretch, StretchDirection};
2use crate::utilities::MathUtilities;
3use crate::{Size, Vector};
4
5pub struct MediaExtensions;
7
8impl MediaExtensions {
9 pub fn calculate_scaling(
19 stretch: Stretch,
20 destination_size: Size,
21 source_size: Size,
22 stretch_direction: StretchDirection,
23 ) -> Vector {
24 let mut scale_x = 1.0;
25 let mut scale_y = 1.0;
26
27 let is_constrained_width = destination_size.width != f64::INFINITY;
28 let is_constrained_height = destination_size.height != f64::INFINITY;
29
30 if (stretch == Stretch::Uniform || stretch == Stretch::UniformToFill || stretch == Stretch::Fill)
31 && (is_constrained_width || is_constrained_height)
32 {
33 scale_x =
35 if MathUtilities::is_zero(source_size.width) { 0.0 } else { destination_size.width / source_size.width };
36 scale_y = if MathUtilities::is_zero(source_size.height) {
37 0.0
38 } else {
39 destination_size.height / source_size.height
40 };
41
42 if !is_constrained_width {
43 scale_x = scale_y;
44 } else if !is_constrained_height {
45 scale_y = scale_x;
46 } else {
47 match stretch {
49 Stretch::Uniform => {
50 let minscale = if scale_x < scale_y { scale_x } else { scale_y };
52 scale_x = minscale;
53 scale_y = minscale;
54 }
55 Stretch::UniformToFill => {
56 let maxscale = if scale_x > scale_y { scale_x } else { scale_y };
58 scale_x = maxscale;
59 scale_y = maxscale;
60 }
61 Stretch::Fill => {
62 }
64 Stretch::None => {}
65 }
66 }
67
68 match stretch_direction {
73 StretchDirection::UpOnly => {
74 if scale_x < 1.0 {
75 scale_x = 1.0;
76 }
77 if scale_y < 1.0 {
78 scale_y = 1.0;
79 }
80 }
81 StretchDirection::DownOnly => {
82 if scale_x > 1.0 {
83 scale_x = 1.0;
84 }
85 if scale_y > 1.0 {
86 scale_y = 1.0;
87 }
88 }
89 StretchDirection::Both => {}
90 }
91 }
92
93 Vector::new(scale_x, scale_y)
94 }
95
96 pub fn calculate_size(
106 stretch: Stretch,
107 destination_size: Size,
108 source_size: Size,
109 stretch_direction: StretchDirection,
110 ) -> Size {
111 source_size * Self::calculate_scaling(stretch, destination_size, source_size, stretch_direction)
112 }
113}
114
115#[cfg(test)]
118mod tests {
119 use super::*;
120
121 const BOTH: StretchDirection = StretchDirection::Both;
122
123 #[test]
124 fn none_never_scales() {
125 let scale = MediaExtensions::calculate_scaling(Stretch::None, Size::new(200.0, 50.0), Size::new(100.0, 100.0), BOTH);
126 assert_eq!(Vector::new(1.0, 1.0), scale);
127 }
128
129 #[test]
130 fn fill_scales_each_axis_independently() {
131 let scale = MediaExtensions::calculate_scaling(Stretch::Fill, Size::new(200.0, 50.0), Size::new(100.0, 100.0), BOTH);
132 assert_eq!(Vector::new(2.0, 0.5), scale);
133 }
134
135 #[test]
136 fn uniform_uses_the_smaller_scale() {
137 let scale =
138 MediaExtensions::calculate_scaling(Stretch::Uniform, Size::new(200.0, 50.0), Size::new(100.0, 100.0), BOTH);
139 assert_eq!(Vector::new(0.5, 0.5), scale);
140 }
141
142 #[test]
143 fn uniform_to_fill_uses_the_larger_scale() {
144 let scale = MediaExtensions::calculate_scaling(
145 Stretch::UniformToFill,
146 Size::new(200.0, 50.0),
147 Size::new(100.0, 100.0),
148 BOTH,
149 );
150 assert_eq!(Vector::new(2.0, 2.0), scale);
151 }
152
153 #[test]
154 fn unconstrained_axis_copies_the_other_scale() {
155 let source = Size::new(100.0, 50.0);
156 for stretch in [Stretch::Fill, Stretch::Uniform, Stretch::UniformToFill] {
157 assert_eq!(
158 Vector::new(3.0, 3.0),
159 MediaExtensions::calculate_scaling(stretch, Size::new(f64::INFINITY, 150.0), source, BOTH)
160 );
161 assert_eq!(
162 Vector::new(4.0, 4.0),
163 MediaExtensions::calculate_scaling(stretch, Size::new(400.0, f64::INFINITY), source, BOTH)
164 );
165 assert_eq!(
166 Vector::new(1.0, 1.0),
167 MediaExtensions::calculate_scaling(stretch, Size::INFINITY, source, BOTH)
168 );
169 }
170 }
171
172 #[test]
173 fn zero_source_gives_zero_scale() {
174 let scale = MediaExtensions::calculate_scaling(Stretch::Fill, Size::new(200.0, 50.0), Size::new(0.0, 100.0), BOTH);
175 assert_eq!(Vector::new(0.0, 0.5), scale);
176
177 let scale =
178 MediaExtensions::calculate_scaling(Stretch::Uniform, Size::new(200.0, 50.0), Size::new(100.0, 0.0), BOTH);
179 assert_eq!(Vector::new(0.0, 0.0), scale);
180 }
181
182 #[test]
183 fn stretch_direction_bounds_the_scales() {
184 let destination = Size::new(200.0, 50.0);
185 let source = Size::new(100.0, 100.0);
186
187 assert_eq!(
188 Vector::new(2.0, 1.0),
189 MediaExtensions::calculate_scaling(Stretch::Fill, destination, source, StretchDirection::UpOnly)
190 );
191 assert_eq!(
192 Vector::new(1.0, 0.5),
193 MediaExtensions::calculate_scaling(Stretch::Fill, destination, source, StretchDirection::DownOnly)
194 );
195 assert_eq!(
196 Vector::new(1.0, 1.0),
197 MediaExtensions::calculate_scaling(Stretch::Uniform, destination, source, StretchDirection::UpOnly)
198 );
199 assert_eq!(
200 Vector::new(1.0, 1.0),
201 MediaExtensions::calculate_scaling(Stretch::UniformToFill, destination, source, StretchDirection::DownOnly)
202 );
203 }
204
205 #[test]
206 fn calculate_size_scales_the_source() {
207 let destination = Size::new(200.0, 50.0);
208 let source = Size::new(100.0, 80.0);
209
210 assert_eq!(source, MediaExtensions::calculate_size(Stretch::None, destination, source, BOTH));
211 assert_eq!(destination, MediaExtensions::calculate_size(Stretch::Fill, destination, source, BOTH));
212 assert_eq!(Size::new(62.5, 50.0), MediaExtensions::calculate_size(Stretch::Uniform, destination, source, BOTH));
213 assert_eq!(
214 Size::new(200.0, 160.0),
215 MediaExtensions::calculate_size(Stretch::UniformToFill, destination, source, BOTH)
216 );
217 }
218}