1 /*
2  * Copyright 2012 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 SkPathOpsCubic_DEFINED
9 #define SkPathOpsCubic_DEFINED
10 
11 #include "include/core/SkPath.h"
12 #include "src/core/SkArenaAlloc.h"
13 #include "src/pathops/SkPathOpsTCurve.h"
14 
15 struct SkDCubicPair;
16 
17 struct SkDCubic {
18     static const int kPointCount = 4;
19     static const int kPointLast = kPointCount - 1;
20     static const int kMaxIntersections = 9;
21 
22     enum SearchAxis {
23         kXAxis,
24         kYAxis
25     };
26 
collapsedSkDCubic27     bool collapsed() const {
28         return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2])
29                 && fPts[0].approximatelyEqual(fPts[3]);
30     }
31 
controlsInsideSkDCubic32     bool controlsInside() const {
33         SkDVector v01 = fPts[0] - fPts[1];
34         SkDVector v02 = fPts[0] - fPts[2];
35         SkDVector v03 = fPts[0] - fPts[3];
36         SkDVector v13 = fPts[1] - fPts[3];
37         SkDVector v23 = fPts[2] - fPts[3];
38         return v03.dot(v01) > 0 && v03.dot(v02) > 0 && v03.dot(v13) > 0 && v03.dot(v23) > 0;
39     }
40 
IsConicSkDCubic41     static bool IsConic() { return false; }
42 
43     const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
44     SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
45 
46     void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
47     double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
48     double calcPrecision() const;
49     SkDCubicPair chopAt(double t) const;
50     static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
51     static int ComplexBreak(const SkPoint pts[4], SkScalar* t);
52     int convexHull(char order[kPointCount]) const;
53 
debugInitSkDCubic54     void debugInit() {
55         sk_bzero(fPts, sizeof(fPts));
56     }
57 
58     void debugSet(const SkDPoint* pts);
59 
60     void dump() const;  // callable from the debugger when the implementation code is linked in
61     void dumpID(int id) const;
62     void dumpInner() const;
63     SkDVector dxdyAtT(double t) const;
64     bool endsAreExtremaInXOrY() const;
65     static int FindExtrema(const double src[], double tValue[2]);
66     int findInflections(double tValues[2]) const;
67 
FindInflectionsSkDCubic68     static int FindInflections(const SkPoint a[kPointCount], double tValues[2]) {
69         SkDCubic cubic;
70         return cubic.set(a).findInflections(tValues);
71     }
72 
73     int findMaxCurvature(double tValues[]) const;
74 
75 #ifdef SK_DEBUG
globalStateSkDCubic76     SkOpGlobalState* globalState() const { return fDebugGlobalState; }
77 #endif
78 
79     bool hullIntersects(const SkDCubic& c2, bool* isLinear) const;
80     bool hullIntersects(const SkDConic& c, bool* isLinear) const;
81     bool hullIntersects(const SkDQuad& c2, bool* isLinear) const;
82     bool hullIntersects(const SkDPoint* pts, int ptCount, bool* isLinear) const;
83     bool isLinear(int startIndex, int endIndex) const;
maxIntersectionsSkDCubic84     static int maxIntersections() { return kMaxIntersections; }
85     bool monotonicInX() const;
86     bool monotonicInY() const;
87     void otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const;
pointCountSkDCubic88     static int pointCount() { return kPointCount; }
pointLastSkDCubic89     static int pointLast() { return kPointLast; }
90     SkDPoint ptAtT(double t) const;
91     static int RootsReal(double A, double B, double C, double D, double t[3]);
92     static int RootsValidT(const double A, const double B, const double C, double D, double s[3]);
93 
94     int searchRoots(double extremes[6], int extrema, double axisIntercept,
95                     SearchAxis xAxis, double* validRoots) const;
96 
97     bool toFloatPoints(SkPoint* ) const;
98     /**
99      *  Return the number of valid roots (0 < root < 1) for this cubic intersecting the
100      *  specified horizontal line.
101      */
102     int horizontalIntersect(double yIntercept, double roots[3]) const;
103     /**
104      *  Return the number of valid roots (0 < root < 1) for this cubic intersecting the
105      *  specified vertical line.
106      */
107     int verticalIntersect(double xIntercept, double roots[3]) const;
108 
109 // add debug only global pointer so asserts can be skipped by fuzzers
setSkDCubic110     const SkDCubic& set(const SkPoint pts[kPointCount]
111             SkDEBUGPARAMS(SkOpGlobalState* state = nullptr)) {
112         fPts[0] = pts[0];
113         fPts[1] = pts[1];
114         fPts[2] = pts[2];
115         fPts[3] = pts[3];
116         SkDEBUGCODE(fDebugGlobalState = state);
117         return *this;
118     }
119 
120     SkDCubic subDivide(double t1, double t2) const;
subDivideSkDCubic121     void subDivide(double t1, double t2, SkDCubic* c) const { *c = this->subDivide(t1, t2); }
122 
SubDivideSkDCubic123     static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
124         SkDCubic cubic;
125         return cubic.set(a).subDivide(t1, t2);
126     }
127 
128     void subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint p[2]) const;
129 
SubDivideSkDCubic130     static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
131                           double t2, SkDPoint p[2]) {
132         SkDCubic cubic;
133         cubic.set(pts).subDivide(a, d, t1, t2, p);
134     }
135 
136     double top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const;
137     SkDQuad toQuad() const;
138 
139     static const int gPrecisionUnit;
140     SkDPoint fPts[kPointCount];
141     SkDEBUGCODE(SkOpGlobalState* fDebugGlobalState);
142 };
143 
144 /* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
145    that computes the other two. Note that:
146 
147    one ^ two == 3 for (0, 3), (1, 2)
148    one ^ two <  3 for (0, 1), (0, 2), (1, 3), (2, 3)
149    3 - (one ^ two) is either 0, 1, or 2
150    1 >> (3 - (one ^ two)) is either 0 or 1
151 thus:
152    returned == 2 for (0, 3), (1, 2)
153    returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
154 given that:
155    (0, 3) ^ 2 -> (2, 1)  (1, 2) ^ 2 -> (3, 0)
156    (0, 1) ^ 3 -> (3, 2)  (0, 2) ^ 3 -> (3, 1)  (1, 3) ^ 3 -> (2, 0)  (2, 3) ^ 3 -> (1, 0)
157 */
other_two(int one,int two)158 inline int other_two(int one, int two) {
159     return 1 >> (3 - (one ^ two)) ^ 3;
160 }
161 
162 struct SkDCubicPair {
firstSkDCubicPair163     SkDCubic first() const {
164 #ifdef SK_DEBUG
165         SkDCubic result;
166         result.debugSet(&pts[0]);
167         return result;
168 #else
169         return (const SkDCubic&) pts[0];
170 #endif
171     }
secondSkDCubicPair172     SkDCubic second() const {
173 #ifdef SK_DEBUG
174         SkDCubic result;
175         result.debugSet(&pts[3]);
176         return result;
177 #else
178         return (const SkDCubic&) pts[3];
179 #endif
180     }
181     SkDPoint pts[7];
182 };
183 
184 class SkTCubic : public SkTCurve {
185 public:
186     SkDCubic fCubic;
187 
SkTCubic()188     SkTCubic() {}
189 
SkTCubic(const SkDCubic & c)190     SkTCubic(const SkDCubic& c)
191         : fCubic(c) {
192     }
193 
~SkTCubic()194     ~SkTCubic() override {}
195 
196     const SkDPoint& operator[](int n) const override { return fCubic[n]; }
197     SkDPoint& operator[](int n) override { return fCubic[n]; }
198 
collapsed()199     bool collapsed() const override { return fCubic.collapsed(); }
controlsInside()200     bool controlsInside() const override { return fCubic.controlsInside(); }
debugInit()201     void debugInit() override { return fCubic.debugInit(); }
202 #if DEBUG_T_SECT
dumpID(int id)203     void dumpID(int id) const override { return fCubic.dumpID(id); }
204 #endif
dxdyAtT(double t)205     SkDVector dxdyAtT(double t) const override { return fCubic.dxdyAtT(t); }
206 #ifdef SK_DEBUG
globalState()207     SkOpGlobalState* globalState() const override { return fCubic.globalState(); }
208 #endif
209     bool hullIntersects(const SkDQuad& quad, bool* isLinear) const override;
210     bool hullIntersects(const SkDConic& conic, bool* isLinear) const override;
211 
hullIntersects(const SkDCubic & cubic,bool * isLinear)212     bool hullIntersects(const SkDCubic& cubic, bool* isLinear) const override {
213         return cubic.hullIntersects(fCubic, isLinear);
214     }
215 
hullIntersects(const SkTCurve & curve,bool * isLinear)216     bool hullIntersects(const SkTCurve& curve, bool* isLinear) const override {
217         return curve.hullIntersects(fCubic, isLinear);
218     }
219 
220     int intersectRay(SkIntersections* i, const SkDLine& line) const override;
IsConic()221     bool IsConic() const override { return false; }
make(SkArenaAlloc & heap)222     SkTCurve* make(SkArenaAlloc& heap) const override { return heap.make<SkTCubic>(); }
223 
maxIntersections()224     int maxIntersections() const override { return SkDCubic::kMaxIntersections; }
225 
otherPts(int oddMan,const SkDPoint * endPt[2])226     void otherPts(int oddMan, const SkDPoint* endPt[2]) const override {
227         fCubic.otherPts(oddMan, endPt);
228     }
229 
pointCount()230     int pointCount() const override { return SkDCubic::kPointCount; }
pointLast()231     int pointLast() const override { return SkDCubic::kPointLast; }
ptAtT(double t)232     SkDPoint ptAtT(double t) const override { return fCubic.ptAtT(t); }
233     void setBounds(SkDRect* ) const override;
234 
subDivide(double t1,double t2,SkTCurve * curve)235     void subDivide(double t1, double t2, SkTCurve* curve) const override {
236         ((SkTCubic*) curve)->fCubic = fCubic.subDivide(t1, t2);
237     }
238 };
239 
240 #endif
241