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 static bool SK_WARN_UNUSED_RESULT InverseMapRect(const SkMatrix& mx, 55 SkRect* dst, const SkRect& src) { 56 if (mx.getType() <= SkMatrix::kTranslate_Mask) { 57 SkScalar tx = mx.getTranslateX(); 58 SkScalar ty = mx.getTranslateY(); 59 skvx::float4 trans(tx, ty, tx, ty); 60 (skvx::float4::Load(&src.fLeft) - trans).store(&dst->fLeft); 61 return true; 62 } 63 // Insert other special-cases here (e.g. scale+translate) 64 65 // general case 66 SkMatrix inverse; 67 if (mx.invert(&inverse)) { 68 inverse.mapRect(dst, src); 69 return true; 70 } 71 return false; 72 } 73 74 /** Maps count pts, skipping stride bytes to advance from one SkPoint to the next. 75 Points are mapped by multiplying each SkPoint by SkMatrix. Given: 76 77 | A B C | | x | 78 Matrix = | D E F |, pt = | y | 79 | G H I | | 1 | 80 81 each resulting pts SkPoint is computed as: 82 83 |A B C| |x| Ax+By+C Dx+Ey+F 84 Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 85 |G H I| |1| Gx+Hy+I Gx+Hy+I 86 87 @param mx matrix used to map the points 88 @param pts storage for mapped points 89 @param stride size of record starting with SkPoint, in bytes 90 @param count number of points to transform 91 */ MapPointsWithStride(const SkMatrix & mx,SkPoint pts[],size_t stride,int count)92 static void MapPointsWithStride(const SkMatrix& mx, SkPoint pts[], size_t stride, int count) { 93 SkASSERT(stride >= sizeof(SkPoint)); 94 SkASSERT(0 == stride % sizeof(SkScalar)); 95 96 SkMatrix::TypeMask tm = mx.getType(); 97 98 if (SkMatrix::kIdentity_Mask == tm) { 99 return; 100 } 101 if (SkMatrix::kTranslate_Mask == tm) { 102 const SkScalar tx = mx.getTranslateX(); 103 const SkScalar ty = mx.getTranslateY(); 104 skvx::float2 trans(tx, ty); 105 for (int i = 0; i < count; ++i) { 106 (skvx::float2::Load(&pts->fX) + trans).store(&pts->fX); 107 pts = (SkPoint*)((intptr_t)pts + stride); 108 } 109 return; 110 } 111 // Insert other special-cases here (e.g. scale+translate) 112 113 // general case 114 SkMatrix::MapXYProc proc = mx.getMapXYProc(); 115 for (int i = 0; i < count; ++i) { 116 proc(mx, pts->fX, pts->fY, pts); 117 pts = (SkPoint*)((intptr_t)pts + stride); 118 } 119 } 120 121 /** Maps src SkPoint array of length count to dst SkPoint array, skipping stride bytes 122 to advance from one SkPoint to the next. 123 Points are mapped by multiplying each SkPoint by SkMatrix. Given: 124 125 | A B C | | x | 126 Matrix = | D E F |, src = | y | 127 | G H I | | 1 | 128 129 each resulting dst SkPoint is computed as: 130 131 |A B C| |x| Ax+By+C Dx+Ey+F 132 Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 133 |G H I| |1| Gx+Hy+I Gx+Hy+I 134 135 @param mx matrix used to map the points 136 @param dst storage for mapped points 137 @param src points to transform 138 @param stride size of record starting with SkPoint, in bytes 139 @param count number of points to transform 140 */ MapPointsWithStride(const SkMatrix & mx,SkPoint dst[],size_t dstStride,const SkPoint src[],size_t srcStride,int count)141 static void MapPointsWithStride(const SkMatrix& mx, SkPoint dst[], size_t dstStride, 142 const SkPoint src[], size_t srcStride, int count) { 143 SkASSERT(srcStride >= sizeof(SkPoint)); 144 SkASSERT(dstStride >= sizeof(SkPoint)); 145 SkASSERT(0 == srcStride % sizeof(SkScalar)); 146 SkASSERT(0 == dstStride % sizeof(SkScalar)); 147 for (int i = 0; i < count; ++i) { 148 mx.mapPoints(dst, src, 1); 149 src = (SkPoint*)((intptr_t)src + srcStride); 150 dst = (SkPoint*)((intptr_t)dst + dstStride); 151 } 152 } 153 154 static void MapHomogeneousPointsWithStride(const SkMatrix& mx, SkPoint3 dst[], size_t dstStride, 155 const SkPoint3 src[], size_t srcStride, int count); 156 PostIDiv(SkMatrix * matrix,int divx,int divy)157 static bool PostIDiv(SkMatrix* matrix, int divx, int divy) { 158 return matrix->postIDiv(divx, divy); 159 } 160 CheapEqual(const SkMatrix & a,const SkMatrix & b)161 static bool CheapEqual(const SkMatrix& a, const SkMatrix& b) { 162 return &a == &b || 0 == memcmp(a.fMat, b.fMat, sizeof(a.fMat)); 163 } 164 M44ColMajor(const SkM44 & m)165 static const SkScalar* M44ColMajor(const SkM44& m) { return m.fMat; } 166 167 // This is legacy functionality that only checks the 3x3 portion. The matrix could have Z-based 168 // shear, or other complex behavior. Only use this if you're planning to use the information 169 // to accelerate some purely 2D operation. IsScaleTranslateAsM33(const SkM44 & m)170 static bool IsScaleTranslateAsM33(const SkM44& m) { 171 return m.rc(1,0) == 0 && m.rc(3,0) == 0 && 172 m.rc(0,1) == 0 && m.rc(3,1) == 0 && 173 m.rc(3,3) == 1; 174 175 } 176 177 // Map the four corners of 'r' and return the bounding box of those points. The four corners of 178 // 'r' are assumed to have z = 0 and w = 1. If the matrix has perspective, the returned 179 // rectangle will be the bounding box of the projected points after being clipped to w > 0. 180 static SkRect MapRect(const SkM44& m, const SkRect& r); 181 182 // Returns the differential area scale factor for a local point 'p' that will be transformed 183 // by 'm' (which may have perspective). If 'm' does not have perspective, this scale factor is 184 // constant regardless of 'p'; when it does have perspective, it is specific to that point. 185 // 186 // This can be crudely thought of as "device pixel area" / "local pixel area" at 'p'. 187 // 188 // Returns positive infinity if the transformed homogeneous point has w <= 0. 189 static SkScalar DifferentialAreaScale(const SkMatrix& m, const SkPoint& p); 190 191 // Determines if the transformation m applied to the bounds can be approximated by 192 // an affine transformation, i.e., the perspective part of the transformation has little 193 // visible effect. 194 static bool NearlyAffine(const SkMatrix& m, 195 const SkRect& bounds, 196 SkScalar tolerance = SK_ScalarNearlyZero); 197 198 static SkScalar ComputeResScaleForStroking(const SkMatrix& matrix); 199 }; 200 201 #endif 202