UnitTransforms.h (17612B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZ_UNIT_TRANSFORMS_H_ 8 #define MOZ_UNIT_TRANSFORMS_H_ 9 10 #include "Units.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/gfx/Matrix.h" 13 #include "nsRegion.h" 14 15 namespace mozilla { 16 17 // Convenience functions for converting an entity from one strongly-typed 18 // coordinate system to another without changing the values it stores (this 19 // can be thought of as a cast). 20 // To use these functions, you must provide a justification for each use! 21 // Feel free to add more justifications to PixelCastJustification, along with 22 // a comment that explains under what circumstances it is appropriate to use. 23 24 enum class PixelCastJustification : uint8_t { 25 // For the root layer, Screen Pixel = Parent Layer Pixel. 26 ScreenIsParentLayerForRoot, 27 // On the layout side, Screen Pixel = LayoutDevice at the outer-window level. 28 LayoutDeviceIsScreenForBounds, 29 // For the root layer, Render Target Pixel = Parent Layer Pixel. 30 RenderTargetIsParentLayerForRoot, 31 // For the root composition size we want to view it as layer pixels in any 32 // layer 33 ParentLayerToLayerForRootComposition, 34 // The Layer coordinate space for one layer is the ParentLayer coordinate 35 // space for its children 36 MovingDownToChildren, 37 // The transform that is usually used to convert between two coordinate 38 // systems is not available (for example, because the object that stores it 39 // is being destroyed), so fall back to the identity. 40 TransformNotAvailable, 41 // When an OS event is initially constructed, its reference point is 42 // technically in screen pixels, as it has not yet accounted for any 43 // asynchronous transforms. This justification is for viewing the initial 44 // reference point as a screen point. The reverse is useful when synthetically 45 // created WidgetEvents need to be converted back to InputData. 46 LayoutDeviceIsScreenForUntransformedEvent, 47 // A combination of LayoutDeviceIsScreenForBounds and 48 // ScreenIsParentLayerForRoot, which is how we're using it. 49 LayoutDeviceIsParentLayerForRCDRSF, 50 // Used to treat the product of AsyncTransformComponentMatrix objects 51 // as an AsyncTransformMatrix. See the definitions of these matrices in 52 // LayersTypes.h for details. 53 MultipleAsyncTransforms, 54 // We have reason to believe a layer doesn't have a local transform. 55 // Should only be used if we've already checked or asserted this. 56 NoTransformOnLayer, 57 // LayerPixels are ImagePixels 58 LayerIsImage, 59 // External pixels are the same scale as screen pixels 60 ExternalIsScreen, 61 // LayerToScreenMatrix is used as LayoutDeviceToLayoutDevice, because 62 // out-of-process iframes uses LayoutDevicePixels as the type system-visible 63 // type of their top-level event coordinate space even if technically 64 // inaccurate. 65 ContentProcessIsLayerInUiProcess, 66 // Propagating TransformToAncestorScale to a child process. 67 PropagatingToChildProcess, 68 // A quantity represents a proportion of a page length, e.g. "0.5 pages". 69 // The proportion does not need to be scaled when converting between 70 // units (the page length that it's mutlipled by will be scaled instead). 71 DeltaIsPageProportion, 72 // Used to cast between CSS and OuterCSS pixels when moving between code 73 // that deals with content outside a scroll frame generically (which would 74 // use CSS pixels) and code related to the scroll frame in APZ (which wants 75 // such quantities in OuterCSS pixels). 76 CSSPixelsOfSurroundingContent, 77 }; 78 79 template <class TargetUnits, class SourceUnits> 80 gfx::CoordTyped<TargetUnits> ViewAs(const gfx::CoordTyped<SourceUnits>& aCoord, 81 PixelCastJustification) { 82 return gfx::CoordTyped<TargetUnits>(aCoord.value); 83 } 84 template <class TargetUnits, class SourceUnits> 85 gfx::IntCoordTyped<TargetUnits> ViewAs( 86 const gfx::IntCoordTyped<SourceUnits>& aCoord, PixelCastJustification) { 87 return gfx::IntCoordTyped<TargetUnits>(aCoord.value); 88 } 89 template <class TargetUnits, class SourceUnits> 90 gfx::SizeTyped<TargetUnits> ViewAs(const gfx::SizeTyped<SourceUnits>& aSize, 91 PixelCastJustification) { 92 return gfx::SizeTyped<TargetUnits>(aSize.width, aSize.height); 93 } 94 template <class TargetUnits, class SourceUnits> 95 gfx::IntSizeTyped<TargetUnits> ViewAs( 96 const gfx::IntSizeTyped<SourceUnits>& aSize, PixelCastJustification) { 97 return gfx::IntSizeTyped<TargetUnits>(aSize.width, aSize.height); 98 } 99 template <class TargetUnits, class SourceUnits> 100 gfx::PointTyped<TargetUnits> ViewAs(const gfx::PointTyped<SourceUnits>& aPoint, 101 PixelCastJustification) { 102 return gfx::PointTyped<TargetUnits>(aPoint.x, aPoint.y); 103 } 104 template <class TargetUnits, class SourceUnits> 105 gfx::IntPointTyped<TargetUnits> ViewAs( 106 const gfx::IntPointTyped<SourceUnits>& aPoint, PixelCastJustification) { 107 return gfx::IntPointTyped<TargetUnits>(aPoint.x, aPoint.y); 108 } 109 template <class TargetUnits, class SourceUnits> 110 gfx::RectTyped<TargetUnits> ViewAs(const gfx::RectTyped<SourceUnits>& aRect, 111 PixelCastJustification) { 112 return gfx::RectTyped<TargetUnits>(aRect.x, aRect.y, aRect.Width(), 113 aRect.Height()); 114 } 115 template <class TargetUnits, class SourceUnits> 116 gfx::IntRectTyped<TargetUnits> ViewAs( 117 const gfx::IntRectTyped<SourceUnits>& aRect, PixelCastJustification) { 118 return gfx::IntRectTyped<TargetUnits>(aRect.x, aRect.y, aRect.Width(), 119 aRect.Height()); 120 } 121 template <class TargetUnits, class SourceUnits> 122 gfx::MarginTyped<TargetUnits> ViewAs( 123 const gfx::MarginTyped<SourceUnits>& aMargin, PixelCastJustification) { 124 return gfx::MarginTyped<TargetUnits>(aMargin.top.value, aMargin.right.value, 125 aMargin.bottom.value, 126 aMargin.left.value); 127 } 128 template <class TargetUnits, class SourceUnits> 129 gfx::IntMarginTyped<TargetUnits> ViewAs( 130 const gfx::IntMarginTyped<SourceUnits>& aMargin, PixelCastJustification) { 131 return gfx::IntMarginTyped<TargetUnits>(aMargin.top, aMargin.right, 132 aMargin.bottom, aMargin.left); 133 } 134 template <class TargetUnits, class SourceUnits> 135 gfx::IntRegionTyped<TargetUnits> ViewAs( 136 const gfx::IntRegionTyped<SourceUnits>& aRegion, PixelCastJustification) { 137 return gfx::IntRegionTyped<TargetUnits>::FromUnknownRegion( 138 aRegion.ToUnknownRegion()); 139 } 140 template <class NewTargetUnits, class OldTargetUnits, class SourceUnits> 141 gfx::ScaleFactor<SourceUnits, NewTargetUnits> ViewTargetAs( 142 const gfx::ScaleFactor<SourceUnits, OldTargetUnits>& aScaleFactor, 143 PixelCastJustification) { 144 return gfx::ScaleFactor<SourceUnits, NewTargetUnits>(aScaleFactor.scale); 145 } 146 template <class NewTargetUnits, class OldTargetUnits, class SourceUnits> 147 gfx::ScaleFactors2D<SourceUnits, NewTargetUnits> ViewTargetAs( 148 const gfx::ScaleFactors2D<SourceUnits, OldTargetUnits>& aScaleFactors, 149 PixelCastJustification) { 150 return gfx::ScaleFactors2D<SourceUnits, NewTargetUnits>(aScaleFactors.xScale, 151 aScaleFactors.yScale); 152 } 153 template <class TargetUnits, class SourceUnits> 154 Maybe<gfx::IntRectTyped<TargetUnits>> ViewAs( 155 const Maybe<gfx::IntRectTyped<SourceUnits>>& aRect, 156 PixelCastJustification aJustification) { 157 if (aRect.isSome()) { 158 return Some(ViewAs<TargetUnits>(aRect.value(), aJustification)); 159 } 160 return Nothing(); 161 } 162 // Unlike the other functions in this category, these functions take the 163 // target matrix or scale type, rather than its source and target unit types, as 164 // the explicit template argument, so an example invocation is: 165 // ViewAs<ScreenToLayerMatrix4x4>(otherTypedMatrix, justification) 166 // The reason is that if it took the source and target unit types as two 167 // template arguments, there may be some confusion as to which is the 168 // source and which is the target. 169 template <class TargetMatrix, class SourceMatrixSourceUnits, 170 class SourceMatrixTargetUnits> 171 TargetMatrix ViewAs(const gfx::Matrix4x4Typed<SourceMatrixSourceUnits, 172 SourceMatrixTargetUnits>& aMatrix, 173 PixelCastJustification) { 174 return aMatrix.template Cast<TargetMatrix>(); 175 } 176 template <class TargetMatrix, class SourceMatrixSourceUnits, 177 class SourceMatrixTargetUnits> 178 Maybe<TargetMatrix> ViewAs( 179 const Maybe<gfx::Matrix4x4Typed<SourceMatrixSourceUnits, 180 SourceMatrixTargetUnits>>& aMatrix, 181 PixelCastJustification) { 182 if (aMatrix.isSome()) { 183 return Some(aMatrix->template Cast<TargetMatrix>()); 184 } 185 return Nothing(); 186 } 187 template <class TargetScale, class SourceScaleSourceUnits, 188 class SourceScaleTargetUnits> 189 TargetScale ViewAs(const gfx::ScaleFactor<SourceScaleSourceUnits, 190 SourceScaleTargetUnits>& aScale, 191 PixelCastJustification) { 192 return TargetScale{aScale.scale}; 193 } 194 195 // A non-member overload of ToUnknownMatrix() for use on a Maybe<Matrix>. 196 // We can't make this a member because we can't inject a member into Maybe. 197 template <typename SourceUnits, typename TargetUnits> 198 Maybe<gfx::Matrix4x4> ToUnknownMatrix( 199 const Maybe<gfx::Matrix4x4Typed<SourceUnits, TargetUnits>>& aMatrix) { 200 if (aMatrix.isSome()) { 201 return Some(aMatrix->ToUnknownMatrix()); 202 } 203 return Nothing(); 204 } 205 206 // Convenience functions for casting untyped entities to typed entities. 207 // Using these functions does not require a justification, but once we convert 208 // all code to use strongly typed units they should not be needed any longer. 209 template <class TargetUnits> 210 gfx::CoordTyped<TargetUnits> ViewAs(const gfx::Coord& aCoord) { 211 return gfx::CoordTyped<TargetUnits>(aCoord.value); 212 } 213 template <class TargetUnits> 214 gfx::PointTyped<TargetUnits> ViewAs(const gfxPoint& aPoint) { 215 return gfx::PointTyped<TargetUnits>(aPoint.x, aPoint.y); 216 } 217 template <class TargetUnits> 218 gfx::PointTyped<TargetUnits> ViewAs(const gfx::Point& aPoint) { 219 return gfx::PointTyped<TargetUnits>(aPoint.x, aPoint.y); 220 } 221 template <class TargetUnits> 222 gfx::RectTyped<TargetUnits> ViewAs(const gfx::Rect& aRect) { 223 return gfx::RectTyped<TargetUnits>(aRect.x, aRect.y, aRect.Width(), 224 aRect.Height()); 225 } 226 template <class TargetUnits> 227 gfx::IntSizeTyped<TargetUnits> ViewAs(const nsIntSize& aSize) { 228 return gfx::IntSizeTyped<TargetUnits>(aSize.width, aSize.height); 229 } 230 template <class TargetUnits> 231 gfx::IntPointTyped<TargetUnits> ViewAs(const nsIntPoint& aPoint) { 232 return gfx::IntPointTyped<TargetUnits>(aPoint.x, aPoint.y); 233 } 234 template <class TargetUnits> 235 gfx::IntRectTyped<TargetUnits> ViewAs(const nsIntRect& aRect) { 236 return gfx::IntRectTyped<TargetUnits>(aRect.x, aRect.y, aRect.Width(), 237 aRect.Height()); 238 } 239 template <class TargetUnits> 240 gfx::IntRegionTyped<TargetUnits> ViewAs(const nsIntRegion& aRegion) { 241 return gfx::IntRegionTyped<TargetUnits>::FromUnknownRegion(aRegion); 242 } 243 // Unlike the other functions in this category, these functions take the 244 // target matrix or scale type, rather than its source and target unit 245 // types, as the template argument, so an example invocation is: 246 // ViewAs<ScreenToLayerMatrix4x4>(untypedMatrix) 247 // The reason is that if it took the source and target unit types as two 248 // template arguments, there may be some confusion as to which is the 249 // source and which is the target. 250 template <class TypedScale> 251 TypedScale ViewAs(const Scale2D& aScale) { 252 return TypedScale(aScale.xScale, aScale.yScale); 253 } 254 template <class TypedMatrix> 255 TypedMatrix ViewAs(const gfx::Matrix4x4& aMatrix) { 256 return TypedMatrix::FromUnknownMatrix(aMatrix); 257 } 258 259 // Convenience functions for transforming an entity from one strongly-typed 260 // coordinate system to another using the provided transformation matrix. 261 template <typename TargetUnits, typename SourceUnits> 262 static gfx::PointTyped<TargetUnits> TransformBy( 263 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 264 const gfx::PointTyped<SourceUnits>& aPoint) { 265 return aTransform.TransformPoint(aPoint); 266 } 267 template <typename TargetUnits, typename SourceUnits> 268 static gfx::IntPointTyped<TargetUnits> TransformBy( 269 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 270 const gfx::IntPointTyped<SourceUnits>& aPoint) { 271 return RoundedToInt( 272 TransformBy(aTransform, gfx::PointTyped<SourceUnits>(aPoint))); 273 } 274 template <typename TargetUnits, typename SourceUnits> 275 static gfx::RectTyped<TargetUnits> TransformBy( 276 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 277 const gfx::RectTyped<SourceUnits>& aRect) { 278 return aTransform.TransformBounds(aRect); 279 } 280 template <typename TargetUnits, typename SourceUnits> 281 static gfx::IntRectTyped<TargetUnits> TransformBy( 282 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 283 const gfx::IntRectTyped<SourceUnits>& aRect) { 284 return RoundedToInt( 285 TransformBy(aTransform, gfx::RectTyped<SourceUnits>(aRect))); 286 } 287 template <typename TargetUnits, typename SourceUnits> 288 static gfx::IntRegionTyped<TargetUnits> TransformBy( 289 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 290 const gfx::IntRegionTyped<SourceUnits>& aRegion) { 291 return ViewAs<TargetUnits>( 292 aRegion.ToUnknownRegion().Transform(aTransform.ToUnknownMatrix())); 293 } 294 295 // Transform |aVector|, which is anchored at |aAnchor|, by the given transform 296 // matrix, yielding a point in |TargetUnits|. 297 // The anchor is necessary because with 3D tranforms, the location of the 298 // vector can affect the result of the transform. 299 template <typename TargetUnits, typename SourceUnits> 300 static gfx::PointTyped<TargetUnits> TransformVector( 301 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 302 const gfx::PointTyped<SourceUnits>& aVector, 303 const gfx::PointTyped<SourceUnits>& aAnchor) { 304 gfx::PointTyped<TargetUnits> transformedStart = 305 TransformBy(aTransform, aAnchor); 306 gfx::PointTyped<TargetUnits> transformedEnd = 307 TransformBy(aTransform, aAnchor + aVector); 308 return transformedEnd - transformedStart; 309 } 310 311 // UntransformBy() and UntransformVector() are like TransformBy() and 312 // TransformVector(), respectively, but are intended for cases where 313 // the transformation matrix is the inverse of a 3D projection. When 314 // using such transforms, the resulting Point4D is only meaningful 315 // if it has a positive w-coordinate. To handle this, these functions 316 // return a Maybe object which contains a value if and only if the 317 // result is meaningful 318 template <typename TargetUnits, typename SourceUnits> 319 static Maybe<gfx::PointTyped<TargetUnits>> UntransformBy( 320 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 321 const gfx::PointTyped<SourceUnits>& aPoint) { 322 gfx::Point4DTyped<TargetUnits> point = aTransform.ProjectPoint(aPoint); 323 if (!point.HasPositiveWCoord()) { 324 return Nothing(); 325 } 326 return Some(point.As2DPoint()); 327 } 328 template <typename TargetUnits, typename SourceUnits> 329 static Maybe<gfx::IntPointTyped<TargetUnits>> UntransformBy( 330 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 331 const gfx::IntPointTyped<SourceUnits>& aPoint) { 332 gfx::PointTyped<SourceUnits> p = aPoint; 333 gfx::Point4DTyped<TargetUnits> point = aTransform.ProjectPoint(p); 334 if (!point.HasPositiveWCoord()) { 335 return Nothing(); 336 } 337 return Some(RoundedToInt(point.As2DPoint())); 338 } 339 340 // The versions of UntransformBy() that take a rectangle also take a clip, 341 // which represents the bounds within which the target must fall. The 342 // result of the transform is intersected with this clip, and is considered 343 // meaningful if the intersection is not empty. 344 template <typename TargetUnits, typename SourceUnits> 345 static Maybe<gfx::RectTyped<TargetUnits>> UntransformBy( 346 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 347 const gfx::RectTyped<SourceUnits>& aRect, 348 const gfx::RectTyped<TargetUnits>& aClip) { 349 gfx::RectTyped<TargetUnits> rect = aTransform.ProjectRectBounds(aRect, aClip); 350 if (rect.IsEmpty()) { 351 return Nothing(); 352 } 353 return Some(rect); 354 } 355 template <typename TargetUnits, typename SourceUnits> 356 static Maybe<gfx::IntRectTyped<TargetUnits>> UntransformBy( 357 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 358 const gfx::IntRectTyped<SourceUnits>& aRect, 359 const gfx::IntRectTyped<TargetUnits>& aClip) { 360 gfx::RectTyped<TargetUnits> rect = aTransform.ProjectRectBounds(aRect, aClip); 361 if (rect.IsEmpty()) { 362 return Nothing(); 363 } 364 return Some(RoundedToInt(rect)); 365 } 366 367 template <typename TargetUnits, typename SourceUnits> 368 static Maybe<gfx::PointTyped<TargetUnits>> UntransformVector( 369 const gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& aTransform, 370 const gfx::PointTyped<SourceUnits>& aVector, 371 const gfx::PointTyped<SourceUnits>& aAnchor) { 372 gfx::Point4DTyped<TargetUnits> projectedAnchor = 373 aTransform.ProjectPoint(aAnchor); 374 gfx::Point4DTyped<TargetUnits> projectedTarget = 375 aTransform.ProjectPoint(aAnchor + aVector); 376 if (!projectedAnchor.HasPositiveWCoord() || 377 !projectedTarget.HasPositiveWCoord()) { 378 return Nothing(); 379 } 380 return Some(projectedTarget.As2DPoint() - projectedAnchor.As2DPoint()); 381 } 382 383 } // namespace mozilla 384 385 #endif