Skip to main content

stipple_geometry/
physical.rs

1use crate::Size;
2
3/// The ratio of physical device pixels to logical pixels for a surface
4/// (e.g. `2.0` on a typical HiDPI "retina" display, `1.0` on a standard
5/// display). Always strictly positive.
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct ScaleFactor(f64);
8
9impl ScaleFactor {
10    /// The identity scale (1 physical pixel per logical pixel).
11    pub const IDENTITY: Self = Self(1.0);
12
13    /// Creates a scale factor, clamping non-finite or non-positive input to
14    /// [`ScaleFactor::IDENTITY`].
15    #[inline]
16    pub fn new(factor: f64) -> Self {
17        if factor.is_finite() && factor > 0.0 {
18            Self(factor)
19        } else {
20            Self::IDENTITY
21        }
22    }
23
24    #[inline]
25    pub fn get(self) -> f64 {
26        self.0
27    }
28
29    /// Convert a logical size to physical pixels, rounding to whole pixels.
30    #[inline]
31    pub fn to_physical(self, logical: Size) -> PhysicalSize {
32        PhysicalSize {
33            width: (logical.width * self.0).round().max(0.0) as u32,
34            height: (logical.height * self.0).round().max(0.0) as u32,
35        }
36    }
37
38    /// Convert a physical size back to logical pixels.
39    #[inline]
40    pub fn to_logical(self, physical: PhysicalSize) -> Size {
41        Size::new(
42            physical.width as f64 / self.0,
43            physical.height as f64 / self.0,
44        )
45    }
46}
47
48impl Default for ScaleFactor {
49    #[inline]
50    fn default() -> Self {
51        Self::IDENTITY
52    }
53}
54
55/// An integer pixel extent in **physical** device pixels. This is the unit the
56/// platform layer and the render surface allocate buffers in.
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub struct PhysicalSize {
59    pub width: u32,
60    pub height: u32,
61}
62
63impl PhysicalSize {
64    #[inline]
65    pub const fn new(width: u32, height: u32) -> Self {
66        Self { width, height }
67    }
68
69    /// Number of pixels (`width × height`).
70    #[inline]
71    pub const fn pixel_count(self) -> u64 {
72        self.width as u64 * self.height as u64
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn hidpi_roundtrip() {
82        let s = ScaleFactor::new(2.0);
83        assert_eq!(
84            s.to_physical(Size::new(100.0, 50.0)),
85            PhysicalSize::new(200, 100)
86        );
87        assert_eq!(
88            s.to_logical(PhysicalSize::new(200, 100)),
89            Size::new(100.0, 50.0)
90        );
91    }
92
93    #[test]
94    fn invalid_scale_falls_back_to_identity() {
95        assert_eq!(ScaleFactor::new(0.0), ScaleFactor::IDENTITY);
96        assert_eq!(ScaleFactor::new(f64::NAN), ScaleFactor::IDENTITY);
97        assert_eq!(ScaleFactor::new(-1.0), ScaleFactor::IDENTITY);
98    }
99}