1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::any::type_name; 6 use std::fmt; 7 8 use euclid::point2; 9 use euclid::size2; 10 use euclid::Size2D; 11 use num_traits::NumCast; 12 use winapi::shared::windef::POINT; 13 use winapi::shared::windef::RECT; 14 15 use super::HostWindowSpace; 16 17 pub type Point = euclid::Point2D<i32, HostWindowSpace>; 18 pub type Rect = euclid::Rect<i32, HostWindowSpace>; 19 pub type Size = euclid::Size2D<i32, HostWindowSpace>; 20 21 pub trait SizeExtension { create_and_enforce_aspect_ratio( original_size: &Self, expected_aspect_ratio: f32, should_adjust_width: bool, ) -> Self22 fn create_and_enforce_aspect_ratio( 23 original_size: &Self, 24 expected_aspect_ratio: f32, 25 should_adjust_width: bool, 26 ) -> Self; get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self27 fn get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self; scale(&self, ratio: f32) -> Self28 fn scale(&self, ratio: f32) -> Self; transpose(&self) -> Self29 fn transpose(&self) -> Self; shorter_edge(&self) -> i3230 fn shorter_edge(&self) -> i32; aspect_ratio(&self) -> f3231 fn aspect_ratio(&self) -> f32; is_square(&self) -> bool32 fn is_square(&self) -> bool; is_landscape(&self) -> bool33 fn is_landscape(&self) -> bool; 34 } 35 36 impl SizeExtension for Size { create_and_enforce_aspect_ratio( original_size: &Self, expected_aspect_ratio: f32, should_adjust_width: bool, ) -> Self37 fn create_and_enforce_aspect_ratio( 38 original_size: &Self, 39 expected_aspect_ratio: f32, 40 should_adjust_width: bool, 41 ) -> Self { 42 let mut size = *original_size; 43 if should_adjust_width { 44 size.width = (size.height as f32 * expected_aspect_ratio).round() as i32; 45 } else { 46 size.height = (size.width as f32 / expected_aspect_ratio).round() as i32; 47 } 48 size 49 } 50 get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self51 fn get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self { 52 Size::create_and_enforce_aspect_ratio( 53 original_size, 54 expected_aspect_ratio, 55 /* should_adjust_width */ original_size.aspect_ratio() > expected_aspect_ratio, 56 ) 57 } 58 59 #[inline] scale(&self, ratio: f32) -> Self60 fn scale(&self, ratio: f32) -> Self { 61 size2( 62 (self.width as f32 * ratio) as i32, 63 (self.height as f32 * ratio) as i32, 64 ) 65 } 66 67 #[inline] transpose(&self) -> Self68 fn transpose(&self) -> Self { 69 size2(self.height, self.width) 70 } 71 72 #[inline] shorter_edge(&self) -> i3273 fn shorter_edge(&self) -> i32 { 74 std::cmp::min(self.width, self.height) 75 } 76 77 #[inline] aspect_ratio(&self) -> f3278 fn aspect_ratio(&self) -> f32 { 79 self.width as f32 / self.height as f32 80 } 81 82 #[inline] is_square(&self) -> bool83 fn is_square(&self) -> bool { 84 self.width == self.height 85 } 86 87 #[inline] is_landscape(&self) -> bool88 fn is_landscape(&self) -> bool { 89 self.width > self.height 90 } 91 } 92 93 pub trait RectExtension { to_sys_rect(&self) -> RECT94 fn to_sys_rect(&self) -> RECT; 95 } 96 97 impl RectExtension for Rect { 98 #[inline] to_sys_rect(&self) -> RECT99 fn to_sys_rect(&self) -> RECT { 100 RECT { 101 left: self.min_x(), 102 top: self.min_y(), 103 right: self.max_x(), 104 bottom: self.max_y(), 105 } 106 } 107 } 108 109 pub trait SysRectExtension { to_rect(&self) -> Rect110 fn to_rect(&self) -> Rect; 111 } 112 113 impl SysRectExtension for RECT { 114 #[inline] to_rect(&self) -> Rect115 fn to_rect(&self) -> Rect { 116 Rect::new( 117 point2(self.left, self.top), 118 size2(self.right - self.left, self.bottom - self.top), 119 ) 120 } 121 } 122 123 pub trait PointExtension { to_sys_point(&self) -> POINT124 fn to_sys_point(&self) -> POINT; 125 } 126 127 impl PointExtension for Point { 128 #[inline] to_sys_point(&self) -> POINT129 fn to_sys_point(&self) -> POINT { 130 POINT { 131 x: self.x, 132 y: self.y, 133 } 134 } 135 } 136 137 pub trait SysPointExtension { to_point(&self) -> Point138 fn to_point(&self) -> Point; 139 } 140 141 impl SysPointExtension for POINT { 142 #[inline] to_point(&self) -> Point143 fn to_point(&self) -> Point { 144 point2(self.x, self.y) 145 } 146 } 147 148 pub trait Size2DCheckedCast<U>: Sized { checked_cast<T: NumCast>(self) -> Size2D<T, U>149 fn checked_cast<T: NumCast>(self) -> Size2D<T, U>; 150 } 151 152 impl<T, U> Size2DCheckedCast<U> for Size2D<T, U> 153 where 154 T: NumCast + Copy + fmt::Debug, 155 { checked_cast<NewT: NumCast>(self) -> Size2D<NewT, U>156 fn checked_cast<NewT: NumCast>(self) -> Size2D<NewT, U> { 157 self.try_cast::<NewT>().unwrap_or_else(|| { 158 panic!( 159 "Cannot cast {:?} from {} to {}", 160 self, 161 type_name::<T>(), 162 type_name::<NewT>(), 163 ) 164 }) 165 } 166 } 167 168 #[cfg(test)] 169 mod tests { 170 use super::*; 171 172 #[test] largest_inner_rect_size_when_outer_is_wider()173 fn largest_inner_rect_size_when_outer_is_wider() { 174 assert_eq!( 175 Size::get_largest_inner_rect_size( 176 /* original_size */ &size2(1600, 900), 177 /* expected_aspect_ratio */ 0.5 178 ), 179 size2(450, 900) 180 ); 181 } 182 183 #[test] largest_inner_rect_size_when_outer_is_taller()184 fn largest_inner_rect_size_when_outer_is_taller() { 185 assert_eq!( 186 Size::get_largest_inner_rect_size( 187 /* original_size */ &size2(900, 1600), 188 /* expected_aspect_ratio */ 3.0 189 ), 190 size2(900, 300) 191 ); 192 } 193 } 194