Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/pathops/SkOpCubicHull.cpp
Line
Count
Source
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/SkPathOpsCubic.h"
8
#include "src/pathops/SkPathOpsPoint.h"
9
#include "src/pathops/SkPathOpsTypes.h"
10
11
#include <algorithm>
12
#include <cstddef>
13
14
681M
static bool rotate(const SkDCubic& cubic, int zero, int index, SkDCubic& rotPath) {
15
681M
    double dy = cubic[index].fY - cubic[zero].fY;
16
681M
    double dx = cubic[index].fX - cubic[zero].fX;
17
681M
    if (approximately_zero(dy)) {
18
6.59M
        if (approximately_zero(dx)) {
19
3.45M
            return false;
20
3.45M
        }
21
3.14M
        rotPath = cubic;
22
3.14M
        if (dy) {
23
580k
            rotPath[index].fY = cubic[zero].fY;
24
580k
            int mask = other_two(index, zero);
25
580k
            int side1 = index ^ mask;
26
580k
            int side2 = zero ^ mask;
27
580k
            if (approximately_equal(cubic[side1].fY, cubic[zero].fY)) {
28
287k
                rotPath[side1].fY = cubic[zero].fY;
29
287k
            }
30
580k
            if (approximately_equal(cubic[side2].fY, cubic[zero].fY)) {
31
267k
                rotPath[side2].fY = cubic[zero].fY;
32
267k
            }
33
580k
        }
34
3.14M
        return true;
35
6.59M
    }
36
3.37G
    for (int i = 0; i < 4; ++i) {
37
2.70G
        rotPath[i].fX = cubic[i].fX * dx + cubic[i].fY * dy;
38
2.70G
        rotPath[i].fY = cubic[i].fY * dx - cubic[i].fX * dy;
39
2.70G
    }
40
675M
    return true;
41
681M
}
42
43
44
// Returns 0 if negative, 1 if zero, 2 if positive
45
1.35G
static int side(double x) {
46
1.35G
    return (x > 0) + (x >= 0);
47
1.35G
}
48
49
/* Given a cubic, find the convex hull described by the end and control points.
50
   The hull may have 3 or 4 points. Cubics that degenerate into a point or line
51
   are not considered.
52
53
   The hull is computed by assuming that three points, if unique and non-linear,
54
   form a triangle. The fourth point may replace one of the first three, may be
55
   discarded if in the triangle or on an edge, or may be inserted between any of
56
   the three to form a convex quadralateral.
57
58
   The indices returned in order describe the convex hull.
59
*/
60
167M
int SkDCubic::convexHull(char order[4]) const {
61
167M
    size_t index;
62
    // find top point
63
167M
    size_t yMin = 0;
64
671M
    for (index = 1; index < 4; ++index) {
65
503M
        if (fPts[yMin].fY > fPts[index].fY || (fPts[yMin].fY == fPts[index].fY
66
265M
                && fPts[yMin].fX > fPts[index].fX)) {
67
239M
            yMin = index;
68
239M
        }
69
503M
    }
70
167M
    order[0] = yMin;
71
167M
    int midX = -1;
72
167M
    int backupYMin = -1;
73
177M
    for (int pass = 0; pass < 2; ++pass) {
74
859M
        for (index = 0; index < 4; ++index) {
75
690M
            if (index == yMin) {
76
172M
                continue;
77
172M
            }
78
            // rotate line from (yMin, index) to axis
79
            // see if remaining two points are both above or below
80
            // use this to find mid
81
517M
            int mask = other_two(yMin, index);
82
517M
            int side1 = yMin ^ mask;
83
517M
            int side2 = index ^ mask;
84
517M
            SkDCubic rotPath;
85
517M
            if (!rotate(*this, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx]
86
3.45M
                order[1] = side1;
87
3.45M
                order[2] = side2;
88
3.45M
                return 3;
89
3.45M
            }
90
514M
            int sides = side(rotPath[side1].fY - rotPath[yMin].fY);
91
514M
            sides ^= side(rotPath[side2].fY - rotPath[yMin].fY);
92
514M
            if (sides == 2) { // '2' means one remaining point <0, one >0
93
160M
                if (midX >= 0) {
94
                    // one of the control points is equal to an end point
95
229k
                    order[0] = 0;
96
229k
                    order[1] = 3;
97
229k
                    if (fPts[1] == fPts[0] || fPts[1] == fPts[3]) {
98
128k
                        order[2] = 2;
99
128k
                        return 3;
100
128k
                    }
101
100k
                    if (fPts[2] == fPts[0] || fPts[2] == fPts[3]) {
102
71.5k
                        order[2] = 1;
103
71.5k
                        return 3;
104
71.5k
                    }
105
                    // one of the control points may be very nearly but not exactly equal --
106
29.0k
                    double dist1_0 = fPts[1].distanceSquared(fPts[0]);
107
29.0k
                    double dist1_3 = fPts[1].distanceSquared(fPts[3]);
108
29.0k
                    double dist2_0 = fPts[2].distanceSquared(fPts[0]);
109
29.0k
                    double dist2_3 = fPts[2].distanceSquared(fPts[3]);
110
29.0k
                    double smallest1distSq = std::min(dist1_0, dist1_3);
111
29.0k
                    double smallest2distSq = std::min(dist2_0, dist2_3);
112
29.0k
                    if (approximately_zero(std::min(smallest1distSq, smallest2distSq))) {
113
1.34k
                        order[2] = smallest1distSq < smallest2distSq ? 2 : 1;
114
1.34k
                        return 3;
115
1.34k
                    }
116
29.0k
                }
117
159M
                midX = index;
118
354M
            } else if (sides == 0) { // '0' means both to one side or the other
119
330M
                backupYMin = index;
120
330M
            }
121
514M
        }
122
169M
        if (midX >= 0) {
123
159M
            break;
124
159M
        }
125
9.82M
        if (backupYMin < 0) {
126
21.1k
            break;
127
21.1k
        }
128
9.80M
        yMin = backupYMin;
129
9.80M
        backupYMin = -1;
130
9.80M
    }
131
164M
    if (midX < 0) {
132
4.54M
        midX = yMin ^ 3; // choose any other point
133
4.54M
    }
134
164M
    int mask = other_two(yMin, midX);
135
164M
    int least = yMin ^ mask;
136
164M
    int most = midX ^ mask;
137
164M
    order[0] = yMin;
138
164M
    order[1] = least;
139
140
    // see if mid value is on same side of line (least, most) as yMin
141
164M
    SkDCubic midPath;
142
164M
    if (!rotate(*this, least, most, midPath)) { // ! if cbc[least]==cbc[most]
143
2.90k
        order[2] = midX;
144
2.90k
        return 3;
145
2.90k
    }
146
164M
    int midSides = side(midPath[yMin].fY - midPath[least].fY);
147
164M
    midSides ^= side(midPath[midX].fY - midPath[least].fY);
148
164M
    if (midSides != 2) {  // if mid point is not between
149
8.45M
        order[2] = most;
150
8.45M
        return 3; // result is a triangle
151
8.45M
    }
152
155M
    order[2] = midX;
153
155M
    order[3] = most;
154
155M
    return 4; // result is a quadralateral
155
164M
}