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; get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self28 fn get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self; scale(&self, ratio: f32) -> Self29 fn scale(&self, ratio: f32) -> Self; transpose(&self) -> Self30 fn transpose(&self) -> Self; shorter_edge(&self) -> i3231 fn shorter_edge(&self) -> i32; aspect_ratio(&self) -> f3232 fn aspect_ratio(&self) -> f32; is_square(&self) -> bool33 fn is_square(&self) -> bool; is_landscape(&self) -> bool34 fn is_landscape(&self) -> bool; 35 } 36 37 impl SizeExtension for Size { create_and_enforce_aspect_ratio( original_size: &Self, expected_aspect_ratio: f32, should_adjust_width: bool, ) -> Self38 fn create_and_enforce_aspect_ratio( 39 original_size: &Self, 40 expected_aspect_ratio: f32, 41 should_adjust_width: bool, 42 ) -> Self { 43 let mut size = *original_size; 44 if should_adjust_width { 45 size.width = (size.height as f32 * expected_aspect_ratio).round() as i32; 46 } else { 47 size.height = (size.width as f32 / expected_aspect_ratio).round() as i32; 48 } 49 size 50 } 51 get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self52 fn get_largest_inner_rect_size(original_size: &Self, expected_aspect_ratio: f32) -> Self { 53 Size::create_and_enforce_aspect_ratio( 54 original_size, 55 expected_aspect_ratio, 56 /* should_adjust_width */ original_size.aspect_ratio() > expected_aspect_ratio, 57 ) 58 } 59 60 #[inline] scale(&self, ratio: f32) -> Self61 fn scale(&self, ratio: f32) -> Self { 62 size2( 63 (self.width as f32 * ratio) as i32, 64 (self.height as f32 * ratio) as i32, 65 ) 66 } 67 68 #[inline] transpose(&self) -> Self69 fn transpose(&self) -> Self { 70 size2(self.height, self.width) 71 } 72 73 #[inline] shorter_edge(&self) -> i3274 fn shorter_edge(&self) -> i32 { 75 std::cmp::min(self.width, self.height) 76 } 77 78 #[inline] aspect_ratio(&self) -> f3279 fn aspect_ratio(&self) -> f32 { 80 self.width as f32 / self.height as f32 81 } 82 83 #[inline] is_square(&self) -> bool84 fn is_square(&self) -> bool { 85 self.width == self.height 86 } 87 88 #[inline] is_landscape(&self) -> bool89 fn is_landscape(&self) -> bool { 90 self.width > self.height 91 } 92 } 93 94 pub trait RectExtension { to_sys_rect(&self) -> RECT95 fn to_sys_rect(&self) -> RECT; 96 } 97 98 impl RectExtension for Rect { 99 #[inline] to_sys_rect(&self) -> RECT100 fn to_sys_rect(&self) -> RECT { 101 RECT { 102 left: self.min_x(), 103 top: self.min_y(), 104 right: self.max_x(), 105 bottom: self.max_y(), 106 } 107 } 108 } 109 110 pub trait SysRectExtension { to_rect(&self) -> Rect111 fn to_rect(&self) -> Rect; 112 } 113 114 impl SysRectExtension for RECT { 115 #[inline] to_rect(&self) -> Rect116 fn to_rect(&self) -> Rect { 117 Rect::new( 118 point2(self.left, self.top), 119 size2(self.right - self.left, self.bottom - self.top), 120 ) 121 } 122 } 123 124 pub trait PointExtension { to_sys_point(&self) -> POINT125 fn to_sys_point(&self) -> POINT; 126 } 127 128 impl PointExtension for Point { 129 #[inline] to_sys_point(&self) -> POINT130 fn to_sys_point(&self) -> POINT { 131 POINT { 132 x: self.x, 133 y: self.y, 134 } 135 } 136 } 137 138 pub trait SysPointExtension { to_point(&self) -> Point139 fn to_point(&self) -> Point; as_mut_ptr(&mut self) -> LPPOINT140 fn as_mut_ptr(&mut self) -> LPPOINT; 141 } 142 143 impl SysPointExtension for POINT { 144 #[inline] to_point(&self) -> Point145 fn to_point(&self) -> Point { 146 point2(self.x, self.y) 147 } 148 149 #[inline] as_mut_ptr(&mut self) -> LPPOINT150 fn as_mut_ptr(&mut self) -> LPPOINT { 151 self as LPPOINT 152 } 153 } 154 155 pub trait Size2DCheckedCast<U>: Sized { checked_cast<T: NumCast>(self) -> Size2D<T, U>156 fn checked_cast<T: NumCast>(self) -> Size2D<T, U>; 157 } 158 159 impl<T, U> Size2DCheckedCast<U> for Size2D<T, U> 160 where 161 T: NumCast + Copy + fmt::Debug, 162 { checked_cast<NewT: NumCast>(self) -> Size2D<NewT, U>163 fn checked_cast<NewT: NumCast>(self) -> Size2D<NewT, U> { 164 self.try_cast::<NewT>().unwrap_or_else(|| { 165 panic!( 166 "Cannot cast {:?} from {} to {}", 167 self, 168 type_name::<T>(), 169 type_name::<NewT>(), 170 ) 171 }) 172 } 173 } 174 175 #[cfg(test)] 176 mod tests { 177 use super::*; 178 179 #[test] largest_inner_rect_size_when_outer_is_wider()180 fn largest_inner_rect_size_when_outer_is_wider() { 181 assert_eq!( 182 Size::get_largest_inner_rect_size( 183 /* original_size */ &size2(1600, 900), 184 /* expected_aspect_ratio */ 0.5 185 ), 186 size2(450, 900) 187 ); 188 } 189 190 #[test] largest_inner_rect_size_when_outer_is_taller()191 fn largest_inner_rect_size_when_outer_is_taller() { 192 assert_eq!( 193 Size::get_largest_inner_rect_size( 194 /* original_size */ &size2(900, 1600), 195 /* expected_aspect_ratio */ 3.0 196 ), 197 size2(900, 300) 198 ); 199 } 200 } 201