1use std::fmt;
2
3#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
7#[serde(transparent)]
8pub struct PxDensity(f32);
9impl fmt::Debug for PxDensity {
10 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11 f.debug_struct("PxDensity")
12 .field(".ppi()", &self.ppi())
13 .field(".ppcm()", &self.ppcm())
14 .finish()
15 }
16}
17impl fmt::Display for PxDensity {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 if f.alternate() {
21 write!(f, "{:.0}ppcm", self.ppcm())
22 } else {
23 write!(f, "{:.0}ppi", self.ppi())
24 }
25 }
26}
27impl Default for PxDensity {
28 fn default() -> Self {
30 PxDensity::new_ppi(96.0)
31 }
32}
33impl PxDensity {
34 pub const CM_TO_INCH: f32 = 2.54;
36
37 pub fn new_ppi(pixels_per_inch: f32) -> Self {
39 Self::new_ppcm(pixels_per_inch / Self::CM_TO_INCH)
40 }
41
42 pub fn new_ppm(pixels_per_meter: f32) -> Self {
44 Self::new_ppcm(pixels_per_meter / 100.0)
45 }
46
47 pub fn new_ppcm(pixels_per_centimeter: f32) -> Self {
49 Self(pixels_per_centimeter)
50 }
51
52 pub fn ppi(self) -> f32 {
54 self.0 * Self::CM_TO_INCH
55 }
56
57 pub fn ppm(self) -> f32 {
59 self.0 * 100.0
60 }
61
62 pub fn ppcm(self) -> f32 {
64 self.0
65 }
66}
67impl PartialEq for PxDensity {
68 fn eq(&self, other: &Self) -> bool {
69 super::about_eq(self.0, other.0, 0.001)
70 }
71}
72impl Eq for PxDensity {}
73impl std::hash::Hash for PxDensity {
74 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
75 super::about_eq_hash(self.0, 0.001, state)
76 }
77}
78impl Ord for PxDensity {
79 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
80 super::about_eq_ord(self.0, other.0, 0.001)
81 }
82}
83impl PartialOrd for PxDensity {
84 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
85 Some(self.cmp(other))
86 }
87}
88impl std::str::FromStr for PxDensity {
91 type Err = std::num::ParseFloatError;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 for suffix in ["ppi", "dpi", ".ppi()", ".dpi()"] {
95 if let Some(s) = s.strip_suffix(suffix) {
96 return Ok(PxDensity::new_ppi(s.trim_end().parse()?));
97 }
98 }
99 for suffix in ["ppcm", ".ppcm()"] {
100 if let Some(s) = s.strip_suffix(suffix) {
101 return Ok(PxDensity::new_ppcm(s.parse()?));
102 }
103 }
104
105 Ok(PxDensity::new_ppi(s.parse()?))
106 }
107}
108
109pub trait PxDensityUnits {
122 fn ppi(self) -> PxDensity;
124 fn dpi(self) -> PxDensity
128 where
129 Self: Sized,
130 {
131 self.ppi()
132 }
133
134 fn ppcm(self) -> PxDensity;
136}
137impl PxDensityUnits for u32 {
138 fn ppi(self) -> PxDensity {
139 PxDensity::new_ppi(self as f32)
140 }
141
142 fn ppcm(self) -> PxDensity {
143 PxDensity::new_ppcm(self as f32)
144 }
145}
146impl PxDensityUnits for f32 {
147 fn ppi(self) -> PxDensity {
148 PxDensity::new_ppi(self)
149 }
150
151 fn ppcm(self) -> PxDensity {
152 PxDensity::new_ppcm(self)
153 }
154}
155
156pub type PxDensity2d = euclid::Size2D<PxDensity, PxDensity>;