1/*
2 * Copyright 2021 Google LLC.
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#include "src/gpu/tessellate/Tessellation.h"
9
10#include "include/core/SkPath.h"
11#include "src/core/SkGeometry.h"
12#include "src/core/SkPathPriv.h"
13#include "src/gpu/BufferWriter.h"
14#include "src/gpu/tessellate/CullTest.h"
15#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
16#include "src/gpu/tessellate/WangsFormula.h"
17
18namespace skgpu {
19
20namespace {
21
22// Writes a new path, chopping as necessary so no verbs require more segments than
23// kMaxTessellationSegmentsPerCurve. Curves completely outside the viewport are flattened into
24// lines.
25class PathChopper {
26public:
27    PathChopper(const SkMatrix& matrix, const SkRect& viewport)
28            : fCullTest(viewport, matrix)
29            , fVectorXform(matrix) {
30        fPath.setIsVolatile(true);
31    }
32
33    SkPath path() const { return fPath; }
34
35    void moveTo(SkPoint p) { fPath.moveTo(p); }
36    void lineTo(SkPoint p1) { fPath.lineTo(p1); }
37    void close() { fPath.close(); }
38
39    void quadTo(const SkPoint p[3]) {
40        if (!fCullTest.areVisible3(p)) {
41            this->lineTo(p[2]);
42            return;
43        }
44        float n = wangs_formula::quadratic_pow4(kTessellationPrecision, p, fVectorXform);
45        if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
46            SkPoint chops[5];
47            SkChopQuadAtHalf(p, chops);
48            this->quadTo(chops);
49            this->quadTo(chops + 2);
50            return;
51        }
52        fPath.quadTo(p[1], p[2]);
53    }
54
55    void conicTo(const SkPoint p[3], float w) {
56        if (!fCullTest.areVisible3(p)) {
57            this->lineTo(p[2]);
58            return;
59        }
60        float n = wangs_formula::conic_pow2(kTessellationPrecision, p, w, fVectorXform);
61        if (n > pow2(kMaxTessellationSegmentsPerCurve)) {
62            SkConic chops[2];
63            if (!SkConic(p,w).chopAt(.5, chops)) {
64                this->lineTo(p[2]);
65                return;
66            }
67            this->conicTo(chops[0].fPts, chops[0].fW);
68            this->conicTo(chops[1].fPts, chops[1].fW);
69            return;
70        }
71        fPath.conicTo(p[1], p[2], w);
72    }
73
74    void cubicTo(const SkPoint p[4]) {
75        if (!fCullTest.areVisible4(p)) {
76            this->lineTo(p[3]);
77            return;
78        }
79        float n = wangs_formula::cubic_pow4(kTessellationPrecision, p, fVectorXform);
80        if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
81            SkPoint chops[7];
82            SkChopCubicAtHalf(p, chops);
83            this->cubicTo(chops);
84            this->cubicTo(chops + 3);
85            return;
86        }
87        fPath.cubicTo(p[1], p[2], p[3]);
88    }
89
90private:
91    const CullTest fCullTest;
92    const wangs_formula::VectorXform fVectorXform;
93    SkPath fPath;
94};
95
96}  // namespace
97
98SkPath PreChopPathCurves(const SkPath& path, const SkMatrix& matrix, const SkRect& viewport) {
99    PathChopper chopper(matrix, viewport);
100    for (auto [verb, p, w] : SkPathPriv::Iterate(path)) {
101        switch (verb) {
102            case SkPathVerb::kMove:
103                chopper.moveTo(p[0]);
104                break;
105            case SkPathVerb::kLine:
106                chopper.lineTo(p[1]);
107                break;
108            case SkPathVerb::kQuad:
109                chopper.quadTo(p);
110                break;
111            case SkPathVerb::kConic:
112                chopper.conicTo(p, *w);
113                break;
114            case SkPathVerb::kCubic:
115                chopper.cubicTo(p);
116                break;
117            case SkPathVerb::kClose:
118                chopper.close();
119                break;
120        }
121    }
122    return chopper.path();
123}
124
125}  // namespace skgpu
126