123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #ifndef GrGrCCStrokeGeometry_DEFINED
- #define GrGrCCStrokeGeometry_DEFINED
- #include "include/core/SkPaint.h"
- #include "include/core/SkPoint.h"
- #include "include/private/SkTArray.h"
- class SkStrokeRec;
- /**
- * This class converts device-space stroked paths into a set of independent strokes, joins, and caps
- * that map directly to coverage-counted GPU instances. Non-hairline strokes can only be drawn with
- * rigid body transforms; we don't yet support skewing the stroke lines themselves.
- */
- class GrCCStrokeGeometry {
- public:
- static constexpr int kMaxNumLinearSegmentsLog2 = 15;
- GrCCStrokeGeometry(int numSkPoints = 0, int numSkVerbs = 0)
- : fVerbs(numSkVerbs * 5/2) // Reserve for a 2.5x expansion in verbs. (Joins get their
- // own separate verb in our representation.)
- , fParams(numSkVerbs * 3) // Somewhere around 1-2 params per verb.
- , fPoints(numSkPoints * 5/4) // Reserve for a 1.25x expansion in points and normals.
- , fNormals(numSkPoints * 5/4) {}
- // A string of verbs and their corresponding, params, points, and normals are a compact
- // representation of what will eventually be independent instances in GPU buffers. When added
- // up, the combined coverage of all these instances will make complete stroked paths.
- enum class Verb : uint8_t {
- kBeginPath, // Instructs the iterator to advance its stroke width, atlas offset, etc.
- // Independent strokes of a single line or curve, with (antialiased) butt caps on the ends.
- kLinearStroke,
- kQuadraticStroke,
- kCubicStroke,
- // Joins are a triangles that connect the outer corners of two adjoining strokes. Miters
- // have an additional triangle cap on top of the bevel, and round joins have an arc on top.
- kBevelJoin,
- kMiterJoin,
- kRoundJoin,
- // We use internal joins when we have to internally break up a stroke because its curvature
- // is too strong for a triangle strip. They are coverage-counted, self-intersecting
- // quadrilaterals that tie the four corners of two adjoining strokes together a like a
- // shoelace. (Coverage is negative on the inside half.) We place an arc on both ends of an
- // internal round join.
- kInternalBevelJoin,
- kInternalRoundJoin,
- kSquareCap,
- kRoundCap,
- kEndContour // Instructs the iterator to advance its internal point and normal ptrs.
- };
- static bool IsInternalJoinVerb(Verb verb);
- // Some verbs require additional parameters(s).
- union Parameter {
- // For cubic and quadratic strokes: How many flat line segments to chop the curve into?
- int fNumLinearSegmentsLog2;
- // For miter and round joins: How tall should the triangle cap be on top of the join?
- // (This triangle is the conic control points for a round join.)
- float fMiterCapHeightOverWidth;
- float fConicWeight; // Round joins only.
- };
- const SkTArray<Verb, true>& verbs() const { SkASSERT(!fInsideContour); return fVerbs; }
- const SkTArray<Parameter, true>& params() const { SkASSERT(!fInsideContour); return fParams; }
- const SkTArray<SkPoint, true>& points() const { SkASSERT(!fInsideContour); return fPoints; }
- const SkTArray<SkVector, true>& normals() const { SkASSERT(!fInsideContour); return fNormals; }
- // These track the numbers of instances required to draw all the recorded strokes.
- struct InstanceTallies {
- int fStrokes[kMaxNumLinearSegmentsLog2 + 1];
- int fTriangles;
- int fConics;
- InstanceTallies operator+(const InstanceTallies&) const;
- };
- void beginPath(const SkStrokeRec&, float strokeDevWidth, InstanceTallies*);
- void moveTo(SkPoint);
- void lineTo(SkPoint);
- void quadraticTo(const SkPoint[3]);
- void cubicTo(const SkPoint[4]);
- void closeContour(); // Connect back to the first point in the contour and exit.
- void capContourAndExit(); // Add endcaps (if any) and exit the contour.
- private:
- void lineTo(Verb leftJoinVerb, SkPoint);
- void quadraticTo(Verb leftJoinVerb, const SkPoint[3], float maxCurvatureT);
- static constexpr float kLeftMaxCurvatureNone = 1;
- static constexpr float kRightMaxCurvatureNone = 0;
- void cubicTo(Verb leftJoinVerb, const SkPoint[4], float maxCurvatureT, float leftMaxCurvatureT,
- float rightMaxCurvatureT);
- // Pushes a new normal to fNormals and records a join, without changing the current position.
- void rotateTo(Verb leftJoinVerb, SkVector normal);
- // Records a stroke in fElememts.
- void recordStroke(Verb, int numSegmentsLog2);
- // Records a join in fElememts with the previous stroke, if the cuurent contour is not empty.
- void recordLeftJoinIfNotEmpty(Verb joinType, SkVector nextNormal);
- void recordBevelJoin(Verb originalJoinVerb);
- void recordMiterJoin(float miterCapHeightOverWidth);
- void recordRoundJoin(Verb roundJoinVerb, float miterCapHeightOverWidth, float conicWeight);
- void recordCapsIfAny();
- float fCurrStrokeRadius;
- Verb fCurrStrokeJoinVerb;
- SkPaint::Cap fCurrStrokeCapType;
- InstanceTallies* fCurrStrokeTallies = nullptr;
- // We implement miters by placing a triangle-shaped cap on top of a bevel join. This field tells
- // us what the miter limit is, restated in terms of how tall that triangle cap can be.
- float fMiterMaxCapHeightOverWidth;
- // Any curvature on the original curve gets magnified on the outer edge of the stroke,
- // proportional to how thick the stroke radius is. This field tells us the maximum curvature we
- // can tolerate using the current stroke radius, before linearization artifacts begin to appear
- // on the outer edge.
- //
- // (Curvature this strong is quite rare in practice, but when it does happen, we decompose the
- // section with strong curvature into lineTo's with round joins in between.)
- float fMaxCurvatureCosTheta;
- int fCurrContourFirstPtIdx;
- int fCurrContourFirstNormalIdx;
- SkDEBUGCODE(bool fInsideContour = false);
- SkSTArray<128, Verb, true> fVerbs;
- SkSTArray<128, Parameter, true> fParams;
- SkSTArray<128, SkPoint, true> fPoints;
- SkSTArray<128, SkVector, true> fNormals;
- };
- inline GrCCStrokeGeometry::InstanceTallies GrCCStrokeGeometry::InstanceTallies::operator+(
- const InstanceTallies& t) const {
- InstanceTallies ret;
- for (int i = 0; i <= kMaxNumLinearSegmentsLog2; ++i) {
- ret.fStrokes[i] = fStrokes[i] + t.fStrokes[i];
- }
- ret.fTriangles = fTriangles + t.fTriangles;
- ret.fConics = fConics + t.fConics;
- return ret;
- }
- inline bool GrCCStrokeGeometry::IsInternalJoinVerb(Verb verb) {
- switch (verb) {
- case Verb::kInternalBevelJoin:
- case Verb::kInternalRoundJoin:
- return true;
- case Verb::kBeginPath:
- case Verb::kLinearStroke:
- case Verb::kQuadraticStroke:
- case Verb::kCubicStroke:
- case Verb::kBevelJoin:
- case Verb::kMiterJoin:
- case Verb::kRoundJoin:
- case Verb::kSquareCap:
- case Verb::kRoundCap:
- case Verb::kEndContour:
- return false;
- }
- SK_ABORT("Invalid GrCCStrokeGeometry::Verb.");
- return false;
- }
- #endif
|