Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/pathops/SkReduceOrder.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "src/pathops/SkReduceOrder.h"
8
9
#include "include/core/SkPoint.h"
10
#include "src/core/SkGeometry.h"
11
#include "src/pathops/SkPathOpsPoint.h"
12
#include "src/pathops/SkPathOpsTypes.h"
13
14
#include <algorithm>
15
#include <cmath>
16
17
0
int SkReduceOrder::reduce(const SkDLine& line) {
18
0
    fLine[0] = line[0];
19
0
    int different = line[0] != line[1];
20
0
    fLine[1] = line[different];
21
0
    return 1 + different;
22
0
}
23
24
275k
static int coincident_line(const SkDQuad& quad, SkDQuad& reduction) {
25
275k
    reduction[0] = reduction[1] = quad[0];
26
275k
    return 1;
27
275k
}
28
29
1.06M
static int reductionLineCount(const SkDQuad& reduction) {
30
1.06M
    return 1 + !reduction[0].approximatelyEqual(reduction[1]);
31
1.06M
}
32
33
98.3k
static int vertical_line(const SkDQuad& quad, SkDQuad& reduction) {
34
98.3k
    reduction[0] = quad[0];
35
98.3k
    reduction[1] = quad[2];
36
98.3k
    return reductionLineCount(reduction);
37
98.3k
}
38
39
135k
static int horizontal_line(const SkDQuad& quad, SkDQuad& reduction) {
40
135k
    reduction[0] = quad[0];
41
135k
    reduction[1] = quad[2];
42
135k
    return reductionLineCount(reduction);
43
135k
}
44
45
static int check_linear(const SkDQuad& quad,
46
8.82M
        int minX, int maxX, int minY, int maxY, SkDQuad& reduction) {
47
8.82M
    if (!quad.isLinear(0, 2)) {
48
7.98M
        return 0;
49
7.98M
    }
50
    // four are colinear: return line formed by outside
51
833k
    reduction[0] = quad[0];
52
833k
    reduction[1] = quad[2];
53
833k
    return reductionLineCount(reduction);
54
8.82M
}
55
56
// reduce to a quadratic or smaller
57
// look for identical points
58
// look for all four points in a line
59
    // note that three points in a line doesn't simplify a cubic
60
// look for approximation with single quadratic
61
    // save approximation with multiple quadratics for later
62
9.33M
int SkReduceOrder::reduce(const SkDQuad& quad) {
63
9.33M
    int index, minX, maxX, minY, maxY;
64
9.33M
    int minXSet, minYSet;
65
9.33M
    minX = maxX = minY = maxY = 0;
66
9.33M
    minXSet = minYSet = 0;
67
27.9M
    for (index = 1; index < 3; ++index) {
68
18.6M
        if (quad[minX].fX > quad[index].fX) {
69
8.02M
            minX = index;
70
8.02M
        }
71
18.6M
        if (quad[minY].fY > quad[index].fY) {
72
8.11M
            minY = index;
73
8.11M
        }
74
18.6M
        if (quad[maxX].fX < quad[index].fX) {
75
7.97M
            maxX = index;
76
7.97M
        }
77
18.6M
        if (quad[maxY].fY < quad[index].fY) {
78
7.99M
            maxY = index;
79
7.99M
        }
80
18.6M
    }
81
37.3M
    for (index = 0; index < 3; ++index) {
82
27.9M
        if (AlmostEqualUlps(quad[index].fX, quad[minX].fX)) {
83
11.0M
            minXSet |= 1 << index;
84
11.0M
        }
85
27.9M
        if (AlmostEqualUlps(quad[index].fY, quad[minY].fY)) {
86
10.9M
            minYSet |= 1 << index;
87
10.9M
        }
88
27.9M
    }
89
9.33M
    if ((minXSet & 0x05) == 0x5 && (minYSet & 0x05) == 0x5) { // test for degenerate
90
        // this quad starts and ends at the same place, so never contributes
91
        // to the fill
92
275k
        return coincident_line(quad, fQuad);
93
275k
    }
94
9.05M
    if (minXSet == 0x7) {  // test for vertical line
95
98.3k
        return vertical_line(quad, fQuad);
96
98.3k
    }
97
8.95M
    if (minYSet == 0x7) {  // test for horizontal line
98
135k
        return horizontal_line(quad, fQuad);
99
135k
    }
100
8.82M
    int result = check_linear(quad, minX, maxX, minY, maxY, fQuad);
101
8.82M
    if (result) {
102
833k
        return result;
103
833k
    }
104
7.98M
    fQuad = quad;
105
7.98M
    return 3;
106
8.82M
}
107
108
////////////////////////////////////////////////////////////////////////////////////
109
110
3.50k
static int coincident_line(const SkDCubic& cubic, SkDCubic& reduction) {
111
3.50k
    reduction[0] = reduction[1] = cubic[0];
112
3.50k
    return 1;
113
3.50k
}
114
115
467k
static int reductionLineCount(const SkDCubic& reduction) {
116
467k
    return 1 + !reduction[0].approximatelyEqual(reduction[1]);
117
467k
}
118
119
27.7k
static int vertical_line(const SkDCubic& cubic, SkDCubic& reduction) {
120
27.7k
    reduction[0] = cubic[0];
121
27.7k
    reduction[1] = cubic[3];
122
27.7k
    return reductionLineCount(reduction);
123
27.7k
}
124
125
57.7k
static int horizontal_line(const SkDCubic& cubic, SkDCubic& reduction) {
126
57.7k
    reduction[0] = cubic[0];
127
57.7k
    reduction[1] = cubic[3];
128
57.7k
    return reductionLineCount(reduction);
129
57.7k
}
130
131
// check to see if it is a quadratic or a line
132
4.52M
static int check_quadratic(const SkDCubic& cubic, SkDCubic& reduction) {
133
4.52M
    double dx10 = cubic[1].fX - cubic[0].fX;
134
4.52M
    double dx23 = cubic[2].fX - cubic[3].fX;
135
4.52M
    double midX = cubic[0].fX + dx10 * 3 / 2;
136
4.52M
    double sideAx = midX - cubic[3].fX;
137
4.52M
    double sideBx = dx23 * 3 / 2;
138
4.52M
    if (approximately_zero(sideAx) ? !approximately_equal(sideAx, sideBx)
139
4.52M
            : !AlmostEqualUlps_Pin(sideAx, sideBx)) {
140
4.35M
        return 0;
141
4.35M
    }
142
167k
    double dy10 = cubic[1].fY - cubic[0].fY;
143
167k
    double dy23 = cubic[2].fY - cubic[3].fY;
144
167k
    double midY = cubic[0].fY + dy10 * 3 / 2;
145
167k
    double sideAy = midY - cubic[3].fY;
146
167k
    double sideBy = dy23 * 3 / 2;
147
167k
    if (approximately_zero(sideAy) ? !approximately_equal(sideAy, sideBy)
148
167k
            : !AlmostEqualUlps_Pin(sideAy, sideBy)) {
149
150k
        return 0;
150
150k
    }
151
17.3k
    reduction[0] = cubic[0];
152
17.3k
    reduction[1].fX = midX;
153
17.3k
    reduction[1].fY = midY;
154
17.3k
    reduction[2] = cubic[3];
155
17.3k
    return 3;
156
167k
}
157
158
static int check_linear(const SkDCubic& cubic,
159
4.90M
        int minX, int maxX, int minY, int maxY, SkDCubic& reduction) {
160
4.90M
    if (!cubic.isLinear(0, 3)) {
161
4.52M
        return 0;
162
4.52M
    }
163
    // four are colinear: return line formed by outside
164
381k
    reduction[0] = cubic[0];
165
381k
    reduction[1] = cubic[3];
166
381k
    return reductionLineCount(reduction);
167
4.90M
}
168
169
/* food for thought:
170
http://objectmix.com/graphics/132906-fast-precision-driven-cubic-quadratic-piecewise-degree-reduction-algos-2-a.html
171
172
Given points c1, c2, c3 and c4 of a cubic Bezier, the points of the
173
corresponding quadratic Bezier are (given in convex combinations of
174
points):
175
176
q1 = (11/13)c1 + (3/13)c2 -(3/13)c3 + (2/13)c4
177
q2 = -c1 + (3/2)c2 + (3/2)c3 - c4
178
q3 = (2/13)c1 - (3/13)c2 + (3/13)c3 + (11/13)c4
179
180
Of course, this curve does not interpolate the end-points, but it would
181
be interesting to see the behaviour of such a curve in an applet.
182
183
--
184
Kalle Rutanen
185
http://kaba.hilvi.org
186
187
*/
188
189
// reduce to a quadratic or smaller
190
// look for identical points
191
// look for all four points in a line
192
    // note that three points in a line doesn't simplify a cubic
193
// look for approximation with single quadratic
194
    // save approximation with multiple quadratics for later
195
4.99M
int SkReduceOrder::reduce(const SkDCubic& cubic, Quadratics allowQuadratics) {
196
4.99M
    int index, minX, maxX, minY, maxY;
197
4.99M
    int minXSet, minYSet;
198
4.99M
    minX = maxX = minY = maxY = 0;
199
4.99M
    minXSet = minYSet = 0;
200
19.9M
    for (index = 1; index < 4; ++index) {
201
14.9M
        if (cubic[minX].fX > cubic[index].fX) {
202
6.80M
            minX = index;
203
6.80M
        }
204
14.9M
        if (cubic[minY].fY > cubic[index].fY) {
205
6.81M
            minY = index;
206
6.81M
        }
207
14.9M
        if (cubic[maxX].fX < cubic[index].fX) {
208
6.94M
            maxX = index;
209
6.94M
        }
210
14.9M
        if (cubic[maxY].fY < cubic[index].fY) {
211
6.69M
            maxY = index;
212
6.69M
        }
213
14.9M
    }
214
24.9M
    for (index = 0; index < 4; ++index) {
215
19.9M
        double cx = cubic[index].fX;
216
19.9M
        double cy = cubic[index].fY;
217
19.9M
        double denom = std::max(fabs(cx), std::max(fabs(cy),
218
19.9M
                std::max(fabs(cubic[minX].fX), fabs(cubic[minY].fY))));
219
19.9M
        if (denom == 0) {
220
39.7k
            minXSet |= 1 << index;
221
39.7k
            minYSet |= 1 << index;
222
39.7k
            continue;
223
39.7k
        }
224
19.9M
        double inv = 1 / denom;
225
19.9M
        if (approximately_equal_half(cx * inv, cubic[minX].fX * inv)) {
226
5.46M
            minXSet |= 1 << index;
227
5.46M
        }
228
19.9M
        if (approximately_equal_half(cy * inv, cubic[minY].fY * inv)) {
229
5.54M
            minYSet |= 1 << index;
230
5.54M
        }
231
19.9M
    }
232
4.99M
    if (minXSet == 0xF) {  // test for vertical line
233
31.2k
        if (minYSet == 0xF) {  // return 1 if all four are coincident
234
3.50k
            return coincident_line(cubic, fCubic);
235
3.50k
        }
236
27.7k
        return vertical_line(cubic, fCubic);
237
31.2k
    }
238
4.96M
    if (minYSet == 0xF) {  // test for horizontal line
239
57.7k
        return horizontal_line(cubic, fCubic);
240
57.7k
    }
241
4.90M
    int result = check_linear(cubic, minX, maxX, minY, maxY, fCubic);
242
4.90M
    if (result) {
243
381k
        return result;
244
381k
    }
245
4.52M
    if (allowQuadratics == SkReduceOrder::kAllow_Quadratics
246
4.52M
            && (result = check_quadratic(cubic, fCubic))) {
247
17.3k
        return result;
248
17.3k
    }
249
4.50M
    fCubic = cubic;
250
4.50M
    return 4;
251
4.52M
}
252
253
9.33M
SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkPoint* reducePts) {
254
9.33M
    SkDQuad quad;
255
9.33M
    quad.set(a);
256
9.33M
    SkReduceOrder reducer;
257
9.33M
    int order = reducer.reduce(quad);
258
9.33M
    if (order == 2) {  // quad became line
259
3.09M
        for (int index = 0; index < order; ++index) {
260
2.06M
            *reducePts++ = reducer.fLine[index].asSkPoint();
261
2.06M
        }
262
1.03M
    }
263
9.33M
    return SkPathOpsPointsToVerb(order - 1);
264
9.33M
}
265
266
168k
SkPath::Verb SkReduceOrder::Conic(const SkConic& c, SkPoint* reducePts) {
267
168k
    SkPath::Verb verb = SkReduceOrder::Quad(c.fPts, reducePts);
268
168k
    if (verb > SkPath::kLine_Verb && c.fW == 1) {
269
5.10k
        return SkPath::kQuad_Verb;
270
5.10k
    }
271
163k
    return verb == SkPath::kQuad_Verb ? SkPath::kConic_Verb : verb;
272
168k
}
273
274
5.05M
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
275
5.05M
    if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
276
5.05M
            && SkDPoint::ApproximatelyEqual(a[0], a[3])) {
277
55.6k
        reducePts[0] = a[0];
278
55.6k
        return SkPath::kMove_Verb;
279
55.6k
    }
280
4.99M
    SkDCubic cubic;
281
4.99M
    cubic.set(a);
282
4.99M
    SkReduceOrder reducer;
283
4.99M
    int order = reducer.reduce(cubic, kAllow_Quadratics);
284
4.99M
    if (order == 2 || order == 3) {  // cubic became line or quad
285
1.45M
        for (int index = 0; index < order; ++index) {
286
977k
            *reducePts++ = reducer.fQuad[index].asSkPoint();
287
977k
        }
288
480k
    }
289
4.99M
    return SkPathOpsPointsToVerb(order - 1);
290
5.05M
}