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::LPPOINT; 13 use winapi::shared::windef::POINT; 14 use winapi::shared::windef::RECT; 15 16 use super::HostWindowSpace; 17 18 pub type Point = euclid::Point2D<i32, HostWindowSpace>; 19 pub type Rect = euclid::Rect<i32, HostWindowSpace>; 20 pub type Size = euclid::Size2D<i32, HostWindowSpace>; 21 22 pub trait SizeExtension { create_and_enforce_aspect_ratio( original_size: &Self, expected_aspect_ratio: f32, should_adjust_width: bool, ) -> Self23 fn create_and_enforce_aspect_ratio( 24 original_size: &Self, 25 expected_aspect_ratio: f32, 26 should_adjust_width: bool, 27 ) -> Self; 28 #[allow(dead_code)] get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self29 fn get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self; 30 #[allow(dead_code)] scale(&self, ratio: f32) -> Self31 fn scale(&self, ratio: f32) -> Self; 32 #[allow(dead_code)] transpose(&self) -> Self33 fn transpose(&self) -> Self; 34 #[allow(dead_code)] shorter_edge(&self) -> i3235 fn shorter_edge(&self) -> i32; aspect_ratio(&self) -> f3236 fn aspect_ratio(&self) -> f32; 37 #[allow(dead_code)] is_square(&self) -> bool38 fn is_square(&self) -> bool; 39 #[allow(dead_code)] is_landscape(&self) -> bool40 fn is_landscape(&self) -> bool; 41 } 42 43 impl SizeExtension for Size { create_and_enforce_aspect_ratio( original_size: &Self, expected_aspect_ratio: f32, should_adjust_width: bool, ) -> Self44 fn create_and_enforce_aspect_ratio( 45 original_size: &Self, 46 expected_aspect_ratio: f32, 47 should_adjust_width: bool, 48 ) -> Self { 49 let mut size = *original_size; 50 if should_adjust_width { 51 size.width = (size.height as f32 * expected_aspect_ratio).round() as i32; 52 } else { 53 size.height = (size.width as f32 / expected_aspect_ratio).round() as i32; 54 } 55 size 56 } 57 get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self58 fn get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self { 59 Size::create_and_enforce_aspect_ratio( 60 original_size, 61 expected_aspect_ratio, 62 /* should_adjust_width */ original_size.aspect_ratio() > expected_aspect_ratio, 63 ) 64 } 65 66 #[inline] scale(&self, ratio: f32) -> Self67 fn scale(&self, ratio: f32) -> Self { 68 size2( 69 (self.width as f32 * ratio) as i32, 70 (self.height as f32 * ratio) as i32, 71 ) 72 } 73 74 #[inline] transpose(&self) -> Self75 fn transpose(&self) -> Self { 76 size2(self.height, self.width) 77 } 78 79 #[inline] shorter_edge(&self) -> i3280 fn shorter_edge(&self) -> i32 { 81 std::cmp::min(self.width, self.height) 82 } 83 84 #[inline] aspect_ratio(&self) -> f3285 fn aspect_ratio(&self) -> f32 { 86 self.width as f32 / self.height as f32 87 } 88 89 #[inline] is_square(&self) -> bool90 fn is_square(&self) -> bool { 91 self.width == self.height 92 } 93 94 #[inline] is_landscape(&self) -> bool95 fn is_landscape(&self) -> bool { 96 self.width > self.height 97 } 98 } 99 100 pub trait RectExtension { to_sys_rect(&self) -> RECT101 fn to_sys_rect(&self) -> RECT; 102 } 103 104 impl RectExtension for Rect { 105 #[inline] to_sys_rect(&self) -> RECT106 fn to_sys_rect(&self) -> RECT { 107 RECT { 108 left: self.min_x(), 109 top: self.min_y(), 110 right: self.max_x(), 111 bottom: self.max_y(), 112 } 113 } 114 } 115 116 pub trait SysRectExtension { to_rect(&self) -> Rect117 fn to_rect(&self) -> Rect; 118 } 119 120 impl SysRectExtension for RECT { 121 #[inline] to_rect(&self) -> Rect122 fn to_rect(&self) -> Rect { 123 Rect::new( 124 point2(self.left, self.top), 125 size2(self.right - self.left, self.bottom - self.top), 126 ) 127 } 128 } 129 130 pub trait PointExtension { to_sys_point(&self) -> POINT131 fn to_sys_point(&self) -> POINT; 132 } 133 134 impl PointExtension for Point { 135 #[inline] to_sys_point(&self) -> POINT136 fn to_sys_point(&self) -> POINT { 137 POINT { 138 x: self.x, 139 y: self.y, 140 } 141 } 142 } 143 144 pub trait SysPointExtension { to_point(&self) -> Point145 fn to_point(&self) -> Point; as_mut_ptr(&mut self) -> LPPOINT146 fn as_mut_ptr(&mut self) -> LPPOINT; 147 } 148 149 impl SysPointExtension for POINT { 150 #[inline] to_point(&self) -> Point151 fn to_point(&self) -> Point { 152 point2(self.x, self.y) 153 } 154 155 #[inline] as_mut_ptr(&mut self) -> LPPOINT156 fn as_mut_ptr(&mut self) -> LPPOINT { 157 self as LPPOINT 158 } 159 } 160 161 pub trait Size2DCheckedCast<U>: Sized { checked_cast<T: NumCast>(self) -> Size2D<T, U>162 fn checked_cast<T: NumCast>(self) -> Size2D<T, U>; 163 } 164 165 impl<T, U> Size2DCheckedCast<U> for Size2D<T, U> 166 where 167 T: NumCast + Copy + fmt::Debug, 168 { checked_cast<NewT: NumCast>(self) -> Size2D<NewT, U>169 fn checked_cast<NewT: NumCast>(self) -> Size2D<NewT, U> { 170 self.try_cast::<NewT>().unwrap_or_else(|| { 171 panic!( 172 "Cannot cast {:?} from {} to {}", 173 self, 174 type_name::<T>(), 175 type_name::<NewT>(), 176 ) 177 }) 178 } 179 } 180 181 #[cfg(test)] 182 mod tests { 183 use super::*; 184 185 #[test] largest_inner_rect_size_when_outer_is_wider()186 fn largest_inner_rect_size_when_outer_is_wider() { 187 assert_eq!( 188 Size::get_largest_inner_rect_size( 189 /* original_size */ &size2(1600, 900), 190 /* expected_aspect_ratio */ 0.5 191 ), 192 size2(450, 900) 193 ); 194 } 195 196 #[test] largest_inner_rect_size_when_outer_is_taller()197 fn largest_inner_rect_size_when_outer_is_taller() { 198 assert_eq!( 199 Size::get_largest_inner_rect_size( 200 /* original_size */ &size2(900, 1600), 201 /* expected_aspect_ratio */ 3.0 202 ), 203 size2(900, 300) 204 ); 205 } 206 } 207