1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef skgpu_graphite_geom_Transform_DEFINED 9 #define skgpu_graphite_geom_Transform_DEFINED 10 11 #include "include/core/SkM44.h" 12 #include "include/core/SkMatrix.h" 13 #include "include/private/base/SkAssert.h" 14 #include "include/private/base/SkFloatingPoint.h" 15 16 #include <utility> 17 18 namespace skgpu::graphite { 19 20 class Rect; 21 22 // Transform encapsulates an SkM44 matrix, its inverse, and other properties dependent on the 23 // original matrix value that are useful when rendering. 24 class Transform { 25 public: 26 // Type classifies the transform into coarse categories so that certain optimizations or 27 // properties can be queried efficiently 28 enum class Type : unsigned { 29 // Applying the matrix to a vector or point is a no-op, so could be skipped entirely. 30 kIdentity, 31 // The matrix transforms a rect to another rect, without mirrors or rotations, so both 32 // pre-and-post transform coordinates can be exactly represented as rects. 33 kSimpleRectStaysRect, 34 // The matrix transforms a rect to another rect, but may mirror or rotate the corners 35 // relative to each other. This means that the post-transformed rect completely fills 36 // that space. 37 kRectStaysRect, 38 // The matrix transform may have skew or rotation, so a mapped rect does not fill space, 39 // but there is no need to perform perspective division or w-plane clipping. This also 40 // includes orthographic projections. 41 kAffine, 42 // The matrix includes perspective and requires further projection to 2D, so care must be 43 // taken when w is less than or near 0, and homogeneous division and perspective-correct 44 // interpolation are needed when rendering. 45 kPerspective, 46 // The matrix is not invertible or not finite, so should not be used to draw. 47 kInvalid, 48 }; 49 50 explicit Transform(const SkM44& m); 51 Transform(const Transform& t) = default; 52 Identity()53 static constexpr Transform Identity() { 54 return Transform(SkM44(), SkM44(), Type::kIdentity, 1.f, 1.f); 55 } Invalid()56 static constexpr Transform Invalid() { 57 return Transform(SkM44(SkM44::kNaN_Constructor), SkM44(SkM44::kNaN_Constructor), 58 Type::kInvalid, 1.f, 1.f); 59 } 60 Translate(float x,float y)61 static inline Transform Translate(float x, float y) { 62 if (x == 0.f && y == 0.f) { 63 return Identity(); 64 } else if (SkIsFinite(x, y)) { 65 return Transform(SkM44::Translate(x, y), SkM44::Translate(-x, -y), 66 Type::kSimpleRectStaysRect, 1.f, 1.f); 67 } else { 68 return Invalid(); 69 } 70 } 71 Inverse(const Transform & t)72 static inline Transform Inverse(const Transform& t) { 73 return Transform(t.fInvM, t.fM, t.fType, 1.f / t.fMaxScaleFactor, 1.f / t.fMinScaleFactor); 74 } 75 76 Transform& operator=(const Transform& t) = default; 77 78 operator const SkM44&() const { return fM; } SkMatrix()79 operator SkMatrix() const { return fM.asM33(); } 80 81 bool operator!=(const Transform& t) const { return !(*this == t); } 82 bool operator==(const Transform& t) const { 83 return this->valid() == t.valid() && (!this->valid() || fM == t.fM); 84 } 85 matrix()86 const SkM44& matrix() const { return fM; } inverse()87 const SkM44& inverse() const { return fInvM; } 88 type()89 Type type() const { return fType; } valid()90 bool valid() const { return fType != Type::kInvalid; } 91 92 // Return the {min,max} scale factor at the pre-transformed location 'p'. A unit circle about 93 // 'p' transformed by this Transform will be contained in an ellipse with radii equal to 'min' 94 // and 'max', e.g. moving 1 local unit will move at least 'min' pixels and at most 'max' pixels 95 std::pair<float, float> scaleFactors(const SkV2& p) const; 96 97 // This is valid for non-projection types and 1.0 for projection matrices. maxScaleFactor()98 float maxScaleFactor() const { 99 SkASSERT(this->valid()); 100 return fMaxScaleFactor; 101 } 102 103 // Return the minimum distance needed to move in local (pre-transform) space to ensure that the 104 // transformed coordinates are at least 1px away from the original mapped point. This minimum 105 // distance is specific to the given local 'bounds' since the scale factors change with 106 // perspective. 107 // 108 // If the bounds would be clipped by the w=0 plane or otherwise is ill-conditioned, this will 109 // return positive infinity. 110 float localAARadius(const Rect& bounds) const; 111 112 Rect mapRect(const Rect& rect) const; 113 Rect inverseMapRect(const Rect& rect) const; 114 115 void mapPoints(const Rect& localRect, SkV4 deviceOut[4]) const; 116 void mapPoints(const SkV2* localIn, SkV4* deviceOut, int count) const; 117 118 void mapPoints(const SkV4* localIn, SkV4* deviceOut, int count) const; 119 void inverseMapPoints(const SkV4* deviceIn, SkV4* localOut, int count) const; 120 121 // Returns a transform equal to the pre- or post-translation of this matrix preTranslate(float x,float y)122 Transform preTranslate(float x, float y) const { 123 return this->concat(SkM44::Translate(x, y)); 124 } postTranslate(float x,float y)125 Transform postTranslate(float x, float y) const { 126 return Translate(x, y).concat(*this); 127 } 128 129 // Returns a transform equal to (this * t) concat(const Transform & t)130 Transform concat(const Transform& t) const { 131 SkASSERT(this->valid()); 132 return Transform(fM * t.fM); 133 } concat(const SkM44 & t)134 Transform concat(const SkM44& t) const { 135 SkASSERT(this->valid()); 136 return Transform(fM * t); 137 } 138 139 // Returns a transform equal to (this * t^-1) concatInverse(const Transform & t)140 Transform concatInverse(const Transform& t) const { 141 SkASSERT(this->valid()); 142 return Transform(fM * t.fInvM); 143 } concatInverse(const SkM44 & t)144 Transform concatInverse(const SkM44& t) const { 145 SkASSERT(this->valid()); 146 // Saves a multiply compared to inverting just 't' and calculating both fM*t^-1 and t*fInvM 147 // (t * this^-1)^-1 = this * t^-1 148 return Inverse(Transform(t * fInvM)); 149 } 150 151 private: 152 // Used for static factories that have known properties Transform(const SkM44 & m,const SkM44 & invM,Type type,float minScale,float maxScale)153 constexpr Transform(const SkM44& m, const SkM44& invM, Type type, 154 float minScale, float maxScale) 155 : fM(m) 156 , fInvM(invM) 157 , fType(type) 158 , fMinScaleFactor(minScale) 159 , fMaxScaleFactor(maxScale) {} 160 161 SkM44 fM; 162 SkM44 fInvM; // M^-1 163 Type fType; 164 165 // These are cached for non-projection transforms since they are constant; projection matrices 166 // must be computed per point, and these values are ignored. 167 float fMinScaleFactor = 1.f; 168 float fMaxScaleFactor = 1.f; 169 }; 170 171 } // namespace skgpu::graphite 172 173 #endif // skgpu_graphite_geom_Transform_DEFINED 174