* 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)
, fParams(numSkVerbs * 3)
, fPoints(numSkPoints * 5/4)
, fNormals(numSkPoints * 5/4) {}
enum class Verb : uint8_t {
kBeginPath,
kLinearStroke,
kQuadraticStroke,
kCubicStroke,
kBevelJoin,
kMiterJoin,
kRoundJoin,
kInternalBevelJoin,
kInternalRoundJoin,
kSquareCap,
kRoundCap,
kEndContour
};
static bool IsInternalJoinVerb(Verb verb);
union Parameter {
int fNumLinearSegmentsLog2;
float fMiterCapHeightOverWidth;
float fConicWeight;
};
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; }
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();
void capContourAndExit();
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);
void rotateTo(Verb leftJoinVerb, SkVector normal);
void recordStroke(Verb, int numSegmentsLog2);
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;
float fMiterMaxCapHeightOverWidth;
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.");
}
#endif