1419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton/*
2419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton * Copyright 2017 Google Inc.
3419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton *
4419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton * Use of this source code is governed by a BSD-style license that can be
5419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton * found in the LICENSE file.
6419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton */
7419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton
8383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton#ifndef GrGrCCGeometry_DEFINED
9383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton#define GrGrCCGeometry_DEFINED
10419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton
117f578bf07b016778e3105b7655a895728b12d847Chris Dalton#include "SkGeometry.h"
12c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton#include "SkNx.h"
13c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton#include "SkPoint.h"
14c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton#include "SkTArray.h"
15419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton
16c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton/**
17c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton * This class chops device-space contours up into a series of segments that CCPR knows how to
18383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton * render. (See GrCCGeometry::Verb.)
19419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton *
20419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton * NOTE: This must be done in device space, since an affine transformation can change whether a
21419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton * curve is monotonic.
22419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton */
23383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Daltonclass GrCCGeometry {
24c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Daltonpublic:
25c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // These are the verbs that CCPR knows how to draw. If a path has any segments that don't map to
26c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // this list, then they are chopped into smaller ones that do. A list of these comprise a
27c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // compact representation of what can later be expanded into GPU instance data.
28c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    enum class Verb : uint8_t {
29c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        kBeginPath, // Included only for caller convenience.
30c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        kBeginContour,
31c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        kLineTo,
32c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        kMonotonicQuadraticTo, // Monotonic relative to the vector between its endpoints [P2 - P0].
33be4ffab4e208ec47b4298621b9c9e8456f31717eChris Dalton        kMonotonicCubicTo,
34c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        kEndClosedContour, // endPt == startPt.
35c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        kEndOpenContour // endPt != startPt.
36c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    };
37c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
3884403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    // These tallies track numbers of CCPR primitives that are required to draw a contour.
39c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    struct PrimitiveTallies {
40c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        int fTriangles; // Number of triangles in the contour's fan.
4184403d7f53d88b2449fd19415538ba1479fe300bChris Dalton        int fWoundTriangles; // Triangles (from the tessellator) whose winding magnitude > 1.
42c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        int fQuadratics;
43be4ffab4e208ec47b4298621b9c9e8456f31717eChris Dalton        int fCubics;
44c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
45c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        void operator+=(const PrimitiveTallies&);
46c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        PrimitiveTallies operator-(const PrimitiveTallies&) const;
479ca27849d8259e4b35243094bdca969612efba2fChris Dalton        bool operator==(const PrimitiveTallies&);
48c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    };
49c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
50383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton    GrCCGeometry(int numSkPoints = 0, int numSkVerbs = 0)
51c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton            : fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
52c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton            , fVerbs(numSkVerbs * 3) {}
53c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
54c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    const SkTArray<SkPoint, true>& points() const { SkASSERT(!fBuildingContour); return fPoints; }
55c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    const SkTArray<Verb, true>& verbs() const { SkASSERT(!fBuildingContour); return fVerbs; }
56c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
57c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    void reset() {
58c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        SkASSERT(!fBuildingContour);
59c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        fPoints.reset();
60c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        fVerbs.reset();
61c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    }
62c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
63c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // This is included in case the caller needs to discard previously added contours. It is up to
64c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // the caller to track counts and ensure we don't pop back into the middle of a different
65c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // contour.
66c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    void resize_back(int numPoints, int numVerbs) {
67c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        SkASSERT(!fBuildingContour);
68c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        fPoints.resize_back(numPoints);
69c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        fVerbs.resize_back(numVerbs);
70c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton        SkASSERT(fVerbs.empty() || fVerbs.back() == Verb::kEndOpenContour ||
71c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton                 fVerbs.back() == Verb::kEndClosedContour);
72c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    }
73c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
74c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    void beginPath();
75c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    void beginContour(const SkPoint& devPt);
76c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    void lineTo(const SkPoint& devPt);
77c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    void quadraticTo(const SkPoint& devP1, const SkPoint& devP2);
787f578bf07b016778e3105b7655a895728b12d847Chris Dalton
797f578bf07b016778e3105b7655a895728b12d847Chris Dalton    // We pass through inflection points and loop intersections using a line and quadratic(s)
807f578bf07b016778e3105b7655a895728b12d847Chris Dalton    // respectively. 'inflectPad' and 'loopIntersectPad' specify how close (in pixels) cubic
817f578bf07b016778e3105b7655a895728b12d847Chris Dalton    // segments are allowed to get to these points. For normal rendering you will want to use the
827f578bf07b016778e3105b7655a895728b12d847Chris Dalton    // default values, but these can be overridden for testing purposes.
837f578bf07b016778e3105b7655a895728b12d847Chris Dalton    //
847f578bf07b016778e3105b7655a895728b12d847Chris Dalton    // NOTE: loops do appear to require two full pixels of padding around the intersection point.
857f578bf07b016778e3105b7655a895728b12d847Chris Dalton    //       With just one pixel-width of pad, we start to see bad pixels. Ultimately this has a
867f578bf07b016778e3105b7655a895728b12d847Chris Dalton    //       minimal effect on the total amount of segments produced. Most sections that pass
877f578bf07b016778e3105b7655a895728b12d847Chris Dalton    //       through the loop intersection can be approximated with a single quadratic anyway,
887f578bf07b016778e3105b7655a895728b12d847Chris Dalton    //       regardless of whether we are use one pixel of pad or two (1.622 avg. quads per loop
897f578bf07b016778e3105b7655a895728b12d847Chris Dalton    //       intersection vs. 1.489 on the tiger).
907f578bf07b016778e3105b7655a895728b12d847Chris Dalton    void cubicTo(const SkPoint& devP1, const SkPoint& devP2, const SkPoint& devP3,
917f578bf07b016778e3105b7655a895728b12d847Chris Dalton                 float inflectPad = 0.55f, float loopIntersectPad = 2);
927f578bf07b016778e3105b7655a895728b12d847Chris Dalton
93c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    PrimitiveTallies endContour(); // Returns the numbers of primitives needed to draw the contour.
94c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
95c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Daltonprivate:
9643646533fa6fb7cd6724cf00f6b8af15ac1649eaChris Dalton    inline void appendMonotonicQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
9743646533fa6fb7cd6724cf00f6b8af15ac1649eaChris Dalton    inline void appendSingleMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2);
987f578bf07b016778e3105b7655a895728b12d847Chris Dalton
99383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton    using AppendCubicFn = void(GrCCGeometry::*)(const Sk2f& p0, const Sk2f& p1,
100383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton                                                const Sk2f& p2, const Sk2f& p3,
101383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Dalton                                                int maxSubdivisions);
1027f578bf07b016778e3105b7655a895728b12d847Chris Dalton    static constexpr int kMaxSubdivionsPerCubicSection = 2;
1037f578bf07b016778e3105b7655a895728b12d847Chris Dalton
1047f578bf07b016778e3105b7655a895728b12d847Chris Dalton    template<AppendCubicFn AppendLeftRight>
1057f578bf07b016778e3105b7655a895728b12d847Chris Dalton    inline void chopCubicAtMidTangent(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
1067f578bf07b016778e3105b7655a895728b12d847Chris Dalton                                      const Sk2f& p3, const Sk2f& tan0, const Sk2f& tan3,
1077f578bf07b016778e3105b7655a895728b12d847Chris Dalton                                      int maxFutureSubdivisions = kMaxSubdivionsPerCubicSection);
1087f578bf07b016778e3105b7655a895728b12d847Chris Dalton
1097f578bf07b016778e3105b7655a895728b12d847Chris Dalton    template<AppendCubicFn AppendLeft, AppendCubicFn AppendRight>
1107f578bf07b016778e3105b7655a895728b12d847Chris Dalton    inline void chopCubic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3,
1117f578bf07b016778e3105b7655a895728b12d847Chris Dalton                          float T, int maxFutureSubdivisions = kMaxSubdivionsPerCubicSection);
1127f578bf07b016778e3105b7655a895728b12d847Chris Dalton
1137f578bf07b016778e3105b7655a895728b12d847Chris Dalton    void appendMonotonicCubics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3,
1147f578bf07b016778e3105b7655a895728b12d847Chris Dalton                               int maxSubdivisions = kMaxSubdivionsPerCubicSection);
1157f578bf07b016778e3105b7655a895728b12d847Chris Dalton    void appendCubicApproximation(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, const Sk2f& p3,
1167f578bf07b016778e3105b7655a895728b12d847Chris Dalton                                  int maxSubdivisions = kMaxSubdivionsPerCubicSection);
117c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
118c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // Transient state used while building a contour.
11984403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    SkPoint fCurrAnchorPoint;
12084403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    SkPoint fCurrFanPoint;
12184403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    PrimitiveTallies fCurrContourTallies;
12284403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    SkCubicType fCurrCubicType;
12384403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    SkDEBUGCODE(bool fBuildingContour = false);
124c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
125c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    // TODO: These points could eventually be written directly to block-allocated GPU buffers.
126c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    SkSTArray<128, SkPoint, true>   fPoints;
127c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    SkSTArray<128, Verb, true>      fVerbs;
128c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton};
129c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
130383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Daltoninline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
131c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    fTriangles += b.fTriangles;
13284403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    fWoundTriangles += b.fWoundTriangles;
133c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    fQuadratics += b.fQuadratics;
134be4ffab4e208ec47b4298621b9c9e8456f31717eChris Dalton    fCubics += b.fCubics;
135c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton}
136c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton
137383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris DaltonGrCCGeometry::PrimitiveTallies
138383a2ef6edb84dbebc7a9c22ea7423037bbf6a2fChris Daltoninline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
139c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton    return {fTriangles - b.fTriangles,
14084403d7f53d88b2449fd19415538ba1479fe300bChris Dalton            fWoundTriangles - b.fWoundTriangles,
141c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton            fQuadratics - b.fQuadratics,
142be4ffab4e208ec47b4298621b9c9e8456f31717eChris Dalton            fCubics - b.fCubics};
143c1e59638b4a08f5210f72f671292b1b3759f54c6Chris Dalton}
144419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton
1459ca27849d8259e4b35243094bdca969612efba2fChris Daltoninline bool GrCCGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
14684403d7f53d88b2449fd19415538ba1479fe300bChris Dalton    return fTriangles == b.fTriangles && fWoundTriangles == b.fWoundTriangles &&
14784403d7f53d88b2449fd19415538ba1479fe300bChris Dalton           fQuadratics == b.fQuadratics && fCubics == b.fCubics;
1489ca27849d8259e4b35243094bdca969612efba2fChris Dalton}
1499ca27849d8259e4b35243094bdca969612efba2fChris Dalton
150419a94da028b33425a0feeb44d0d022a5d3d3704Chris Dalton#endif
151