Revert "Handle too many (or too large) paths in GrDefaultPathRenderer"
This reverts commit 6383b29848
.
Reason for revert: Test failures
Original change's description:
> Handle too many (or too large) paths in GrDefaultPathRenderer
>
> PathGeoBuilder constructs the geometry with the same basic
> technique as before, but allows interrupting the process
> to emit multiple draws.
>
> Original test case was 2000 non-AA stroked circles, which
> created ~66000 vertices. That now renders, as do various
> tests with a single large path (as well as filled paths).
>
> TODO: I think that this could be extracted and re-used for
> MSAA path renderer without too much work? I need to read
> that code more carefully to make sure it lines up.
>
> Bug: skia:6695
> Change-Id: I18983ba3d4f475ae0651946958b4911008aa623f
> Reviewed-on: https://skia-review.googlesource.com/18360
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Osman <brianosman@google.com>
TBR=bsalomon@google.com,robertphillips@google.com,brianosman@google.com
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:6695
Change-Id: I78ce9879a2e45e19f53027ca506cc2e8fae664b3
Reviewed-on: https://skia-review.googlesource.com/18981
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
7785dd235d
commit
fd7819c5d8
@ -11,6 +11,7 @@
|
||||
#include "SkGeometry.h"
|
||||
#include "SkMathPriv.h"
|
||||
|
||||
static const int MAX_POINTS_PER_CURVE = 1 << 10;
|
||||
static const SkScalar gMinCurveTol = 0.0001f;
|
||||
|
||||
SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
|
||||
@ -44,7 +45,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol)
|
||||
|
||||
SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
|
||||
if (!SkScalarIsFinite(d)) {
|
||||
return kMaxPointsPerCurve;
|
||||
return MAX_POINTS_PER_CURVE;
|
||||
} else if (d <= tol) {
|
||||
return 1;
|
||||
} else {
|
||||
@ -54,7 +55,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol)
|
||||
// 2^(log4(x)) = sqrt(x);
|
||||
SkScalar divSqrt = SkScalarSqrt(d / tol);
|
||||
if (((SkScalar)SK_MaxS32) <= divSqrt) {
|
||||
return kMaxPointsPerCurve;
|
||||
return MAX_POINTS_PER_CURVE;
|
||||
} else {
|
||||
int temp = SkScalarCeilToInt(divSqrt);
|
||||
int pow2 = GrNextPow2(temp);
|
||||
@ -64,7 +65,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol)
|
||||
if (pow2 < 1) {
|
||||
pow2 = 1;
|
||||
}
|
||||
return SkTMin(pow2, kMaxPointsPerCurve);
|
||||
return SkTMin(pow2, MAX_POINTS_PER_CURVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,13 +105,13 @@ uint32_t GrPathUtils::cubicPointCount(const SkPoint points[],
|
||||
points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
|
||||
d = SkScalarSqrt(d);
|
||||
if (!SkScalarIsFinite(d)) {
|
||||
return kMaxPointsPerCurve;
|
||||
return MAX_POINTS_PER_CURVE;
|
||||
} else if (d <= tol) {
|
||||
return 1;
|
||||
} else {
|
||||
SkScalar divSqrt = SkScalarSqrt(d / tol);
|
||||
if (((SkScalar)SK_MaxS32) <= divSqrt) {
|
||||
return kMaxPointsPerCurve;
|
||||
return MAX_POINTS_PER_CURVE;
|
||||
} else {
|
||||
int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
|
||||
int pow2 = GrNextPow2(temp);
|
||||
@ -120,7 +121,7 @@ uint32_t GrPathUtils::cubicPointCount(const SkPoint points[],
|
||||
if (pow2 < 1) {
|
||||
pow2 = 1;
|
||||
}
|
||||
return SkTMin(pow2, kMaxPointsPerCurve);
|
||||
return SkTMin(pow2, MAX_POINTS_PER_CURVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,8 +165,5 @@ namespace GrPathUtils {
|
||||
// This value was chosen to approximate the supersampling accuracy of the raster path (16
|
||||
// samples, or one quarter pixel).
|
||||
static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
|
||||
|
||||
// We guarantee that no quad or cubic will ever produce more than this many points
|
||||
static const int kMaxPointsPerCurve = 1 << 10;
|
||||
};
|
||||
#endif
|
||||
|
@ -59,269 +59,37 @@ GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
|
||||
}
|
||||
}
|
||||
|
||||
// Needs to be large enough to handle quads/cubics, which have a worst-case of 1k points
|
||||
static const int kVerticesPerChunk = 16384;
|
||||
|
||||
class PathGeoBuilder {
|
||||
public:
|
||||
PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
|
||||
GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline)
|
||||
: fMesh(primitiveType)
|
||||
, fTarget(target)
|
||||
, fVertexStride(sizeof(SkPoint))
|
||||
, fGeometryProcessor(geometryProcessor)
|
||||
, fPipeline(pipeline)
|
||||
, fIndexBuffer(nullptr)
|
||||
, fFirstIndex(0)
|
||||
, fIndices(nullptr) {
|
||||
this->allocNewBuffers();
|
||||
static inline void append_countour_edge_indices(bool hairLine,
|
||||
uint16_t fanCenterIdx,
|
||||
uint16_t edgeV0Idx,
|
||||
uint16_t** indices) {
|
||||
// when drawing lines we're appending line segments along
|
||||
// the contour. When applying the other fill rules we're
|
||||
// drawing triangle fans around fanCenterIdx.
|
||||
if (!hairLine) {
|
||||
*((*indices)++) = fanCenterIdx;
|
||||
}
|
||||
*((*indices)++) = edgeV0Idx;
|
||||
*((*indices)++) = edgeV0Idx + 1;
|
||||
}
|
||||
|
||||
~PathGeoBuilder() {
|
||||
this->emitMesh();
|
||||
this->putBackReserve();
|
||||
}
|
||||
|
||||
// Called before we start each path
|
||||
void beginInstance() {
|
||||
fSubpathIndexStart = fVertexOffset;
|
||||
fCurIdx = fIndices + fIndexOffset;
|
||||
fCurVert = fVertices + fVertexOffset;
|
||||
}
|
||||
|
||||
// Called after we end each path
|
||||
void endInstance() {
|
||||
fVertexOffset = fCurVert - fVertices;
|
||||
fIndexOffset = fCurIdx - fIndices;
|
||||
SkASSERT(fVertexOffset <= kVerticesPerChunk);
|
||||
SkASSERT(fIndexOffset <= this->maxIndices());
|
||||
}
|
||||
|
||||
/**
|
||||
* Path verbs
|
||||
*/
|
||||
void moveTo(const SkPoint& p) {
|
||||
needSpace(1);
|
||||
|
||||
fSubpathIndexStart = this->currentIndex();
|
||||
*(fCurVert++) = p;
|
||||
}
|
||||
|
||||
void addLine(const SkPoint& p) {
|
||||
needSpace(1, this->indexScale());
|
||||
|
||||
if (this->isIndexed()) {
|
||||
uint16_t prevIdx = this->currentIndex() - 1;
|
||||
appendCountourEdgeIndices(prevIdx);
|
||||
}
|
||||
*(fCurVert++) = p;
|
||||
}
|
||||
|
||||
void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
|
||||
this->needSpace(GrPathUtils::kMaxPointsPerCurve,
|
||||
GrPathUtils::kMaxPointsPerCurve * this->indexScale());
|
||||
|
||||
// First pt of quad is the pt we ended on in previous step
|
||||
uint16_t firstQPtIdx = this->currentIndex() - 1;
|
||||
uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
|
||||
pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
|
||||
GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
|
||||
if (this->isIndexed()) {
|
||||
for (uint16_t i = 0; i < numPts; ++i) {
|
||||
appendCountourEdgeIndices(firstQPtIdx + i);
|
||||
}
|
||||
static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
|
||||
SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
|
||||
bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
|
||||
// first pt of quad is the pt we ended on in previous step
|
||||
uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
|
||||
uint16_t numPts = (uint16_t)
|
||||
GrPathUtils::generateQuadraticPoints(
|
||||
pts[0], pts[1], pts[2],
|
||||
srcSpaceTolSqd, vert,
|
||||
GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
|
||||
if (indexed) {
|
||||
for (uint16_t i = 0; i < numPts; ++i) {
|
||||
append_countour_edge_indices(isHairline, subpathIdxStart,
|
||||
firstQPtIdx + i, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
|
||||
SkScalar srcSpaceTol) {
|
||||
SkAutoConicToQuads converter;
|
||||
const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
|
||||
for (int i = 0; i < converter.countQuads(); ++i) {
|
||||
this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
|
||||
}
|
||||
}
|
||||
|
||||
void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
|
||||
this->needSpace(GrPathUtils::kMaxPointsPerCurve,
|
||||
GrPathUtils::kMaxPointsPerCurve * this->indexScale());
|
||||
|
||||
// First pt of cubic is the pt we ended on in previous step
|
||||
uint16_t firstCPtIdx = this->currentIndex() - 1;
|
||||
uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
|
||||
pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
|
||||
GrPathUtils::cubicPointCount(pts, srcSpaceTol));
|
||||
if (this->isIndexed()) {
|
||||
for (uint16_t i = 0; i < numPts; ++i) {
|
||||
appendCountourEdgeIndices(firstCPtIdx + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addPath(const SkPath& path, SkScalar srcSpaceTol) {
|
||||
SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
|
||||
|
||||
SkPath::Iter iter(path, false);
|
||||
SkPoint pts[4];
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
SkPath::Verb verb = iter.next(pts);
|
||||
switch (verb) {
|
||||
case SkPath::kMove_Verb:
|
||||
this->moveTo(pts[0]);
|
||||
break;
|
||||
case SkPath::kLine_Verb:
|
||||
this->addLine(pts[1]);
|
||||
break;
|
||||
case SkPath::kConic_Verb:
|
||||
this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
|
||||
break;
|
||||
case SkPath::kClose_Verb:
|
||||
break;
|
||||
case SkPath::kDone_Verb:
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool PathHasMultipleSubpaths(const SkPath& path) {
|
||||
bool first = true;
|
||||
|
||||
SkPath::Iter iter(path, false);
|
||||
SkPath::Verb verb;
|
||||
|
||||
SkPoint pts[4];
|
||||
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||
if (SkPath::kMove_Verb == verb && !first) {
|
||||
return true;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Derived properties
|
||||
* TODO: Cache some of these for better performance, rather than re-computing?
|
||||
*/
|
||||
bool isIndexed() const {
|
||||
return kLines_GrPrimitiveType == fMesh.primitiveType() ||
|
||||
kTriangles_GrPrimitiveType == fMesh.primitiveType();
|
||||
}
|
||||
bool isHairline() const {
|
||||
return kLines_GrPrimitiveType == fMesh.primitiveType() ||
|
||||
kLineStrip_GrPrimitiveType == fMesh.primitiveType();
|
||||
}
|
||||
int indexScale() const {
|
||||
switch (fMesh.primitiveType()) {
|
||||
case kLines_GrPrimitiveType:
|
||||
return 2;
|
||||
case kTriangles_GrPrimitiveType:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int maxIndices() const { return kVerticesPerChunk * this->indexScale(); }
|
||||
|
||||
uint16_t currentIndex() const { return fCurVert - fVertices; }
|
||||
|
||||
void putBackReserve() {
|
||||
fTarget->putBackIndices((size_t)(this->maxIndices() - fIndexOffset));
|
||||
fTarget->putBackVertices((size_t)(kVerticesPerChunk - fVertexOffset), fVertexStride);
|
||||
}
|
||||
|
||||
// Allocate vertex and (possibly) index buffers
|
||||
void allocNewBuffers() {
|
||||
fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpace(fVertexStride, kVerticesPerChunk,
|
||||
&fVertexBuffer, &fFirstVertex));
|
||||
if (this->isIndexed()) {
|
||||
fIndices = fTarget->makeIndexSpace(this->maxIndices(), &fIndexBuffer, &fFirstIndex);
|
||||
}
|
||||
fVertexOffset = 0;
|
||||
fIndexOffset = 0;
|
||||
}
|
||||
|
||||
void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
|
||||
// When drawing lines we're appending line segments along the countour. When applying the
|
||||
// other fill rules we're drawing triangle fans around the start of the current (sub)path.
|
||||
if (!this->isHairline()) {
|
||||
*(fCurIdx++) = fSubpathIndexStart;
|
||||
}
|
||||
*(fCurIdx++) = edgeV0Idx;
|
||||
*(fCurIdx++) = edgeV0Idx + 1;
|
||||
}
|
||||
|
||||
// Emits a single draw with all accumulated vertex/index data
|
||||
void emitMesh() {
|
||||
if (!this->isIndexed()) {
|
||||
fMesh.setNonIndexedNonInstanced(fVertexOffset);
|
||||
} else {
|
||||
fMesh.setIndexed(fIndexBuffer, fIndexOffset, fFirstIndex, 0, fVertexOffset - 1);
|
||||
}
|
||||
fMesh.setVertexData(fVertexBuffer, fFirstVertex);
|
||||
fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
|
||||
}
|
||||
|
||||
void needSpace(int vertsNeeded, int indicesNeeded = 0) {
|
||||
if (fCurVert + vertsNeeded > fVertices + kVerticesPerChunk ||
|
||||
fCurIdx + indicesNeeded > fIndices + this->maxIndices()) {
|
||||
// We are about to run out of space (possibly)
|
||||
|
||||
// To maintain continuity, we need to remember one or two points from the current mesh.
|
||||
// Lines only need the last point, fills need the first point from the current contour.
|
||||
// We always grab both here, and append the ones we need at the end of this process.
|
||||
SkPoint lastPt = *(fCurVert - 1);
|
||||
SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
|
||||
|
||||
// Pretend that we've reached the end of an entire path, so our offsets are correct
|
||||
this->endInstance();
|
||||
|
||||
// Draw the mesh we've accumulated
|
||||
this->emitMesh();
|
||||
|
||||
// Put back any unused space, get new buffers
|
||||
this->putBackReserve();
|
||||
this->allocNewBuffers();
|
||||
|
||||
// Start a "new" path, which is really just a continuation of the in-progress one
|
||||
this->beginInstance();
|
||||
|
||||
// Append copies of the points we saved so the two meshes will weld properly
|
||||
if (!this->isHairline()) {
|
||||
*(fCurVert++) = subpathStartPt;
|
||||
}
|
||||
*(fCurVert++) = lastPt;
|
||||
}
|
||||
}
|
||||
|
||||
GrMesh fMesh;
|
||||
GrMeshDrawOp::Target* fTarget;
|
||||
size_t fVertexStride;
|
||||
GrGeometryProcessor* fGeometryProcessor;
|
||||
const GrPipeline* fPipeline;
|
||||
|
||||
const GrBuffer* fVertexBuffer;
|
||||
int fFirstVertex;
|
||||
SkPoint* fVertices;
|
||||
SkPoint* fCurVert;
|
||||
int fVertexOffset;
|
||||
|
||||
const GrBuffer* fIndexBuffer;
|
||||
int fFirstIndex;
|
||||
uint16_t* fIndices;
|
||||
uint16_t* fCurIdx;
|
||||
int fIndexOffset;
|
||||
uint16_t fSubpathIndexStart;
|
||||
};
|
||||
}
|
||||
|
||||
class DefaultPathOp final : public GrLegacyMeshDrawOp {
|
||||
public:
|
||||
@ -385,35 +153,112 @@ private:
|
||||
gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
|
||||
}
|
||||
|
||||
SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
|
||||
size_t vertexStride = gp->getVertexStride();
|
||||
SkASSERT(vertexStride == sizeof(SkPoint));
|
||||
|
||||
int instanceCount = fPaths.count();
|
||||
|
||||
// compute number of vertices
|
||||
int maxVertices = 0;
|
||||
|
||||
// We will use index buffers if we have multiple paths or one path with multiple contours
|
||||
bool isIndexed = instanceCount > 1;
|
||||
for (int i = 0; !isIndexed && i < instanceCount; i++) {
|
||||
const PathData& args = fPaths[i];
|
||||
isIndexed = isIndexed || PathGeoBuilder::PathHasMultipleSubpaths(args.fPath);
|
||||
}
|
||||
|
||||
// determine primitiveType
|
||||
GrPrimitiveType primitiveType;
|
||||
if (this->isHairline()) {
|
||||
primitiveType = isIndexed ? kLines_GrPrimitiveType : kLineStrip_GrPrimitiveType;
|
||||
} else {
|
||||
primitiveType = isIndexed ? kTriangles_GrPrimitiveType : kTriangleFan_GrPrimitiveType;
|
||||
}
|
||||
|
||||
PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(), this->pipeline());
|
||||
|
||||
// fill buffers
|
||||
for (int i = 0; i < instanceCount; i++) {
|
||||
const PathData& args = fPaths[i];
|
||||
|
||||
pathGeoBuilder.beginInstance();
|
||||
pathGeoBuilder.addPath(args.fPath, args.fTolerance);
|
||||
pathGeoBuilder.endInstance();
|
||||
int contourCount;
|
||||
maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
|
||||
args.fTolerance);
|
||||
|
||||
isIndexed = isIndexed || contourCount > 1;
|
||||
}
|
||||
|
||||
if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
|
||||
//SkDebugf("Cannot render path (%d)\n", maxVertices);
|
||||
return;
|
||||
}
|
||||
|
||||
// determine primitiveType
|
||||
int maxIndices = 0;
|
||||
GrPrimitiveType primitiveType;
|
||||
if (this->isHairline()) {
|
||||
if (isIndexed) {
|
||||
maxIndices = 2 * maxVertices;
|
||||
primitiveType = kLines_GrPrimitiveType;
|
||||
} else {
|
||||
primitiveType = kLineStrip_GrPrimitiveType;
|
||||
}
|
||||
} else {
|
||||
if (isIndexed) {
|
||||
maxIndices = 3 * maxVertices;
|
||||
primitiveType = kTriangles_GrPrimitiveType;
|
||||
} else {
|
||||
primitiveType = kTriangleFan_GrPrimitiveType;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate vertex / index buffers
|
||||
const GrBuffer* vertexBuffer;
|
||||
int firstVertex;
|
||||
|
||||
void* verts = target->makeVertexSpace(vertexStride, maxVertices,
|
||||
&vertexBuffer, &firstVertex);
|
||||
|
||||
if (!verts) {
|
||||
SkDebugf("Could not allocate vertices\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const GrBuffer* indexBuffer = nullptr;
|
||||
int firstIndex = 0;
|
||||
|
||||
void* indices = nullptr;
|
||||
if (isIndexed) {
|
||||
indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
|
||||
|
||||
if (!indices) {
|
||||
SkDebugf("Could not allocate indices\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// fill buffers
|
||||
int vertexOffset = 0;
|
||||
int indexOffset = 0;
|
||||
for (int i = 0; i < instanceCount; i++) {
|
||||
const PathData& args = fPaths[i];
|
||||
|
||||
int vertexCnt = 0;
|
||||
int indexCnt = 0;
|
||||
if (!this->createGeom(verts,
|
||||
vertexOffset,
|
||||
indices,
|
||||
indexOffset,
|
||||
&vertexCnt,
|
||||
&indexCnt,
|
||||
args.fPath,
|
||||
args.fTolerance,
|
||||
isIndexed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vertexOffset += vertexCnt;
|
||||
indexOffset += indexCnt;
|
||||
SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
|
||||
}
|
||||
|
||||
GrMesh mesh(primitiveType);
|
||||
if (!isIndexed) {
|
||||
mesh.setNonIndexedNonInstanced(vertexOffset);
|
||||
} else {
|
||||
mesh.setIndexed(indexBuffer, indexOffset, firstIndex, 0, vertexOffset - 1);
|
||||
}
|
||||
mesh.setVertexData(vertexBuffer, firstVertex);
|
||||
target->draw(gp.get(), this->pipeline(), mesh);
|
||||
|
||||
// put back reserves
|
||||
target->putBackIndices((size_t)(maxIndices - indexOffset));
|
||||
target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
|
||||
}
|
||||
|
||||
bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
|
||||
@ -444,6 +289,98 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool createGeom(void* vertices,
|
||||
size_t vertexOffset,
|
||||
void* indices,
|
||||
size_t indexOffset,
|
||||
int* vertexCnt,
|
||||
int* indexCnt,
|
||||
const SkPath& path,
|
||||
SkScalar srcSpaceTol,
|
||||
bool isIndexed) const {
|
||||
SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
|
||||
|
||||
uint16_t indexOffsetU16 = (uint16_t)indexOffset;
|
||||
uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
|
||||
|
||||
uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
|
||||
uint16_t* idx = idxBase;
|
||||
uint16_t subpathIdxStart = vertexOffsetU16;
|
||||
|
||||
SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
|
||||
SkPoint* vert = base;
|
||||
|
||||
SkPoint pts[4];
|
||||
|
||||
bool first = true;
|
||||
int subpath = 0;
|
||||
|
||||
SkPath::Iter iter(path, false);
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
SkPath::Verb verb = iter.next(pts);
|
||||
switch (verb) {
|
||||
case SkPath::kMove_Verb:
|
||||
if (!first) {
|
||||
uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
|
||||
subpathIdxStart = currIdx;
|
||||
++subpath;
|
||||
}
|
||||
*vert = pts[0];
|
||||
vert++;
|
||||
break;
|
||||
case SkPath::kLine_Verb:
|
||||
if (isIndexed) {
|
||||
uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
|
||||
append_countour_edge_indices(this->isHairline(), subpathIdxStart,
|
||||
prevIdx, &idx);
|
||||
}
|
||||
*(vert++) = pts[1];
|
||||
break;
|
||||
case SkPath::kConic_Verb: {
|
||||
SkScalar weight = iter.conicWeight();
|
||||
SkAutoConicToQuads converter;
|
||||
const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
|
||||
for (int i = 0; i < converter.countQuads(); ++i) {
|
||||
add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
|
||||
isIndexed, this->isHairline(), subpathIdxStart,
|
||||
(int)vertexOffset, &idx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SkPath::kQuad_Verb:
|
||||
add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
|
||||
this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
|
||||
break;
|
||||
case SkPath::kCubic_Verb: {
|
||||
// first pt of cubic is the pt we ended on in previous step
|
||||
uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
|
||||
uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
|
||||
pts[0], pts[1], pts[2], pts[3],
|
||||
srcSpaceTolSqd, &vert,
|
||||
GrPathUtils::cubicPointCount(pts, srcSpaceTol));
|
||||
if (isIndexed) {
|
||||
for (uint16_t i = 0; i < numPts; ++i) {
|
||||
append_countour_edge_indices(this->isHairline(), subpathIdxStart,
|
||||
firstCPtIdx + i, &idx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SkPath::kClose_Verb:
|
||||
break;
|
||||
case SkPath::kDone_Verb:
|
||||
done = true;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
*vertexCnt = static_cast<int>(vert - base);
|
||||
*indexCnt = static_cast<int>(idx - idxBase);
|
||||
return true;
|
||||
}
|
||||
|
||||
GrColor color() const { return fColor; }
|
||||
uint8_t coverage() const { return fCoverage; }
|
||||
bool usesLocalCoords() const { return fUsesLocalCoords; }
|
||||
|
Loading…
Reference in New Issue
Block a user