123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- // 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<f64> {
- fn from_f64(f: f64) -> Self;
- fn cast<P: Pixel>(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<P> {
- pub x: P,
- pub y: P,
- }
- impl<P> LogicalPosition<P> {
- #[inline]
- pub const fn new(x: P, y: P) -> Self {
- LogicalPosition { x, y }
- }
- }
- impl<P: Pixel> LogicalPosition<P> {
- #[inline]
- pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
- physical: T,
- scale_factor: f64,
- ) -> Self {
- physical.into().to_logical(scale_factor)
- }
- #[inline]
- pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
- 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<X: Pixel>(&self) -> LogicalPosition<X> {
- LogicalPosition {
- x: self.x.cast(),
- y: self.y.cast(),
- }
- }
- }
- impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
- fn from((x, y): (X, X)) -> LogicalPosition<P> {
- LogicalPosition::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
- fn from(pos: LogicalPosition<P>) -> Self {
- (pos.x.cast(), pos.y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
- fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
- LogicalPosition::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
- fn from(pos: LogicalPosition<P>) -> 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<P> {
- pub x: P,
- pub y: P,
- }
- impl<P> PhysicalPosition<P> {
- #[inline]
- pub const fn new(x: P, y: P) -> Self {
- PhysicalPosition { x, y }
- }
- }
- impl<P: Pixel> PhysicalPosition<P> {
- #[inline]
- pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
- logical: T,
- scale_factor: f64,
- ) -> Self {
- logical.into().to_physical(scale_factor)
- }
- #[inline]
- pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
- 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<X: Pixel>(&self) -> PhysicalPosition<X> {
- PhysicalPosition {
- x: self.x.cast(),
- y: self.y.cast(),
- }
- }
- }
- impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
- fn from((x, y): (X, X)) -> PhysicalPosition<P> {
- PhysicalPosition::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
- fn from(pos: PhysicalPosition<P>) -> Self {
- (pos.x.cast(), pos.y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
- fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
- PhysicalPosition::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
- fn from(pos: PhysicalPosition<P>) -> 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<P> {
- pub width: P,
- pub height: P,
- }
- impl<P> LogicalSize<P> {
- #[inline]
- pub const fn new(width: P, height: P) -> Self {
- LogicalSize { width, height }
- }
- }
- impl<P: Pixel> LogicalSize<P> {
- #[inline]
- pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(physical: T, scale_factor: f64) -> Self {
- physical.into().to_logical(scale_factor)
- }
- #[inline]
- pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
- 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<X: Pixel>(&self) -> LogicalSize<X> {
- LogicalSize {
- width: self.width.cast(),
- height: self.height.cast(),
- }
- }
- }
- impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
- fn from((x, y): (X, X)) -> LogicalSize<P> {
- LogicalSize::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
- fn from(size: LogicalSize<P>) -> Self {
- (size.width.cast(), size.height.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
- fn from([x, y]: [X; 2]) -> LogicalSize<P> {
- LogicalSize::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
- fn from(size: LogicalSize<P>) -> 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<P> {
- pub width: P,
- pub height: P,
- }
- impl<P> PhysicalSize<P> {
- #[inline]
- pub const fn new(width: P, height: P) -> Self {
- PhysicalSize { width, height }
- }
- }
- impl<P: Pixel> PhysicalSize<P> {
- #[inline]
- pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
- logical.into().to_physical(scale_factor)
- }
- #[inline]
- pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
- 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<X: Pixel>(&self) -> PhysicalSize<X> {
- PhysicalSize {
- width: self.width.cast(),
- height: self.height.cast(),
- }
- }
- }
- impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
- fn from((x, y): (X, X)) -> PhysicalSize<P> {
- PhysicalSize::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
- fn from(size: PhysicalSize<P>) -> Self {
- (size.width.cast(), size.height.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
- fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
- PhysicalSize::new(x.cast(), y.cast())
- }
- }
- impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
- fn from(size: PhysicalSize<P>) -> 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<u32>),
- Logical(LogicalSize<f64>),
- }
- impl Size {
- pub fn new<S: Into<Size>>(size: S) -> Size {
- size.into()
- }
- pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
- match *self {
- Size::Physical(size) => size.to_logical(scale_factor),
- Size::Logical(size) => size.cast(),
- }
- }
- pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
- match *self {
- Size::Physical(size) => size.cast(),
- Size::Logical(size) => size.to_physical(scale_factor),
- }
- }
- }
- impl<P: Pixel> From<PhysicalSize<P>> for Size {
- #[inline]
- fn from(size: PhysicalSize<P>) -> Size {
- Size::Physical(size.cast())
- }
- }
- impl<P: Pixel> From<LogicalSize<P>> for Size {
- #[inline]
- fn from(size: LogicalSize<P>) -> 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<i32>),
- Logical(LogicalPosition<f64>),
- }
- impl Position {
- pub fn new<S: Into<Position>>(position: S) -> Position {
- position.into()
- }
- pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
- match *self {
- Position::Physical(position) => position.to_logical(scale_factor),
- Position::Logical(position) => position.cast(),
- }
- }
- pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
- match *self {
- Position::Physical(position) => position.cast(),
- Position::Logical(position) => position.to_physical(scale_factor),
- }
- }
- }
- impl<P: Pixel> From<PhysicalPosition<P>> for Position {
- #[inline]
- fn from(position: PhysicalPosition<P>) -> Position {
- Position::Physical(position.cast())
- }
- }
- impl<P: Pixel> From<LogicalPosition<P>> for Position {
- #[inline]
- fn from(position: LogicalPosition<P>) -> Position {
- Position::Logical(position.cast())
- }
- }
|