xref: /aosp_15_r20/external/skia/src/core/SkMatrixPriv.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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