// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use serde::{Deserialize, Serialize}; pub trait Pixel: Copy + Into { fn from_f64(f: f64) -> Self; fn cast(self) -> P { P::from_f64(self.into()) } } impl Pixel for u8 { fn from_f64(f: f64) -> Self { f.round() as u8 } } impl Pixel for u16 { fn from_f64(f: f64) -> Self { f.round() as u16 } } impl Pixel for u32 { fn from_f64(f: f64) -> Self { f.round() as u32 } } impl Pixel for i8 { fn from_f64(f: f64) -> Self { f.round() as i8 } } impl Pixel for i16 { fn from_f64(f: f64) -> Self { f.round() as i16 } } impl Pixel for i32 { fn from_f64(f: f64) -> Self { f.round() as i32 } } impl Pixel for f32 { fn from_f64(f: f64) -> Self { f as f32 } } impl Pixel for f64 { fn from_f64(f: f64) -> Self { f } } /// Checks that the scale factor is a normal positive `f64`. /// /// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from /// anywhere other than tao, it's recommended to validate them using this function before passing them to tao; /// otherwise, you risk panics. #[inline] pub fn validate_scale_factor(scale_factor: f64) -> bool { scale_factor.is_sign_positive() && scale_factor.is_normal() } /// A position represented in logical pixels. /// /// The position is stored as floats, so please be careful. Casting floats to integers truncates the /// fractional part, which can cause noticeable issues. To help with that, an `Into<(i32, i32)>` /// implementation is provided which does the rounding for you. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] pub struct LogicalPosition

{ pub x: P, pub y: P, } impl

LogicalPosition

{ #[inline] pub const fn new(x: P, y: P) -> Self { LogicalPosition { x, y } } } impl LogicalPosition

{ #[inline] pub fn from_physical>, X: Pixel>( physical: T, scale_factor: f64, ) -> Self { physical.into().to_logical(scale_factor) } #[inline] pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition { assert!(validate_scale_factor(scale_factor)); let x = self.x.into() * scale_factor; let y = self.y.into() * scale_factor; PhysicalPosition::new(x, y).cast() } #[inline] pub fn cast(&self) -> LogicalPosition { LogicalPosition { x: self.x.cast(), y: self.y.cast(), } } } impl From<(X, X)> for LogicalPosition

{ fn from((x, y): (X, X)) -> LogicalPosition

{ LogicalPosition::new(x.cast(), y.cast()) } } impl From> for (X, X) { fn from(pos: LogicalPosition

) -> Self { (pos.x.cast(), pos.y.cast()) } } impl From<[X; 2]> for LogicalPosition

{ fn from([x, y]: [X; 2]) -> LogicalPosition

{ LogicalPosition::new(x.cast(), y.cast()) } } impl From> for [X; 2] { fn from(pos: LogicalPosition

) -> Self { [pos.x.cast(), pos.y.cast()] } } /// A position represented in physical pixels. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] pub struct PhysicalPosition

{ pub x: P, pub y: P, } impl

PhysicalPosition

{ #[inline] pub const fn new(x: P, y: P) -> Self { PhysicalPosition { x, y } } } impl PhysicalPosition

{ #[inline] pub fn from_logical>, X: Pixel>( logical: T, scale_factor: f64, ) -> Self { logical.into().to_physical(scale_factor) } #[inline] pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition { assert!(validate_scale_factor(scale_factor)); let x = self.x.into() / scale_factor; let y = self.y.into() / scale_factor; LogicalPosition::new(x, y).cast() } #[inline] pub fn cast(&self) -> PhysicalPosition { PhysicalPosition { x: self.x.cast(), y: self.y.cast(), } } } impl From<(X, X)> for PhysicalPosition

{ fn from((x, y): (X, X)) -> PhysicalPosition

{ PhysicalPosition::new(x.cast(), y.cast()) } } impl From> for (X, X) { fn from(pos: PhysicalPosition

) -> Self { (pos.x.cast(), pos.y.cast()) } } impl From<[X; 2]> for PhysicalPosition

{ fn from([x, y]: [X; 2]) -> PhysicalPosition

{ PhysicalPosition::new(x.cast(), y.cast()) } } impl From> for [X; 2] { fn from(pos: PhysicalPosition

) -> Self { [pos.x.cast(), pos.y.cast()] } } /// A size represented in logical pixels. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] pub struct LogicalSize

{ pub width: P, pub height: P, } impl

LogicalSize

{ #[inline] pub const fn new(width: P, height: P) -> Self { LogicalSize { width, height } } } impl LogicalSize

{ #[inline] pub fn from_physical>, X: Pixel>(physical: T, scale_factor: f64) -> Self { physical.into().to_logical(scale_factor) } #[inline] pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize { assert!(validate_scale_factor(scale_factor)); let width = self.width.into() * scale_factor; let height = self.height.into() * scale_factor; PhysicalSize::new(width, height).cast() } #[inline] pub fn cast(&self) -> LogicalSize { LogicalSize { width: self.width.cast(), height: self.height.cast(), } } } impl From<(X, X)> for LogicalSize

{ fn from((x, y): (X, X)) -> LogicalSize

{ LogicalSize::new(x.cast(), y.cast()) } } impl From> for (X, X) { fn from(size: LogicalSize

) -> Self { (size.width.cast(), size.height.cast()) } } impl From<[X; 2]> for LogicalSize

{ fn from([x, y]: [X; 2]) -> LogicalSize

{ LogicalSize::new(x.cast(), y.cast()) } } impl From> for [X; 2] { fn from(size: LogicalSize

) -> Self { [size.width.cast(), size.height.cast()] } } /// A size represented in physical pixels. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] pub struct PhysicalSize

{ pub width: P, pub height: P, } impl

PhysicalSize

{ #[inline] pub const fn new(width: P, height: P) -> Self { PhysicalSize { width, height } } } impl PhysicalSize

{ #[inline] pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { logical.into().to_physical(scale_factor) } #[inline] pub fn to_logical(&self, scale_factor: f64) -> LogicalSize { assert!(validate_scale_factor(scale_factor)); let width = self.width.into() / scale_factor; let height = self.height.into() / scale_factor; LogicalSize::new(width, height).cast() } #[inline] pub fn cast(&self) -> PhysicalSize { PhysicalSize { width: self.width.cast(), height: self.height.cast(), } } } impl From<(X, X)> for PhysicalSize

{ fn from((x, y): (X, X)) -> PhysicalSize

{ PhysicalSize::new(x.cast(), y.cast()) } } impl From> for (X, X) { fn from(size: PhysicalSize

) -> Self { (size.width.cast(), size.height.cast()) } } impl From<[X; 2]> for PhysicalSize

{ fn from([x, y]: [X; 2]) -> PhysicalSize

{ PhysicalSize::new(x.cast(), y.cast()) } } impl From> for [X; 2] { fn from(size: PhysicalSize

) -> Self { [size.width.cast(), size.height.cast()] } } /// A size that's either physical or logical. #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] pub enum Size { Physical(PhysicalSize), Logical(LogicalSize), } impl Size { pub fn new>(size: S) -> Size { size.into() } pub fn to_logical(&self, scale_factor: f64) -> LogicalSize

{ match *self { Size::Physical(size) => size.to_logical(scale_factor), Size::Logical(size) => size.cast(), } } pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize

{ match *self { Size::Physical(size) => size.cast(), Size::Logical(size) => size.to_physical(scale_factor), } } } impl From> for Size { #[inline] fn from(size: PhysicalSize

) -> Size { Size::Physical(size.cast()) } } impl From> for Size { #[inline] fn from(size: LogicalSize

) -> Size { Size::Logical(size.cast()) } } /// A position that's either physical or logical. #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] pub enum Position { Physical(PhysicalPosition), Logical(LogicalPosition), } impl Position { pub fn new>(position: S) -> Position { position.into() } pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ match *self { Position::Physical(position) => position.to_logical(scale_factor), Position::Logical(position) => position.cast(), } } pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ match *self { Position::Physical(position) => position.cast(), Position::Logical(position) => position.to_physical(scale_factor), } } } impl From> for Position { #[inline] fn from(position: PhysicalPosition

) -> Position { Position::Physical(position.cast()) } } impl From> for Position { #[inline] fn from(position: LogicalPosition

) -> Position { Position::Logical(position.cast()) } }