dpi/
lib.rs

1//! # DPI
2//!
3//! ## Why should I care about UI scaling?
4//!
5//! Modern computer screens don't have a consistent relationship between resolution and size.
6//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens
7//! typically being less than a quarter the size of their desktop counterparts. Moreover, neither
8//! desktop nor mobile screens have consistent resolutions within their own size classes - common
9//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K
10//! and beyond.
11//!
12//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with
13//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen and
14//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up
15//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially
16//! problematic with text rendering, where quarter-sized text becomes a significant legibility
17//! problem.
18//!
19//! Failure to account for the scale factor can create a significantly degraded user experience.
20//! Most notably, it can make users feel like they have bad eyesight, which will potentially cause
21//! them to think about growing elderly, resulting in them having an existential crisis. Once users
22//! enter that state, they will no longer be focused on your application.
23//!
24//! ## How should I handle it?
25//!
26//! The solution to this problem is to account for the device's *scale factor*. The scale factor is
27//! the factor UI elements should be scaled by to be consistent with the rest of the user's system -
28//! for example, a button that's usually 50 pixels across would be 100 pixels across on a device
29//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`.
30//!
31//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's
32//! usually a mistake since there's no consistent mapping between the scale factor and the screen's
33//! actual DPI. Unless printing to a physical medium, you should work in scaled pixels rather
34//! than any DPI-dependent units.
35//!
36//! ### Position and Size types
37//!
38//! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual pixels on the
39//! device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types correspond to the physical pixels
40//! divided by the scale factor.
41//!
42//! The position and size types are generic over their exact pixel type, `P`, to allow the
43//! API to have integer precision where appropriate (e.g. most window manipulation functions) and
44//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
45//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
46//! will truncate the fractional part of the float rather than properly round to the nearest
47//! integer. Use the provided `cast` function or [`From`]/[`Into`] conversions, which handle the
48//! rounding properly. Note that precision loss will still occur when rounding from a float to an
49//! int, although rounding lessens the problem.
50//!
51//! ## Cargo Features
52//!
53//! This crate provides the following Cargo features:
54//!
55//! * `serde`: Enables serialization/deserialization of certain types with
56//!   [Serde](https://crates.io/crates/serde).
57//! * `mint`: Enables mint (math interoperability standard types) conversions.
58//!
59//!
60//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
61//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
62
63#![cfg_attr(
64    docsrs,
65    feature(doc_auto_cfg, doc_cfg_hide),
66    doc(cfg_hide(doc, docsrs))
67)]
68#![forbid(unsafe_code)]
69
70#[cfg(feature = "serde")]
71use serde::{Deserialize, Serialize};
72
73pub trait Pixel: Copy + Into<f64> {
74    fn from_f64(f: f64) -> Self;
75    fn cast<P: Pixel>(self) -> P {
76        P::from_f64(self.into())
77    }
78}
79
80impl Pixel for u8 {
81    fn from_f64(f: f64) -> Self {
82        f.round() as u8
83    }
84}
85impl Pixel for u16 {
86    fn from_f64(f: f64) -> Self {
87        f.round() as u16
88    }
89}
90impl Pixel for u32 {
91    fn from_f64(f: f64) -> Self {
92        f.round() as u32
93    }
94}
95impl Pixel for i8 {
96    fn from_f64(f: f64) -> Self {
97        f.round() as i8
98    }
99}
100impl Pixel for i16 {
101    fn from_f64(f: f64) -> Self {
102        f.round() as i16
103    }
104}
105impl Pixel for i32 {
106    fn from_f64(f: f64) -> Self {
107        f.round() as i32
108    }
109}
110impl Pixel for f32 {
111    fn from_f64(f: f64) -> Self {
112        f as f32
113    }
114}
115impl Pixel for f64 {
116    fn from_f64(f: f64) -> Self {
117        f
118    }
119}
120
121/// Checks that the scale factor is a normal positive `f64`.
122///
123/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
124/// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
125/// otherwise, you risk panics.
126#[inline]
127pub fn validate_scale_factor(scale_factor: f64) -> bool {
128    scale_factor.is_sign_positive() && scale_factor.is_normal()
129}
130
131/// A logical pixel unit.
132#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134pub struct LogicalUnit<P>(pub P);
135
136impl<P> LogicalUnit<P> {
137    /// Represents a minimum logical unit of [`f64::MAX`].
138    pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
139    /// Represents a logical unit of `0_f64`.
140    pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
141    /// Represents a maximum logical unit that is equal to [`f64::MAX`].
142    pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
143
144    #[inline]
145    pub const fn new(v: P) -> Self {
146        LogicalUnit(v)
147    }
148}
149
150impl<P: Pixel> LogicalUnit<P> {
151    #[inline]
152    pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
153        physical: T,
154        scale_factor: f64,
155    ) -> Self {
156        physical.into().to_logical(scale_factor)
157    }
158
159    #[inline]
160    pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
161        assert!(validate_scale_factor(scale_factor));
162        PhysicalUnit::new(self.0.into() * scale_factor).cast()
163    }
164
165    #[inline]
166    pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
167        LogicalUnit(self.0.cast())
168    }
169}
170
171impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
172    fn from(v: X) -> LogicalUnit<P> {
173        LogicalUnit::new(v.cast())
174    }
175}
176
177impl<P: Pixel> From<LogicalUnit<P>> for u8 {
178    fn from(v: LogicalUnit<P>) -> u8 {
179        v.0.cast()
180    }
181}
182
183impl<P: Pixel> From<LogicalUnit<P>> for u16 {
184    fn from(v: LogicalUnit<P>) -> u16 {
185        v.0.cast()
186    }
187}
188
189impl<P: Pixel> From<LogicalUnit<P>> for u32 {
190    fn from(v: LogicalUnit<P>) -> u32 {
191        v.0.cast()
192    }
193}
194
195impl<P: Pixel> From<LogicalUnit<P>> for i8 {
196    fn from(v: LogicalUnit<P>) -> i8 {
197        v.0.cast()
198    }
199}
200
201impl<P: Pixel> From<LogicalUnit<P>> for i16 {
202    fn from(v: LogicalUnit<P>) -> i16 {
203        v.0.cast()
204    }
205}
206
207impl<P: Pixel> From<LogicalUnit<P>> for i32 {
208    fn from(v: LogicalUnit<P>) -> i32 {
209        v.0.cast()
210    }
211}
212
213impl<P: Pixel> From<LogicalUnit<P>> for f32 {
214    fn from(v: LogicalUnit<P>) -> f32 {
215        v.0.cast()
216    }
217}
218
219impl<P: Pixel> From<LogicalUnit<P>> for f64 {
220    fn from(v: LogicalUnit<P>) -> f64 {
221        v.0.cast()
222    }
223}
224
225/// A physical pixel unit.
226#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
227#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
228pub struct PhysicalUnit<P>(pub P);
229
230impl<P> PhysicalUnit<P> {
231    /// Represents a minimum physical unit of [`f64::MAX`].
232    pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
233    /// Represents a physical unit of `0_f64`.
234    pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
235    /// Represents a maximum physical unit that is equal to [`f64::MAX`].
236    pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
237
238    #[inline]
239    pub const fn new(v: P) -> Self {
240        PhysicalUnit(v)
241    }
242}
243
244impl<P: Pixel> PhysicalUnit<P> {
245    #[inline]
246    pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
247        logical.into().to_physical(scale_factor)
248    }
249
250    #[inline]
251    pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
252        assert!(validate_scale_factor(scale_factor));
253        LogicalUnit::new(self.0.into() / scale_factor).cast()
254    }
255
256    #[inline]
257    pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
258        PhysicalUnit(self.0.cast())
259    }
260}
261
262impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
263    fn from(v: X) -> PhysicalUnit<P> {
264        PhysicalUnit::new(v.cast())
265    }
266}
267
268impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
269    fn from(v: PhysicalUnit<P>) -> u8 {
270        v.0.cast()
271    }
272}
273
274impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
275    fn from(v: PhysicalUnit<P>) -> u16 {
276        v.0.cast()
277    }
278}
279
280impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
281    fn from(v: PhysicalUnit<P>) -> u32 {
282        v.0.cast()
283    }
284}
285
286impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
287    fn from(v: PhysicalUnit<P>) -> i8 {
288        v.0.cast()
289    }
290}
291
292impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
293    fn from(v: PhysicalUnit<P>) -> i16 {
294        v.0.cast()
295    }
296}
297
298impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
299    fn from(v: PhysicalUnit<P>) -> i32 {
300        v.0.cast()
301    }
302}
303
304impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
305    fn from(v: PhysicalUnit<P>) -> f32 {
306        v.0.cast()
307    }
308}
309
310impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
311    fn from(v: PhysicalUnit<P>) -> f64 {
312        v.0.cast()
313    }
314}
315
316/// A pixel unit that's either physical or logical.
317#[derive(Debug, Copy, Clone, PartialEq)]
318#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
319pub enum PixelUnit {
320    Physical(PhysicalUnit<i32>),
321    Logical(LogicalUnit<f64>),
322}
323
324impl PixelUnit {
325    /// Represents a minimum logical unit of [`f64::MAX`].
326    pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
327    /// Represents a logical unit of `0_f64`.
328    pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
329    /// Represents a maximum logical unit that is equal to [`f64::MAX`].
330    pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
331
332    pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
333        unit.into()
334    }
335
336    pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
337        match *self {
338            PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
339            PixelUnit::Logical(unit) => unit.cast(),
340        }
341    }
342
343    pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
344        match *self {
345            PixelUnit::Physical(unit) => unit.cast(),
346            PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
347        }
348    }
349}
350
351impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
352    #[inline]
353    fn from(unit: PhysicalUnit<P>) -> PixelUnit {
354        PixelUnit::Physical(unit.cast())
355    }
356}
357
358impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
359    #[inline]
360    fn from(unit: LogicalUnit<P>) -> PixelUnit {
361        PixelUnit::Logical(unit.cast())
362    }
363}
364
365/// A position represented in logical pixels.
366///
367/// The position is stored as floats, so please be careful. Casting floats to integers truncates the
368/// fractional part, which can cause noticeable issues. To help with that, an `Into<(i32, i32)>`
369/// implementation is provided which does the rounding for you.
370#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
371#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
372pub struct LogicalPosition<P> {
373    pub x: P,
374    pub y: P,
375}
376
377impl<P> LogicalPosition<P> {
378    #[inline]
379    pub const fn new(x: P, y: P) -> Self {
380        LogicalPosition { x, y }
381    }
382}
383
384impl<P: Pixel> LogicalPosition<P> {
385    #[inline]
386    pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
387        physical: T,
388        scale_factor: f64,
389    ) -> Self {
390        physical.into().to_logical(scale_factor)
391    }
392
393    #[inline]
394    pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
395        assert!(validate_scale_factor(scale_factor));
396        let x = self.x.into() * scale_factor;
397        let y = self.y.into() * scale_factor;
398        PhysicalPosition::new(x, y).cast()
399    }
400
401    #[inline]
402    pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
403        LogicalPosition {
404            x: self.x.cast(),
405            y: self.y.cast(),
406        }
407    }
408}
409
410impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
411    fn from((x, y): (X, X)) -> LogicalPosition<P> {
412        LogicalPosition::new(x.cast(), y.cast())
413    }
414}
415
416impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
417    fn from(p: LogicalPosition<P>) -> (X, X) {
418        (p.x.cast(), p.y.cast())
419    }
420}
421
422impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
423    fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
424        LogicalPosition::new(x.cast(), y.cast())
425    }
426}
427
428impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
429    fn from(p: LogicalPosition<P>) -> [X; 2] {
430        [p.x.cast(), p.y.cast()]
431    }
432}
433
434#[cfg(feature = "mint")]
435impl<P: Pixel> From<mint::Point2<P>> for LogicalPosition<P> {
436    fn from(p: mint::Point2<P>) -> Self {
437        Self::new(p.x, p.y)
438    }
439}
440
441#[cfg(feature = "mint")]
442impl<P: Pixel> From<LogicalPosition<P>> for mint::Point2<P> {
443    fn from(p: LogicalPosition<P>) -> Self {
444        mint::Point2 { x: p.x, y: p.y }
445    }
446}
447
448/// A position represented in physical pixels.
449#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
450#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
451pub struct PhysicalPosition<P> {
452    pub x: P,
453    pub y: P,
454}
455
456impl<P> PhysicalPosition<P> {
457    #[inline]
458    pub const fn new(x: P, y: P) -> Self {
459        PhysicalPosition { x, y }
460    }
461}
462
463impl<P: Pixel> PhysicalPosition<P> {
464    #[inline]
465    pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
466        logical: T,
467        scale_factor: f64,
468    ) -> Self {
469        logical.into().to_physical(scale_factor)
470    }
471
472    #[inline]
473    pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
474        assert!(validate_scale_factor(scale_factor));
475        let x = self.x.into() / scale_factor;
476        let y = self.y.into() / scale_factor;
477        LogicalPosition::new(x, y).cast()
478    }
479
480    #[inline]
481    pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
482        PhysicalPosition {
483            x: self.x.cast(),
484            y: self.y.cast(),
485        }
486    }
487}
488
489impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
490    fn from((x, y): (X, X)) -> PhysicalPosition<P> {
491        PhysicalPosition::new(x.cast(), y.cast())
492    }
493}
494
495impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
496    fn from(p: PhysicalPosition<P>) -> (X, X) {
497        (p.x.cast(), p.y.cast())
498    }
499}
500
501impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
502    fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
503        PhysicalPosition::new(x.cast(), y.cast())
504    }
505}
506
507impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
508    fn from(p: PhysicalPosition<P>) -> [X; 2] {
509        [p.x.cast(), p.y.cast()]
510    }
511}
512
513#[cfg(feature = "mint")]
514impl<P: Pixel> From<mint::Point2<P>> for PhysicalPosition<P> {
515    fn from(p: mint::Point2<P>) -> Self {
516        Self::new(p.x, p.y)
517    }
518}
519
520#[cfg(feature = "mint")]
521impl<P: Pixel> From<PhysicalPosition<P>> for mint::Point2<P> {
522    fn from(p: PhysicalPosition<P>) -> Self {
523        mint::Point2 { x: p.x, y: p.y }
524    }
525}
526
527/// A size represented in logical pixels.
528#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
529#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
530pub struct LogicalSize<P> {
531    pub width: P,
532    pub height: P,
533}
534
535impl<P> LogicalSize<P> {
536    #[inline]
537    pub const fn new(width: P, height: P) -> Self {
538        LogicalSize { width, height }
539    }
540}
541
542impl<P: Pixel> LogicalSize<P> {
543    #[inline]
544    pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(
545        physical: T,
546        scale_factor: f64,
547    ) -> Self {
548        physical.into().to_logical(scale_factor)
549    }
550
551    #[inline]
552    pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
553        assert!(validate_scale_factor(scale_factor));
554        let width = self.width.into() * scale_factor;
555        let height = self.height.into() * scale_factor;
556        PhysicalSize::new(width, height).cast()
557    }
558
559    #[inline]
560    pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
561        LogicalSize {
562            width: self.width.cast(),
563            height: self.height.cast(),
564        }
565    }
566}
567
568impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
569    fn from((x, y): (X, X)) -> LogicalSize<P> {
570        LogicalSize::new(x.cast(), y.cast())
571    }
572}
573
574impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
575    fn from(s: LogicalSize<P>) -> (X, X) {
576        (s.width.cast(), s.height.cast())
577    }
578}
579
580impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
581    fn from([x, y]: [X; 2]) -> LogicalSize<P> {
582        LogicalSize::new(x.cast(), y.cast())
583    }
584}
585
586impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
587    fn from(s: LogicalSize<P>) -> [X; 2] {
588        [s.width.cast(), s.height.cast()]
589    }
590}
591
592#[cfg(feature = "mint")]
593impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
594    fn from(v: mint::Vector2<P>) -> Self {
595        Self::new(v.x, v.y)
596    }
597}
598
599#[cfg(feature = "mint")]
600impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
601    fn from(s: LogicalSize<P>) -> Self {
602        mint::Vector2 {
603            x: s.width,
604            y: s.height,
605        }
606    }
607}
608
609/// A size represented in physical pixels.
610#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
611#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
612pub struct PhysicalSize<P> {
613    pub width: P,
614    pub height: P,
615}
616
617impl<P> PhysicalSize<P> {
618    #[inline]
619    pub const fn new(width: P, height: P) -> Self {
620        PhysicalSize { width, height }
621    }
622}
623
624impl<P: Pixel> PhysicalSize<P> {
625    #[inline]
626    pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
627        logical.into().to_physical(scale_factor)
628    }
629
630    #[inline]
631    pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
632        assert!(validate_scale_factor(scale_factor));
633        let width = self.width.into() / scale_factor;
634        let height = self.height.into() / scale_factor;
635        LogicalSize::new(width, height).cast()
636    }
637
638    #[inline]
639    pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
640        PhysicalSize {
641            width: self.width.cast(),
642            height: self.height.cast(),
643        }
644    }
645}
646
647impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
648    fn from((x, y): (X, X)) -> PhysicalSize<P> {
649        PhysicalSize::new(x.cast(), y.cast())
650    }
651}
652
653impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
654    fn from(s: PhysicalSize<P>) -> (X, X) {
655        (s.width.cast(), s.height.cast())
656    }
657}
658
659impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
660    fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
661        PhysicalSize::new(x.cast(), y.cast())
662    }
663}
664
665impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
666    fn from(s: PhysicalSize<P>) -> [X; 2] {
667        [s.width.cast(), s.height.cast()]
668    }
669}
670
671#[cfg(feature = "mint")]
672impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
673    fn from(v: mint::Vector2<P>) -> Self {
674        Self::new(v.x, v.y)
675    }
676}
677
678#[cfg(feature = "mint")]
679impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
680    fn from(s: PhysicalSize<P>) -> Self {
681        mint::Vector2 {
682            x: s.width,
683            y: s.height,
684        }
685    }
686}
687
688/// A size that's either physical or logical.
689#[derive(Debug, Copy, Clone, PartialEq)]
690#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
691pub enum Size {
692    Physical(PhysicalSize<u32>),
693    Logical(LogicalSize<f64>),
694}
695
696impl Size {
697    pub fn new<S: Into<Size>>(size: S) -> Size {
698        size.into()
699    }
700
701    pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
702        match *self {
703            Size::Physical(size) => size.to_logical(scale_factor),
704            Size::Logical(size) => size.cast(),
705        }
706    }
707
708    pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
709        match *self {
710            Size::Physical(size) => size.cast(),
711            Size::Logical(size) => size.to_physical(scale_factor),
712        }
713    }
714
715    pub fn clamp<S: Into<Size>>(input: S, min: S, max: S, scale_factor: f64) -> Size {
716        let (input, min, max) = (
717            input.into().to_physical::<f64>(scale_factor),
718            min.into().to_physical::<f64>(scale_factor),
719            max.into().to_physical::<f64>(scale_factor),
720        );
721
722        let width = input.width.clamp(min.width, max.width);
723        let height = input.height.clamp(min.height, max.height);
724
725        PhysicalSize::new(width, height).into()
726    }
727}
728
729impl<P: Pixel> From<PhysicalSize<P>> for Size {
730    #[inline]
731    fn from(size: PhysicalSize<P>) -> Size {
732        Size::Physical(size.cast())
733    }
734}
735
736impl<P: Pixel> From<LogicalSize<P>> for Size {
737    #[inline]
738    fn from(size: LogicalSize<P>) -> Size {
739        Size::Logical(size.cast())
740    }
741}
742
743/// A position that's either physical or logical.
744#[derive(Debug, Copy, Clone, PartialEq)]
745#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
746pub enum Position {
747    Physical(PhysicalPosition<i32>),
748    Logical(LogicalPosition<f64>),
749}
750
751impl Position {
752    pub fn new<S: Into<Position>>(position: S) -> Position {
753        position.into()
754    }
755
756    pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
757        match *self {
758            Position::Physical(position) => position.to_logical(scale_factor),
759            Position::Logical(position) => position.cast(),
760        }
761    }
762
763    pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
764        match *self {
765            Position::Physical(position) => position.cast(),
766            Position::Logical(position) => position.to_physical(scale_factor),
767        }
768    }
769}
770
771impl<P: Pixel> From<PhysicalPosition<P>> for Position {
772    #[inline]
773    fn from(position: PhysicalPosition<P>) -> Position {
774        Position::Physical(position.cast())
775    }
776}
777
778impl<P: Pixel> From<LogicalPosition<P>> for Position {
779    #[inline]
780    fn from(position: LogicalPosition<P>) -> Position {
781        Position::Logical(position.cast())
782    }
783}
784
785#[cfg(test)]
786mod tests {
787    use super::*;
788    use std::collections::HashSet;
789
790    macro_rules! test_pixel_int_impl {
791        ($($name:ident => $ty:ty),*) => {$(
792            #[test]
793            fn $name() {
794                assert_eq!(
795                    <$ty as Pixel>::from_f64(37.0),
796                    37,
797                );
798                assert_eq!(
799                    <$ty as Pixel>::from_f64(37.4),
800                    37,
801                );
802                assert_eq!(
803                    <$ty as Pixel>::from_f64(37.5),
804                    38,
805                );
806                assert_eq!(
807                    <$ty as Pixel>::from_f64(37.9),
808                    38,
809                );
810
811                assert_eq!(
812                    <$ty as Pixel>::cast::<u8>(37),
813                    37,
814                );
815                assert_eq!(
816                    <$ty as Pixel>::cast::<u16>(37),
817                    37,
818                );
819                assert_eq!(
820                    <$ty as Pixel>::cast::<u32>(37),
821                    37,
822                );
823                assert_eq!(
824                    <$ty as Pixel>::cast::<i8>(37),
825                    37,
826                );
827                assert_eq!(
828                    <$ty as Pixel>::cast::<i16>(37),
829                    37,
830                );
831                assert_eq!(
832                    <$ty as Pixel>::cast::<i32>(37),
833                    37,
834                );
835            }
836        )*};
837    }
838
839    test_pixel_int_impl! {
840        test_pixel_int_u8 => u8,
841        test_pixel_int_u16 => u16,
842        test_pixel_int_u32 => u32,
843        test_pixel_int_i8 => i8,
844        test_pixel_int_i16 => i16
845    }
846
847    macro_rules! assert_approx_eq {
848        ($a:expr, $b:expr $(,)?) => {
849            assert!(
850                ($a - $b).abs() < 0.001,
851                "{} is not approximately equal to {}",
852                $a,
853                $b
854            );
855        };
856    }
857
858    macro_rules! test_pixel_float_impl {
859    ($($name:ident => $ty:ty),*) => {$(
860        #[test]
861        fn $name() {
862            assert_approx_eq!(
863                <$ty as Pixel>::from_f64(37.0),
864                37.0,
865            );
866            assert_approx_eq!(
867                <$ty as Pixel>::from_f64(37.4),
868                37.4,
869            );
870            assert_approx_eq!(
871                <$ty as Pixel>::from_f64(37.5),
872                37.5,
873            );
874            assert_approx_eq!(
875                <$ty as Pixel>::from_f64(37.9),
876                37.9,
877            );
878
879            assert_eq!(
880                <$ty as Pixel>::cast::<u8>(37.0),
881                37,
882            );
883            assert_eq!(
884                <$ty as Pixel>::cast::<u8>(37.4),
885                37,
886            );
887            assert_eq!(
888                <$ty as Pixel>::cast::<u8>(37.5),
889                38,
890            );
891
892            assert_eq!(
893                <$ty as Pixel>::cast::<u16>(37.0),
894                37,
895            );
896            assert_eq!(
897                <$ty as Pixel>::cast::<u16>(37.4),
898                37,
899            );
900            assert_eq!(
901                <$ty as Pixel>::cast::<u16>(37.5),
902                38,
903            );
904
905            assert_eq!(
906                <$ty as Pixel>::cast::<u32>(37.0),
907                37,
908            );
909            assert_eq!(
910                <$ty as Pixel>::cast::<u32>(37.4),
911                37,
912            );
913            assert_eq!(
914                <$ty as Pixel>::cast::<u32>(37.5),
915                38,
916            );
917
918            assert_eq!(
919                <$ty as Pixel>::cast::<i8>(37.0),
920                37,
921            );
922            assert_eq!(
923                <$ty as Pixel>::cast::<i8>(37.4),
924                37,
925            );
926            assert_eq!(
927                <$ty as Pixel>::cast::<i8>(37.5),
928                38,
929            );
930
931            assert_eq!(
932                <$ty as Pixel>::cast::<i16>(37.0),
933                37,
934            );
935            assert_eq!(
936                <$ty as Pixel>::cast::<i16>(37.4),
937                37,
938            );
939            assert_eq!(
940                <$ty as Pixel>::cast::<i16>(37.5),
941                38,
942            );
943        }
944    )*};
945}
946
947    test_pixel_float_impl! {
948        test_pixel_float_f32 => f32,
949        test_pixel_float_f64 => f64
950    }
951
952    #[test]
953    fn test_validate_scale_factor() {
954        assert!(validate_scale_factor(1.0));
955        assert!(validate_scale_factor(2.0));
956        assert!(validate_scale_factor(3.0));
957        assert!(validate_scale_factor(1.5));
958        assert!(validate_scale_factor(0.5));
959
960        assert!(!validate_scale_factor(0.0));
961        assert!(!validate_scale_factor(-1.0));
962        assert!(!validate_scale_factor(f64::INFINITY));
963        assert!(!validate_scale_factor(f64::NAN));
964        assert!(!validate_scale_factor(f64::NEG_INFINITY));
965    }
966
967    #[test]
968    fn test_logical_unity() {
969        let log_unit = LogicalUnit::new(1.0);
970        assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1));
971        assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2));
972        assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1));
973        assert_eq!(
974            log_unit,
975            LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0)
976        );
977        assert_eq!(
978            log_unit,
979            LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0)
980        );
981        assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0));
982
983        let x: f64 = log_unit.into();
984        assert_eq!(x, 1.0);
985    }
986
987    #[test]
988    fn test_physical_unit() {
989        assert_eq!(
990            PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0),
991            PhysicalUnit::new(1)
992        );
993        assert_eq!(
994            PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5),
995            PhysicalUnit::new(1)
996        );
997        assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,));
998        assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0));
999
1000        let x: f64 = PhysicalUnit::new(1).into();
1001        assert_eq!(x, 1.0);
1002    }
1003
1004    #[test]
1005    fn test_logical_position() {
1006        let log_pos = LogicalPosition::new(1.0, 2.0);
1007        assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2));
1008        assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4));
1009        assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2));
1010        assert_eq!(
1011            log_pos,
1012            LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0)
1013        );
1014        assert_eq!(
1015            log_pos,
1016            LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0)
1017        );
1018        assert_eq!(
1019            LogicalPosition::from((2.0, 2.0)),
1020            LogicalPosition::new(2.0, 2.0)
1021        );
1022        assert_eq!(
1023            LogicalPosition::from([2.0, 3.0]),
1024            LogicalPosition::new(2.0, 3.0)
1025        );
1026
1027        let x: (f64, f64) = log_pos.into();
1028        assert_eq!(x, (1.0, 2.0));
1029        let x: [f64; 2] = log_pos.into();
1030        assert_eq!(x, [1.0, 2.0]);
1031    }
1032
1033    #[test]
1034    fn test_physical_position() {
1035        assert_eq!(
1036            PhysicalPosition::from_logical(LogicalPosition::new(1.0, 2.0), 1.0),
1037            PhysicalPosition::new(1, 2)
1038        );
1039        assert_eq!(
1040            PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5),
1041            PhysicalPosition::new(1, 2)
1042        );
1043        assert_eq!(
1044            PhysicalPosition::from((2.0, 2.0)),
1045            PhysicalPosition::new(2.0, 2.0)
1046        );
1047        assert_eq!(
1048            PhysicalPosition::from([2.0, 3.0]),
1049            PhysicalPosition::new(2.0, 3.0)
1050        );
1051
1052        let x: (f64, f64) = PhysicalPosition::new(1, 2).into();
1053        assert_eq!(x, (1.0, 2.0));
1054        let x: [f64; 2] = PhysicalPosition::new(1, 2).into();
1055        assert_eq!(x, [1.0, 2.0]);
1056    }
1057
1058    #[test]
1059    fn test_logical_size() {
1060        let log_size = LogicalSize::new(1.0, 2.0);
1061        assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2));
1062        assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4));
1063        assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2));
1064        assert_eq!(
1065            log_size,
1066            LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0)
1067        );
1068        assert_eq!(
1069            log_size,
1070            LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0)
1071        );
1072        assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0));
1073        assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0));
1074
1075        let x: (f64, f64) = log_size.into();
1076        assert_eq!(x, (1.0, 2.0));
1077        let x: [f64; 2] = log_size.into();
1078        assert_eq!(x, [1.0, 2.0]);
1079    }
1080
1081    #[test]
1082    fn test_physical_size() {
1083        assert_eq!(
1084            PhysicalSize::from_logical(LogicalSize::new(1.0, 2.0), 1.0),
1085            PhysicalSize::new(1, 2)
1086        );
1087        assert_eq!(
1088            PhysicalSize::from_logical(LogicalSize::new(2.0, 4.0), 0.5),
1089            PhysicalSize::new(1, 2)
1090        );
1091        assert_eq!(PhysicalSize::from((2.0, 2.0)), PhysicalSize::new(2.0, 2.0));
1092        assert_eq!(PhysicalSize::from([2.0, 3.0]), PhysicalSize::new(2.0, 3.0));
1093
1094        let x: (f64, f64) = PhysicalSize::new(1, 2).into();
1095        assert_eq!(x, (1.0, 2.0));
1096        let x: [f64; 2] = PhysicalSize::new(1, 2).into();
1097        assert_eq!(x, [1.0, 2.0]);
1098    }
1099
1100    #[test]
1101    fn test_size() {
1102        assert_eq!(
1103            Size::new(PhysicalSize::new(1, 2)),
1104            Size::Physical(PhysicalSize::new(1, 2))
1105        );
1106        assert_eq!(
1107            Size::new(LogicalSize::new(1.0, 2.0)),
1108            Size::Logical(LogicalSize::new(1.0, 2.0))
1109        );
1110
1111        assert_eq!(
1112            Size::new(PhysicalSize::new(1, 2)).to_logical::<f64>(1.0),
1113            LogicalSize::new(1.0, 2.0)
1114        );
1115        assert_eq!(
1116            Size::new(PhysicalSize::new(1, 2)).to_logical::<f64>(2.0),
1117            LogicalSize::new(0.5, 1.0)
1118        );
1119        assert_eq!(
1120            Size::new(LogicalSize::new(1.0, 2.0)).to_logical::<f64>(1.0),
1121            LogicalSize::new(1.0, 2.0)
1122        );
1123
1124        assert_eq!(
1125            Size::new(PhysicalSize::new(1, 2)).to_physical::<u32>(1.0),
1126            PhysicalSize::new(1, 2)
1127        );
1128        assert_eq!(
1129            Size::new(PhysicalSize::new(1, 2)).to_physical::<u32>(2.0),
1130            PhysicalSize::new(1, 2)
1131        );
1132        assert_eq!(
1133            Size::new(LogicalSize::new(1.0, 2.0)).to_physical::<u32>(1.0),
1134            PhysicalSize::new(1, 2)
1135        );
1136        assert_eq!(
1137            Size::new(LogicalSize::new(1.0, 2.0)).to_physical::<u32>(2.0),
1138            PhysicalSize::new(2, 4)
1139        );
1140
1141        let small = Size::Physical((1, 2).into());
1142        let medium = Size::Logical((3, 4).into());
1143        let medium_physical = Size::new(medium.to_physical::<u32>(1.0));
1144        let large = Size::Physical((5, 6).into());
1145        assert_eq!(Size::clamp(medium, small, large, 1.0), medium_physical);
1146        assert_eq!(Size::clamp(small, medium, large, 1.0), medium_physical);
1147        assert_eq!(Size::clamp(large, small, medium, 1.0), medium_physical);
1148    }
1149
1150    #[test]
1151    fn test_position() {
1152        assert_eq!(
1153            Position::new(PhysicalPosition::new(1, 2)),
1154            Position::Physical(PhysicalPosition::new(1, 2))
1155        );
1156        assert_eq!(
1157            Position::new(LogicalPosition::new(1.0, 2.0)),
1158            Position::Logical(LogicalPosition::new(1.0, 2.0))
1159        );
1160
1161        assert_eq!(
1162            Position::new(PhysicalPosition::new(1, 2)).to_logical::<f64>(1.0),
1163            LogicalPosition::new(1.0, 2.0)
1164        );
1165        assert_eq!(
1166            Position::new(PhysicalPosition::new(1, 2)).to_logical::<f64>(2.0),
1167            LogicalPosition::new(0.5, 1.0)
1168        );
1169        assert_eq!(
1170            Position::new(LogicalPosition::new(1.0, 2.0)).to_logical::<f64>(1.0),
1171            LogicalPosition::new(1.0, 2.0)
1172        );
1173
1174        assert_eq!(
1175            Position::new(PhysicalPosition::new(1, 2)).to_physical::<u32>(1.0),
1176            PhysicalPosition::new(1, 2)
1177        );
1178        assert_eq!(
1179            Position::new(PhysicalPosition::new(1, 2)).to_physical::<u32>(2.0),
1180            PhysicalPosition::new(1, 2)
1181        );
1182        assert_eq!(
1183            Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(1.0),
1184            PhysicalPosition::new(1, 2)
1185        );
1186        assert_eq!(
1187            Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(2.0),
1188            PhysicalPosition::new(2, 4)
1189        );
1190    }
1191
1192    // Eat coverage for the Debug impls et al
1193    #[test]
1194    fn ensure_attrs_do_not_panic() {
1195        let _ = format!("{:?}", LogicalPosition::<u32>::default().clone());
1196        HashSet::new().insert(LogicalPosition::<u32>::default());
1197
1198        let _ = format!("{:?}", PhysicalPosition::<u32>::default().clone());
1199        HashSet::new().insert(PhysicalPosition::<u32>::default());
1200
1201        let _ = format!("{:?}", LogicalSize::<u32>::default().clone());
1202        HashSet::new().insert(LogicalSize::<u32>::default());
1203
1204        let _ = format!("{:?}", PhysicalSize::<u32>::default().clone());
1205        HashSet::new().insert(PhysicalSize::<u32>::default());
1206
1207        let _ = format!("{:?}", Size::Physical((1, 2).into()).clone());
1208        let _ = format!("{:?}", Position::Physical((1, 2).into()).clone());
1209    }
1210
1211    #[test]
1212    fn ensure_copy_trait() {
1213        fn is_copy<T: Copy>() {}
1214
1215        is_copy::<LogicalUnit<i32>>();
1216        is_copy::<PhysicalUnit<f64>>();
1217        is_copy::<PixelUnit>();
1218
1219        is_copy::<LogicalSize<i32>>();
1220        is_copy::<PhysicalSize<f64>>();
1221        is_copy::<Size>();
1222
1223        is_copy::<LogicalPosition<i32>>();
1224        is_copy::<PhysicalPosition<f64>>();
1225        is_copy::<Position>();
1226    }
1227
1228    #[test]
1229    fn ensure_partial_eq_trait() {
1230        fn is_partial_eq<T: PartialEq>() {}
1231
1232        is_partial_eq::<LogicalUnit<i32>>();
1233        is_partial_eq::<PhysicalUnit<f64>>();
1234        is_partial_eq::<PixelUnit>();
1235
1236        is_partial_eq::<LogicalSize<i32>>();
1237        is_partial_eq::<PhysicalSize<f64>>();
1238        is_partial_eq::<Size>();
1239
1240        is_partial_eq::<LogicalPosition<i32>>();
1241        is_partial_eq::<PhysicalPosition<f64>>();
1242        is_partial_eq::<Position>();
1243    }
1244}