Coverage Report

Created: 2024-05-20 07:14

/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
173k
static int coincident_line(const SkDQuad& quad, SkDQuad& reduction) {
25
173k
    reduction[0] = reduction[1] = quad[0];
26
173k
    return 1;
27
173k
}
28
29
719k
static int reductionLineCount(const SkDQuad& reduction) {
30
719k
    return 1 + !reduction[0].approximatelyEqual(reduction[1]);
31
719k
}
32
33
77.2k
static int vertical_line(const SkDQuad& quad, SkDQuad& reduction) {
34
77.2k
    reduction[0] = quad[0];
35
77.2k
    reduction[1] = quad[2];
36
77.2k
    return reductionLineCount(reduction);
37
77.2k
}
38
39
92.9k
static int horizontal_line(const SkDQuad& quad, SkDQuad& reduction) {
40
92.9k
    reduction[0] = quad[0];
41
92.9k
    reduction[1] = quad[2];
42
92.9k
    return reductionLineCount(reduction);
43
92.9k
}
44
45
static int check_linear(const SkDQuad& quad,
46
5.80M
        int minX, int maxX, int minY, int maxY, SkDQuad& reduction) {
47
5.80M
    if (!quad.isLinear(0, 2)) {
48
5.25M
        return 0;
49
5.25M
    }
50
    // four are colinear: return line formed by outside
51
549k
    reduction[0] = quad[0];
52
549k
    reduction[1] = quad[2];
53
549k
    return reductionLineCount(reduction);
54
5.80M
}
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
6.15M
int SkReduceOrder::reduce(const SkDQuad& quad) {
63
6.15M
    int index, minX, maxX, minY, maxY;
64
6.15M
    int minXSet, minYSet;
65
6.15M
    minX = maxX = minY = maxY = 0;
66
6.15M
    minXSet = minYSet = 0;
67
18.4M
    for (index = 1; index < 3; ++index) {
68
12.3M
        if (quad[minX].fX > quad[index].fX) {
69
5.26M
            minX = index;
70
5.26M
        }
71
12.3M
        if (quad[minY].fY > quad[index].fY) {
72
5.24M
            minY = index;
73
5.24M
        }
74
12.3M
        if (quad[maxX].fX < quad[index].fX) {
75
5.15M
            maxX = index;
76
5.15M
        }
77
12.3M
        if (quad[maxY].fY < quad[index].fY) {
78
5.28M
            maxY = index;
79
5.28M
        }
80
12.3M
    }
81
24.6M
    for (index = 0; index < 3; ++index) {
82
18.4M
        if (AlmostEqualUlps(quad[index].fX, quad[minX].fX)) {
83
7.28M
            minXSet |= 1 << index;
84
7.28M
        }
85
18.4M
        if (AlmostEqualUlps(quad[index].fY, quad[minY].fY)) {
86
7.25M
            minYSet |= 1 << index;
87
7.25M
        }
88
18.4M
    }
89
6.15M
    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
173k
        return coincident_line(quad, fQuad);
93
173k
    }
94
5.97M
    if (minXSet == 0x7) {  // test for vertical line
95
77.2k
        return vertical_line(quad, fQuad);
96
77.2k
    }
97
5.89M
    if (minYSet == 0x7) {  // test for horizontal line
98
92.9k
        return horizontal_line(quad, fQuad);
99
92.9k
    }
100
5.80M
    int result = check_linear(quad, minX, maxX, minY, maxY, fQuad);
101
5.80M
    if (result) {
102
549k
        return result;
103
549k
    }
104
5.25M
    fQuad = quad;
105
5.25M
    return 3;
106
5.80M
}
107
108
////////////////////////////////////////////////////////////////////////////////////
109
110
3.24k
static int coincident_line(const SkDCubic& cubic, SkDCubic& reduction) {
111
3.24k
    reduction[0] = reduction[1] = cubic[0];
112
3.24k
    return 1;
113
3.24k
}
114
115
329k
static int reductionLineCount(const SkDCubic& reduction) {
116
329k
    return 1 + !reduction[0].approximatelyEqual(reduction[1]);
117
329k
}
118
119
18.3k
static int vertical_line(const SkDCubic& cubic, SkDCubic& reduction) {
120
18.3k
    reduction[0] = cubic[0];
121
18.3k
    reduction[1] = cubic[3];
122
18.3k
    return reductionLineCount(reduction);
123
18.3k
}
124
125
34.6k
static int horizontal_line(const SkDCubic& cubic, SkDCubic& reduction) {
126
34.6k
    reduction[0] = cubic[0];
127
34.6k
    reduction[1] = cubic[3];
128
34.6k
    return reductionLineCount(reduction);
129
34.6k
}
130
131
// check to see if it is a quadratic or a line
132
3.02M
static int check_quadratic(const SkDCubic& cubic, SkDCubic& reduction) {
133
3.02M
    double dx10 = cubic[1].fX - cubic[0].fX;
134
3.02M
    double dx23 = cubic[2].fX - cubic[3].fX;
135
3.02M
    double midX = cubic[0].fX + dx10 * 3 / 2;
136
3.02M
    double sideAx = midX - cubic[3].fX;
137
3.02M
    double sideBx = dx23 * 3 / 2;
138
3.02M
    if (approximately_zero(sideAx) ? !approximately_equal(sideAx, sideBx)
139
3.02M
            : !AlmostEqualUlps_Pin(sideAx, sideBx)) {
140
2.89M
        return 0;
141
2.89M
    }
142
134k
    double dy10 = cubic[1].fY - cubic[0].fY;
143
134k
    double dy23 = cubic[2].fY - cubic[3].fY;
144
134k
    double midY = cubic[0].fY + dy10 * 3 / 2;
145
134k
    double sideAy = midY - cubic[3].fY;
146
134k
    double sideBy = dy23 * 3 / 2;
147
134k
    if (approximately_zero(sideAy) ? !approximately_equal(sideAy, sideBy)
148
134k
            : !AlmostEqualUlps_Pin(sideAy, sideBy)) {
149
118k
        return 0;
150
118k
    }
151
15.2k
    reduction[0] = cubic[0];
152
15.2k
    reduction[1].fX = midX;
153
15.2k
    reduction[1].fY = midY;
154
15.2k
    reduction[2] = cubic[3];
155
15.2k
    return 3;
156
134k
}
157
158
static int check_linear(const SkDCubic& cubic,
159
3.30M
        int minX, int maxX, int minY, int maxY, SkDCubic& reduction) {
160
3.30M
    if (!cubic.isLinear(0, 3)) {
161
3.02M
        return 0;
162
3.02M
    }
163
    // four are colinear: return line formed by outside
164
276k
    reduction[0] = cubic[0];
165
276k
    reduction[1] = cubic[3];
166
276k
    return reductionLineCount(reduction);
167
3.30M
}
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
3.35M
int SkReduceOrder::reduce(const SkDCubic& cubic, Quadratics allowQuadratics) {
196
3.35M
    int index, minX, maxX, minY, maxY;
197
3.35M
    int minXSet, minYSet;
198
3.35M
    minX = maxX = minY = maxY = 0;
199
3.35M
    minXSet = minYSet = 0;
200
13.4M
    for (index = 1; index < 4; ++index) {
201
10.0M
        if (cubic[minX].fX > cubic[index].fX) {
202
4.61M
            minX = index;
203
4.61M
        }
204
10.0M
        if (cubic[minY].fY > cubic[index].fY) {
205
4.58M
            minY = index;
206
4.58M
        }
207
10.0M
        if (cubic[maxX].fX < cubic[index].fX) {
208
4.60M
            maxX = index;
209
4.60M
        }
210
10.0M
        if (cubic[maxY].fY < cubic[index].fY) {
211
4.49M
            maxY = index;
212
4.49M
        }
213
10.0M
    }
214
16.7M
    for (index = 0; index < 4; ++index) {
215
13.4M
        double cx = cubic[index].fX;
216
13.4M
        double cy = cubic[index].fY;
217
13.4M
        double denom = std::max(fabs(cx), std::max(fabs(cy),
218
13.4M
                std::max(fabs(cubic[minX].fX), fabs(cubic[minY].fY))));
219
13.4M
        if (denom == 0) {
220
17.5k
            minXSet |= 1 << index;
221
17.5k
            minYSet |= 1 << index;
222
17.5k
            continue;
223
17.5k
        }
224
13.4M
        double inv = 1 / denom;
225
13.4M
        if (approximately_equal_half(cx * inv, cubic[minX].fX * inv)) {
226
3.68M
            minXSet |= 1 << index;
227
3.68M
        }
228
13.4M
        if (approximately_equal_half(cy * inv, cubic[minY].fY * inv)) {
229
3.72M
            minYSet |= 1 << index;
230
3.72M
        }
231
13.4M
    }
232
3.35M
    if (minXSet == 0xF) {  // test for vertical line
233
21.6k
        if (minYSet == 0xF) {  // return 1 if all four are coincident
234
3.24k
            return coincident_line(cubic, fCubic);
235
3.24k
        }
236
18.3k
        return vertical_line(cubic, fCubic);
237
21.6k
    }
238
3.33M
    if (minYSet == 0xF) {  // test for horizontal line
239
34.6k
        return horizontal_line(cubic, fCubic);
240
34.6k
    }
241
3.30M
    int result = check_linear(cubic, minX, maxX, minY, maxY, fCubic);
242
3.30M
    if (result) {
243
276k
        return result;
244
276k
    }
245
3.02M
    if (allowQuadratics == SkReduceOrder::kAllow_Quadratics
246
3.02M
            && (result = check_quadratic(cubic, fCubic))) {
247
15.2k
        return result;
248
15.2k
    }
249
3.01M
    fCubic = cubic;
250
3.01M
    return 4;
251
3.02M
}
252
253
6.15M
SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkPoint* reducePts) {
254
6.15M
    SkDQuad quad;
255
6.15M
    quad.set(a);
256
6.15M
    SkReduceOrder reducer;
257
6.15M
    int order = reducer.reduce(quad);
258
6.15M
    if (order == 2) {  // quad became line
259
2.07M
        for (int index = 0; index < order; ++index) {
260
1.38M
            *reducePts++ = reducer.fLine[index].asSkPoint();
261
1.38M
        }
262
693k
    }
263
6.15M
    return SkPathOpsPointsToVerb(order - 1);
264
6.15M
}
265
266
159k
SkPath::Verb SkReduceOrder::Conic(const SkConic& c, SkPoint* reducePts) {
267
159k
    SkPath::Verb verb = SkReduceOrder::Quad(c.fPts, reducePts);
268
159k
    if (verb > SkPath::kLine_Verb && c.fW == 1) {
269
4.60k
        return SkPath::kQuad_Verb;
270
4.60k
    }
271
154k
    return verb == SkPath::kQuad_Verb ? SkPath::kConic_Verb : verb;
272
159k
}
273
274
3.40M
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
275
3.40M
    if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
276
3.40M
            && SkDPoint::ApproximatelyEqual(a[0], a[3])) {
277
43.3k
        reducePts[0] = a[0];
278
43.3k
        return SkPath::kMove_Verb;
279
43.3k
    }
280
3.35M
    SkDCubic cubic;
281
3.35M
    cubic.set(a);
282
3.35M
    SkReduceOrder reducer;
283
3.35M
    int order = reducer.reduce(cubic, kAllow_Quadratics);
284
3.35M
    if (order == 2 || order == 3) {  // cubic became line or quad
285
1.04M
        for (int index = 0; index < order; ++index) {
286
699k
            *reducePts++ = reducer.fQuad[index].asSkPoint();
287
699k
        }
288
342k
    }
289
3.35M
    return SkPathOpsPointsToVerb(order - 1);
290
3.40M
}