/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! A collection of coordinate spaces and their corresponding Point, Size and Rect types. //! //! Physical pixels take into account the device pixel ratio and their dimensions tend //! to correspond to the allocated size of resources in memory, while logical pixels //! don't have the device pixel ratio applied which means they are agnostic to the usage //! of hidpi screens and the like. //! //! The terms "layer" and "stacking context" can be used interchangeably //! in the context of coordinate systems. pub use app_units::Au; use euclid::{Length, Rect, Scale, Size2D, Transform3D, Translation2D}; use euclid::{Point2D, Point3D, Vector2D, Vector3D, SideOffsets2D, Box2D}; use euclid::HomogeneousVector; use peek_poke::PeekPoke; // local imports use crate::image::DirtyRect; /// Geometry in the coordinate system of the render target (screen or intermediate /// surface) in physical pixels. #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct DevicePixel; pub type DeviceIntRect = Box2D; pub type DeviceIntPoint = Point2D; pub type DeviceIntSize = Size2D; pub type DeviceIntLength = Length; pub type DeviceIntSideOffsets = SideOffsets2D; pub type DeviceIntVector2D = Vector2D; pub type DeviceRect = Box2D; pub type DeviceBox2D = Box2D; pub type DevicePoint = Point2D; pub type DeviceVector2D = Vector2D; pub type DeviceSize = Size2D; pub type DeviceHomogeneousVector = HomogeneousVector; /// Geometry in the coordinate system of the framebuffer in physical pixels. /// It's Y-flipped comparing to DevicePixel. #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct FramebufferPixel; pub type FramebufferIntPoint = Point2D; pub type FramebufferIntSize = Size2D; pub type FramebufferIntRect = Box2D; /// Geometry in the coordinate system of a Picture (intermediate /// surface) in physical pixels. #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct PicturePixel; pub type PictureIntRect = Box2D; pub type PictureIntPoint = Point2D; pub type PictureIntSize = Size2D; pub type PictureRect = Box2D; pub type PicturePoint = Point2D; pub type PictureSize = Size2D; pub type PicturePoint3D = Point3D; pub type PictureVector2D = Vector2D; pub type PictureVector3D = Vector3D; pub type PictureBox2D = Box2D; /// Geometry gets rasterized in a given root coordinate space. This /// is often the root spatial node (world space), but may be a local /// space for a variety of reasons (e.g. perspective). #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct RasterPixel; pub type RasterIntRect = Box2D; pub type RasterIntPoint = Point2D; pub type RasterIntSize = Size2D; pub type RasterRect = Box2D; pub type RasterPoint = Point2D; pub type RasterSize = Size2D; pub type RasterPoint3D = Point3D; pub type RasterVector2D = Vector2D; pub type RasterVector3D = Vector3D; /// Geometry in a stacking context's local coordinate space (logical pixels). #[derive(Hash, Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Ord, PartialOrd, Deserialize, Serialize, PeekPoke)] pub struct LayoutPixel; pub type LayoutRect = Box2D; pub type LayoutPoint = Point2D; pub type LayoutPoint3D = Point3D; pub type LayoutVector2D = Vector2D; pub type LayoutVector3D = Vector3D; pub type LayoutSize = Size2D; pub type LayoutSideOffsets = SideOffsets2D; pub type LayoutIntRect = Box2D; pub type LayoutIntPoint = Point2D; pub type LayoutIntSize = Size2D; /// Geometry in the document's coordinate space (logical pixels). #[derive(Hash, Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct WorldPixel; pub type WorldRect = Box2D; pub type WorldIntRect = Box2D; pub type WorldPoint = Point2D; pub type WorldSize = Size2D; pub type WorldPoint3D = Point3D; pub type WorldVector2D = Vector2D; pub type WorldVector3D = Vector3D; /// Offset in number of tiles. #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct Tiles; pub type TileOffset = Point2D; pub type TileRange = Box2D; /// Scaling ratio from world pixels to device pixels. pub type DevicePixelScale = Scale; /// Scaling ratio from layout to world. Used for cases where we know the layout /// is in world space, or specifically want to treat it this way. pub type LayoutToWorldScale = Scale; /// A complete scaling ratio from layout space to device pixel space. pub type LayoutToDeviceScale = Scale; pub type LayoutTransform = Transform3D; pub type LayoutToWorldTransform = Transform3D; pub type WorldToLayoutTransform = Transform3D; pub type LayoutToPictureTransform = Transform3D; pub type PictureToLayoutTransform = Transform3D; pub type LayoutToRasterTransform = Transform3D; pub type RasterToLayoutTransform = Transform3D; pub type PictureToRasterTransform = Transform3D; pub type RasterToPictureTransform = Transform3D; /// Scaling ratio from picture pixels to raster pixels (e.g. if scaling a picture surface up/down). pub type RasterPixelScale = Scale; // Fixed position coordinates, to avoid float precision errors. pub type LayoutPointAu = Point2D; pub type LayoutRectAu = Box2D; pub type LayoutSizeAu = Size2D; pub type LayoutVector2DAu = Vector2D; pub type LayoutSideOffsetsAu = SideOffsets2D; pub type ImageDirtyRect = DirtyRect; pub type BlobDirtyRect = DirtyRect; pub type BlobToDeviceTranslation = Translation2D; /// Stores two coordinates in texel space. The coordinates /// are stored in texel coordinates because the texture atlas /// may grow. Storing them as texel coords and normalizing /// the UVs in the vertex shader means nothing needs to be /// updated on the CPU when the texture size changes. #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct TexelRect { pub uv0: DevicePoint, pub uv1: DevicePoint, } impl TexelRect { pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> Self { TexelRect { uv0: DevicePoint::new(u0, v0), uv1: DevicePoint::new(u1, v1), } } pub fn invalid() -> Self { TexelRect { uv0: DevicePoint::new(-1.0, -1.0), uv1: DevicePoint::new(-1.0, -1.0), } } } impl Into for DeviceIntRect { fn into(self) -> TexelRect { TexelRect { uv0: self.min.to_f32(), uv1: self.max.to_f32(), } } } const MAX_AU_FLOAT: f32 = 1.0e6; pub trait AuHelpers { fn from_au(data: T) -> Self; fn to_au(&self) -> T; } impl AuHelpers for LayoutSize { fn from_au(size: LayoutSizeAu) -> Self { LayoutSize::new( size.width.to_f32_px(), size.height.to_f32_px(), ) } fn to_au(&self) -> LayoutSizeAu { let width = self.width.min(2.0 * MAX_AU_FLOAT); let height = self.height.min(2.0 * MAX_AU_FLOAT); LayoutSizeAu::new( Au::from_f32_px(width), Au::from_f32_px(height), ) } } impl AuHelpers for LayoutVector2D { fn from_au(size: LayoutVector2DAu) -> Self { LayoutVector2D::new( size.x.to_f32_px(), size.y.to_f32_px(), ) } fn to_au(&self) -> LayoutVector2DAu { LayoutVector2DAu::new( Au::from_f32_px(self.x), Au::from_f32_px(self.y), ) } } impl AuHelpers for LayoutPoint { fn from_au(point: LayoutPointAu) -> Self { LayoutPoint::new( point.x.to_f32_px(), point.y.to_f32_px(), ) } fn to_au(&self) -> LayoutPointAu { let x = self.x.min(MAX_AU_FLOAT).max(-MAX_AU_FLOAT); let y = self.y.min(MAX_AU_FLOAT).max(-MAX_AU_FLOAT); LayoutPointAu::new( Au::from_f32_px(x), Au::from_f32_px(y), ) } } impl AuHelpers for LayoutRect { fn from_au(rect: LayoutRectAu) -> Self { LayoutRect { min: LayoutPoint::from_au(rect.min), max: LayoutPoint::from_au(rect.max), } } fn to_au(&self) -> LayoutRectAu { LayoutRectAu { min: self.min.to_au(), max: self.max.to_au(), } } } impl AuHelpers for LayoutSideOffsets { fn from_au(offsets: LayoutSideOffsetsAu) -> Self { LayoutSideOffsets::new( offsets.top.to_f32_px(), offsets.right.to_f32_px(), offsets.bottom.to_f32_px(), offsets.left.to_f32_px(), ) } fn to_au(&self) -> LayoutSideOffsetsAu { LayoutSideOffsetsAu::new( Au::from_f32_px(self.top), Au::from_f32_px(self.right), Au::from_f32_px(self.bottom), Au::from_f32_px(self.left), ) } } pub trait RectExt { type Point; fn top_left(&self) -> Self::Point; fn top_right(&self) -> Self::Point; fn bottom_left(&self) -> Self::Point; fn bottom_right(&self) -> Self::Point; } impl RectExt for Rect { type Point = Point2D; fn top_left(&self) -> Self::Point { self.min() } fn top_right(&self) -> Self::Point { Point2D::new(self.max_x(), self.min_y()) } fn bottom_left(&self) -> Self::Point { Point2D::new(self.min_x(), self.max_y()) } fn bottom_right(&self) -> Self::Point { self.max() } } impl RectExt for Box2D { type Point = Point2D; fn top_left(&self) -> Self::Point { self.min } fn top_right(&self) -> Self::Point { Point2D::new(self.max.x, self.min.y) } fn bottom_left(&self) -> Self::Point { Point2D::new(self.min.x, self.max.y) } fn bottom_right(&self) -> Self::Point { self.max } } // A few helpers to convert to cast between coordinate spaces that are often equivalent. #[inline] pub fn layout_rect_as_picture_rect(layout_rect: &LayoutRect) -> PictureRect { layout_rect.cast_unit() } #[inline] pub fn layout_vector_as_picture_vector(layout_vector: LayoutVector2D) -> PictureVector2D { layout_vector.cast_unit() } #[inline] pub fn device_size_as_framebuffer_size(framebuffer_size: DeviceIntSize) -> FramebufferIntSize { framebuffer_size.cast_unit() } #[inline] pub fn device_rect_as_framebuffer_rect(framebuffer_rect: &DeviceIntRect) -> FramebufferIntRect { framebuffer_rect.cast_unit() }