1 /* 2 * Copyright 2016 Google Inc. 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 SkMatrixPriv_DEFINE 9 #define SkMatrixPriv_DEFINE 10 11 #include "include/core/SkM44.h" 12 #include "include/core/SkMatrix.h" 13 #include "include/core/SkPoint.h" 14 #include "include/core/SkRect.h" 15 #include "include/core/SkScalar.h" 16 #include "include/core/SkTypes.h" 17 #include "src/base/SkVx.h" 18 19 #include <cstdint> 20 #include <cstring> 21 struct SkPoint3; 22 23 class SkMatrixPriv { 24 public: 25 enum { 26 // writeTo/readFromMemory will never return a value larger than this 27 kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t), 28 }; 29 WriteToMemory(const SkMatrix & matrix,void * buffer)30 static size_t WriteToMemory(const SkMatrix& matrix, void* buffer) { 31 return matrix.writeToMemory(buffer); 32 } 33 ReadFromMemory(SkMatrix * matrix,const void * buffer,size_t length)34 static size_t ReadFromMemory(SkMatrix* matrix, const void* buffer, size_t length) { 35 return matrix->readFromMemory(buffer, length); 36 } 37 38 typedef SkMatrix::MapXYProc MapXYProc; 39 typedef SkMatrix::MapPtsProc MapPtsProc; 40 41 GetMapPtsProc(const SkMatrix & matrix)42 static MapPtsProc GetMapPtsProc(const SkMatrix& matrix) { 43 return SkMatrix::GetMapPtsProc(matrix.getType()); 44 } 45 GetMapXYProc(const SkMatrix & matrix)46 static MapXYProc GetMapXYProc(const SkMatrix& matrix) { 47 return SkMatrix::GetMapXYProc(matrix.getType()); 48 } 49 50 /** 51 * Attempt to map the rect through the inverse of the matrix. If it is not invertible, 52 * then this returns false and dst is unchanged. 53 */ InverseMapRect(const SkMatrix & mx,SkRect * dst,const SkRect & src)54 [[nodiscard]] static bool InverseMapRect(const SkMatrix& mx, SkRect* dst, const SkRect& src) { 55 if (mx.isScaleTranslate()) { 56 // A scale-translate matrix with a 0 scale factor is not invertible. 57 if (mx.getScaleX() == 0.f || mx.getScaleY() == 0.f) { 58 return false; 59 } 60 61 const SkScalar tx = mx.getTranslateX(); 62 const SkScalar ty = mx.getTranslateY(); 63 // mx maps coordinates as ((sx*x + tx), (sy*y + ty)) so the inverse is 64 // ((x - tx)/sx), (y - ty)/sy). If sx or sy are negative, we have to swap the edge 65 // values to maintain a sorted rect. 66 auto inverted = skvx::float4::Load(&src.fLeft); 67 inverted -= skvx::float4(tx, ty, tx, ty); 68 69 if (mx.getType() > SkMatrix::kTranslate_Mask) { 70 const SkScalar sx = 1.f / mx.getScaleX(); 71 const SkScalar sy = 1.f / mx.getScaleY(); 72 inverted *= skvx::float4(sx, sy, sx, sy); 73 if (sx < 0.f && sy < 0.f) { 74 inverted = skvx::shuffle<2, 3, 0, 1>(inverted); // swap L|R and T|B 75 } else if (sx < 0.f) { 76 inverted = skvx::shuffle<2, 1, 0, 3>(inverted); // swap L|R 77 } else if (sy < 0.f) { 78 inverted = skvx::shuffle<0, 3, 2, 1>(inverted); // swap T|B 79 } 80 } 81 inverted.store(&dst->fLeft); 82 return true; 83 } 84 85 // general case 86 SkMatrix inverse; 87 if (mx.invert(&inverse)) { 88 inverse.mapRect(dst, src); 89 return true; 90 } 91 return false; 92 } 93 94 /** Maps count pts, skipping stride bytes to advance from one SkPoint to the next. 95 Points are mapped by multiplying each SkPoint by SkMatrix. Given: 96 97 | A B C | | x | 98 Matrix = | D E F |, pt = | y | 99 | G H I | | 1 | 100 101 each resulting pts SkPoint is computed as: 102 103 |A B C| |x| Ax+By+C Dx+Ey+F 104 Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 105 |G H I| |1| Gx+Hy+I Gx+Hy+I 106 107 @param mx matrix used to map the points 108 @param pts storage for mapped points 109 @param stride size of record starting with SkPoint, in bytes 110 @param count number of points to transform 111 */ MapPointsWithStride(const SkMatrix & mx,SkPoint pts[],size_t stride,int count)112 static void MapPointsWithStride(const SkMatrix& mx, SkPoint pts[], size_t stride, int count) { 113 SkASSERT(stride >= sizeof(SkPoint)); 114 SkASSERT(0 == stride % sizeof(SkScalar)); 115 116 SkMatrix::TypeMask tm = mx.getType(); 117 118 if (SkMatrix::kIdentity_Mask == tm) { 119 return; 120 } 121 if (SkMatrix::kTranslate_Mask == tm) { 122 const SkScalar tx = mx.getTranslateX(); 123 const SkScalar ty = mx.getTranslateY(); 124 skvx::float2 trans(tx, ty); 125 for (int i = 0; i < count; ++i) { 126 (skvx::float2::Load(&pts->fX) + trans).store(&pts->fX); 127 pts = (SkPoint*)((intptr_t)pts + stride); 128 } 129 return; 130 } 131 // Insert other special-cases here (e.g. scale+translate) 132 133 // general case 134 SkMatrix::MapXYProc proc = mx.getMapXYProc(); 135 for (int i = 0; i < count; ++i) { 136 proc(mx, pts->fX, pts->fY, pts); 137 pts = (SkPoint*)((intptr_t)pts + stride); 138 } 139 } 140 141 /** Maps src SkPoint array of length count to dst SkPoint array, skipping stride bytes 142 to advance from one SkPoint to the next. 143 Points are mapped by multiplying each SkPoint by SkMatrix. Given: 144 145 | A B C | | x | 146 Matrix = | D E F |, src = | y | 147 | G H I | | 1 | 148 149 each resulting dst SkPoint is computed as: 150 151 |A B C| |x| Ax+By+C Dx+Ey+F 152 Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 153 |G H I| |1| Gx+Hy+I Gx+Hy+I 154 155 @param mx matrix used to map the points 156 @param dst storage for mapped points 157 @param src points to transform 158 @param stride size of record starting with SkPoint, in bytes 159 @param count number of points to transform 160 */ MapPointsWithStride(const SkMatrix & mx,SkPoint dst[],size_t dstStride,const SkPoint src[],size_t srcStride,int count)161 static void MapPointsWithStride(const SkMatrix& mx, SkPoint dst[], size_t dstStride, 162 const SkPoint src[], size_t srcStride, int count) { 163 SkASSERT(srcStride >= sizeof(SkPoint)); 164 SkASSERT(dstStride >= sizeof(SkPoint)); 165 SkASSERT(0 == srcStride % sizeof(SkScalar)); 166 SkASSERT(0 == dstStride % sizeof(SkScalar)); 167 for (int i = 0; i < count; ++i) { 168 mx.mapPoints(dst, src, 1); 169 src = (SkPoint*)((intptr_t)src + srcStride); 170 dst = (SkPoint*)((intptr_t)dst + dstStride); 171 } 172 } 173 174 static void MapHomogeneousPointsWithStride(const SkMatrix& mx, SkPoint3 dst[], size_t dstStride, 175 const SkPoint3 src[], size_t srcStride, int count); 176 PostIDiv(SkMatrix * matrix,int divx,int divy)177 static bool PostIDiv(SkMatrix* matrix, int divx, int divy) { 178 return matrix->postIDiv(divx, divy); 179 } 180 CheapEqual(const SkMatrix & a,const SkMatrix & b)181 static bool CheapEqual(const SkMatrix& a, const SkMatrix& b) { 182 return &a == &b || 0 == memcmp(a.fMat, b.fMat, sizeof(a.fMat)); 183 } 184 M44ColMajor(const SkM44 & m)185 static const SkScalar* M44ColMajor(const SkM44& m) { return m.fMat; } 186 187 // This is legacy functionality that only checks the 3x3 portion. The matrix could have Z-based 188 // shear, or other complex behavior. Only use this if you're planning to use the information 189 // to accelerate some purely 2D operation. IsScaleTranslateAsM33(const SkM44 & m)190 static bool IsScaleTranslateAsM33(const SkM44& m) { 191 return m.rc(1,0) == 0 && m.rc(3,0) == 0 && 192 m.rc(0,1) == 0 && m.rc(3,1) == 0 && 193 m.rc(3,3) == 1; 194 195 } 196 197 // Map the four corners of 'r' and return the bounding box of those points. The four corners of 198 // 'r' are assumed to have z = 0 and w = 1. If the matrix has perspective, the returned 199 // rectangle will be the bounding box of the projected points after being clipped to w > 0. 200 static SkRect MapRect(const SkM44& m, const SkRect& r); 201 202 // Returns the differential area scale factor for a local point 'p' that will be transformed 203 // by 'm' (which may have perspective). If 'm' does not have perspective, this scale factor is 204 // constant regardless of 'p'; when it does have perspective, it is specific to that point. 205 // 206 // This can be crudely thought of as "device pixel area" / "local pixel area" at 'p'. 207 // 208 // Returns positive infinity if the transformed homogeneous point has w <= 0. 209 static SkScalar DifferentialAreaScale(const SkMatrix& m, const SkPoint& p); 210 211 // Determines if the transformation m applied to the bounds can be approximated by 212 // an affine transformation, i.e., the perspective part of the transformation has little 213 // visible effect. 214 static bool NearlyAffine(const SkMatrix& m, 215 const SkRect& bounds, 216 SkScalar tolerance = SK_ScalarNearlyZero); 217 218 static SkScalar ComputeResScaleForStroking(const SkMatrix& matrix); 219 }; 220 221 #endif 222