Added GrAAFlatteningConvexPathRenderer.
This is an alternate version of GrAAConvexPathRenderer which handles curves by first flattening them to straight lines. BUG=skia: Review URL: https://codereview.chromium.org/1158803002
This commit is contained in:
parent
523beb7fd9
commit
1a1b3ac0d4
@ -57,6 +57,8 @@
|
|||||||
'<(skia_src_path)/gpu/GrAAHairLinePathRenderer.h',
|
'<(skia_src_path)/gpu/GrAAHairLinePathRenderer.h',
|
||||||
'<(skia_src_path)/gpu/GrAAConvexPathRenderer.cpp',
|
'<(skia_src_path)/gpu/GrAAConvexPathRenderer.cpp',
|
||||||
'<(skia_src_path)/gpu/GrAAConvexPathRenderer.h',
|
'<(skia_src_path)/gpu/GrAAConvexPathRenderer.h',
|
||||||
|
'<(skia_src_path)/gpu/GrAALinearizingConvexPathRenderer.cpp',
|
||||||
|
'<(skia_src_path)/gpu/GrAALinearizingConvexPathRenderer.h',
|
||||||
'<(skia_src_path)/gpu/GrAAConvexTessellator.cpp',
|
'<(skia_src_path)/gpu/GrAAConvexTessellator.cpp',
|
||||||
'<(skia_src_path)/gpu/GrAAConvexTessellator.h',
|
'<(skia_src_path)/gpu/GrAAConvexTessellator.h',
|
||||||
'<(skia_src_path)/gpu/GrAADistanceFieldPathRenderer.cpp',
|
'<(skia_src_path)/gpu/GrAADistanceFieldPathRenderer.cpp',
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "SkPath.h"
|
#include "SkPath.h"
|
||||||
#include "SkPoint.h"
|
#include "SkPoint.h"
|
||||||
#include "SkString.h"
|
#include "SkString.h"
|
||||||
|
#include "GrPathUtils.h"
|
||||||
|
|
||||||
// Next steps:
|
// Next steps:
|
||||||
// use in AAConvexPathRenderer
|
// use in AAConvexPathRenderer
|
||||||
@ -51,13 +52,15 @@ static SkScalar abs_dist_from_line(const SkPoint& p0, const SkVector& v, const S
|
|||||||
|
|
||||||
int GrAAConvexTessellator::addPt(const SkPoint& pt,
|
int GrAAConvexTessellator::addPt(const SkPoint& pt,
|
||||||
SkScalar depth,
|
SkScalar depth,
|
||||||
bool movable) {
|
bool movable,
|
||||||
|
bool isCurve) {
|
||||||
this->validate();
|
this->validate();
|
||||||
|
|
||||||
int index = fPts.count();
|
int index = fPts.count();
|
||||||
*fPts.push() = pt;
|
*fPts.push() = pt;
|
||||||
*fDepths.push() = depth;
|
*fDepths.push() = depth;
|
||||||
*fMovable.push() = movable;
|
*fMovable.push() = movable;
|
||||||
|
*fIsCurve.push() = isCurve;
|
||||||
|
|
||||||
this->validate();
|
this->validate();
|
||||||
return index;
|
return index;
|
||||||
@ -236,7 +239,6 @@ bool GrAAConvexTessellator::computePtAlongBisector(int startIdx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) {
|
bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) {
|
||||||
SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
|
|
||||||
SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
|
SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
|
||||||
|
|
||||||
// Outer ring: 3*numPts
|
// Outer ring: 3*numPts
|
||||||
@ -250,7 +252,8 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat
|
|||||||
|
|
||||||
fNorms.setReserve(path.countPoints());
|
fNorms.setReserve(path.countPoints());
|
||||||
|
|
||||||
SkScalar minCross = SK_ScalarMax, maxCross = -SK_ScalarMax;
|
SkDEBUGCODE(fMinCross = SK_ScalarMax;)
|
||||||
|
SkDEBUGCODE(fMaxCross = -SK_ScalarMax;)
|
||||||
|
|
||||||
// TODO: is there a faster way to extract the points from the path? Perhaps
|
// TODO: is there a faster way to extract the points from the path? Perhaps
|
||||||
// get all the points via a new entry point, transform them all in bulk
|
// get all the points via a new entry point, transform them all in bulk
|
||||||
@ -261,38 +264,16 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat
|
|||||||
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||||
switch (verb) {
|
switch (verb) {
|
||||||
case SkPath::kLine_Verb:
|
case SkPath::kLine_Verb:
|
||||||
m.mapPoints(&pts[1], 1);
|
this->lineTo(m, pts[1], false);
|
||||||
if (this->numPts() > 0 && duplicate_pt(pts[1], this->lastPoint())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkASSERT(fPts.count() <= 1 || fPts.count() == fNorms.count()+1);
|
|
||||||
if (this->numPts() >= 2 &&
|
|
||||||
abs_dist_from_line(fPts.top(), fNorms.top(), pts[1]) < kClose) {
|
|
||||||
// The old last point is on the line from the second to last to the new point
|
|
||||||
this->popLastPt();
|
|
||||||
fNorms.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->addPt(pts[1], 0.0f, false);
|
|
||||||
if (this->numPts() > 1) {
|
|
||||||
*fNorms.push() = fPts.top() - fPts[fPts.count()-2];
|
|
||||||
SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
|
|
||||||
SkASSERT(len > 0.0f);
|
|
||||||
SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->numPts() >= 3) {
|
|
||||||
int cur = this->numPts()-1;
|
|
||||||
SkScalar cross = SkPoint::CrossProduct(fNorms[cur-1], fNorms[cur-2]);
|
|
||||||
maxCross = SkTMax(maxCross, cross);
|
|
||||||
minCross = SkTMin(minCross, cross);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SkPath::kQuad_Verb:
|
case SkPath::kQuad_Verb:
|
||||||
case SkPath::kConic_Verb:
|
this->quadTo(m, pts);
|
||||||
|
break;
|
||||||
case SkPath::kCubic_Verb:
|
case SkPath::kCubic_Verb:
|
||||||
SkASSERT(false);
|
this->cubicTo(m, pts);
|
||||||
|
break;
|
||||||
|
case SkPath::kConic_Verb:
|
||||||
|
this->conicTo(m, pts, iter.conicWeight());
|
||||||
break;
|
break;
|
||||||
case SkPath::kMove_Verb:
|
case SkPath::kMove_Verb:
|
||||||
case SkPath::kClose_Verb:
|
case SkPath::kClose_Verb:
|
||||||
@ -342,16 +323,14 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the cross produce of the final trio
|
// Check the cross product of the final trio
|
||||||
SkScalar cross = SkPoint::CrossProduct(fNorms[0], fNorms.top());
|
SkScalar cross = SkPoint::CrossProduct(fNorms[0], fNorms.top());
|
||||||
maxCross = SkTMax(maxCross, cross);
|
SkDEBUGCODE(fMaxCross = SkTMax(fMaxCross, cross));
|
||||||
minCross = SkTMin(minCross, cross);
|
SkDEBUGCODE(fMinCross = SkTMin(fMinCross, cross));
|
||||||
|
SkASSERT((fMaxCross >= 0.0f) == (fMinCross >= 0.0f));
|
||||||
if (maxCross > 0.0f) {
|
if (cross > 0.0f) {
|
||||||
SkASSERT(minCross >= 0.0f);
|
|
||||||
fSide = SkPoint::kRight_Side;
|
fSide = SkPoint::kRight_Side;
|
||||||
} else {
|
} else {
|
||||||
SkASSERT(minCross <= 0.0f);
|
|
||||||
fSide = SkPoint::kLeft_Side;
|
fSide = SkPoint::kLeft_Side;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,69 +383,109 @@ void GrAAConvexTessellator::createOuterRing() {
|
|||||||
|
|
||||||
const int numPts = fPts.count();
|
const int numPts = fPts.count();
|
||||||
|
|
||||||
// For each vertex of the original polygon we add three points to the
|
|
||||||
// outset polygon - one extending perpendicular to each impinging edge
|
|
||||||
// and one along the bisector. Two triangles are added for each corner
|
|
||||||
// and two are added along each edge.
|
|
||||||
int prev = numPts - 1;
|
int prev = numPts - 1;
|
||||||
int lastPerpIdx = -1, firstPerpIdx = -1, newIdx0, newIdx1, newIdx2;
|
int lastPerpIdx = -1, firstPerpIdx = -1, newIdx0, newIdx1, newIdx2;
|
||||||
for (int cur = 0; cur < numPts; ++cur) {
|
for (int cur = 0; cur < numPts; ++cur) {
|
||||||
// The perpendicular point for the last edge
|
if (fIsCurve[cur]) {
|
||||||
SkPoint temp = fNorms[prev];
|
// Inside a curve, we assume that the curvature is shallow enough (due to tesselation)
|
||||||
temp.scale(fTargetDepth);
|
// that we only need one corner point. Mathematically, the distance the corner point
|
||||||
temp += fPts[cur];
|
// gets shifted out should depend on the angle between the two line segments (as in
|
||||||
|
// mitering), but again due to tesselation we assume that this angle is small and
|
||||||
|
// therefore the correction factor is negligible and we do not bother with it.
|
||||||
|
|
||||||
// We know it isn't a duplicate of the prior point (since it and this
|
// The bisector outset point
|
||||||
// one are just perpendicular offsets from the non-merged polygon points)
|
SkPoint temp = fBisectors[cur];
|
||||||
newIdx0 = this->addPt(temp, -fTargetDepth, false);
|
temp.scale(-fTargetDepth); // the bisectors point in
|
||||||
|
temp += fPts[cur];
|
||||||
|
|
||||||
// The bisector outset point
|
// double-check our "sufficiently flat" assumption; we want the bisector point to be
|
||||||
temp = fBisectors[cur];
|
// close to the normal point.
|
||||||
temp.scale(-fTargetDepth); // the bisectors point in
|
#define kFlatnessTolerance 1.0f
|
||||||
temp += fPts[cur];
|
SkDEBUGCODE(SkPoint prevNormal = fNorms[prev];)
|
||||||
|
SkDEBUGCODE(prevNormal.scale(fTargetDepth);)
|
||||||
|
SkDEBUGCODE(prevNormal += fPts[cur];)
|
||||||
|
SkASSERT((temp - prevNormal).length() < kFlatnessTolerance);
|
||||||
|
|
||||||
// For very shallow angles all the corner points could fuse
|
newIdx1 = this->addPt(temp, -fTargetDepth, false, true);
|
||||||
if (duplicate_pt(temp, this->point(newIdx0))) {
|
|
||||||
newIdx1 = newIdx0;
|
if (0 == cur) {
|
||||||
} else {
|
// Store the index of the first perpendicular point to finish up
|
||||||
newIdx1 = this->addPt(temp, -fTargetDepth, false);
|
firstPerpIdx = newIdx1;
|
||||||
|
SkASSERT(-1 == lastPerpIdx);
|
||||||
|
} else {
|
||||||
|
// The triangles for the previous edge
|
||||||
|
this->addTri(prev, newIdx1, cur);
|
||||||
|
this->addTri(prev, lastPerpIdx, newIdx1);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
|
// Track the last perpendicular outset point so we can construct the
|
||||||
|
// trailing edge triangles.
|
||||||
|
lastPerpIdx = newIdx1;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// For each vertex of the original polygon we add three points to the
|
||||||
|
// outset polygon - one extending perpendicular to each impinging edge
|
||||||
|
// and one along the bisector. Two triangles are added for each corner
|
||||||
|
// and two are added along each edge.
|
||||||
|
|
||||||
// The perpendicular point for the next edge.
|
// The perpendicular point for the last edge
|
||||||
temp = fNorms[cur];
|
SkPoint temp = fNorms[prev];
|
||||||
temp.scale(fTargetDepth);
|
temp.scale(fTargetDepth);
|
||||||
temp += fPts[cur];
|
temp += fPts[cur];
|
||||||
|
|
||||||
// For very shallow angles all the corner points could fuse.
|
// We know it isn't a duplicate of the prior point (since it and this
|
||||||
if (duplicate_pt(temp, this->point(newIdx1))) {
|
// one are just perpendicular offsets from the non-merged polygon points)
|
||||||
newIdx2 = newIdx1;
|
newIdx0 = this->addPt(temp, -fTargetDepth, false, false);
|
||||||
} else {
|
|
||||||
newIdx2 = this->addPt(temp, -fTargetDepth, false);
|
// The bisector outset point
|
||||||
|
temp = fBisectors[cur];
|
||||||
|
temp.scale(-fTargetDepth); // the bisectors point in
|
||||||
|
temp += fPts[cur];
|
||||||
|
|
||||||
|
// For very shallow angles all the corner points could fuse
|
||||||
|
if (duplicate_pt(temp, this->point(newIdx0))) {
|
||||||
|
newIdx1 = newIdx0;
|
||||||
|
} else {
|
||||||
|
newIdx1 = this->addPt(temp, -fTargetDepth, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The perpendicular point for the next edge.
|
||||||
|
temp = fNorms[cur];
|
||||||
|
temp.scale(fTargetDepth);
|
||||||
|
temp += fPts[cur];
|
||||||
|
|
||||||
|
// For very shallow angles all the corner points could fuse.
|
||||||
|
if (duplicate_pt(temp, this->point(newIdx1))) {
|
||||||
|
newIdx2 = newIdx1;
|
||||||
|
} else {
|
||||||
|
newIdx2 = this->addPt(temp, -fTargetDepth, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == cur) {
|
||||||
|
// Store the index of the first perpendicular point to finish up
|
||||||
|
firstPerpIdx = newIdx0;
|
||||||
|
SkASSERT(-1 == lastPerpIdx);
|
||||||
|
} else {
|
||||||
|
// The triangles for the previous edge
|
||||||
|
this->addTri(prev, newIdx0, cur);
|
||||||
|
this->addTri(prev, lastPerpIdx, newIdx0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The two triangles for the corner
|
||||||
|
this->addTri(cur, newIdx0, newIdx1);
|
||||||
|
this->addTri(cur, newIdx1, newIdx2);
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
|
// Track the last perpendicular outset point so we can construct the
|
||||||
|
// trailing edge triangles.
|
||||||
|
lastPerpIdx = newIdx2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == cur) {
|
|
||||||
// Store the index of the first perpendicular point to finish up
|
|
||||||
firstPerpIdx = newIdx0;
|
|
||||||
SkASSERT(-1 == lastPerpIdx);
|
|
||||||
} else {
|
|
||||||
// The triangles for the previous edge
|
|
||||||
this->addTri(prev, newIdx0, cur);
|
|
||||||
this->addTri(prev, lastPerpIdx, newIdx0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The two triangles for the corner
|
|
||||||
this->addTri(cur, newIdx0, newIdx1);
|
|
||||||
this->addTri(cur, newIdx1, newIdx2);
|
|
||||||
|
|
||||||
prev = cur;
|
|
||||||
// Track the last perpendicular outset point so we can construct the
|
|
||||||
// trailing edge triangles.
|
|
||||||
lastPerpIdx = newIdx2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pick up the final edge rect
|
// pick up the final edge rect
|
||||||
this->addTri(numPts-1, firstPerpIdx, 0);
|
this->addTri(numPts - 1, firstPerpIdx, 0);
|
||||||
this->addTri(numPts-1, lastPerpIdx, firstPerpIdx);
|
this->addTri(numPts - 1, lastPerpIdx, firstPerpIdx);
|
||||||
|
|
||||||
this->validate();
|
this->validate();
|
||||||
}
|
}
|
||||||
@ -592,7 +611,7 @@ bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing
|
|||||||
// if the originating index is still valid then this point wasn't
|
// if the originating index is still valid then this point wasn't
|
||||||
// fused (and is thus movable)
|
// fused (and is thus movable)
|
||||||
newIdx = this->addPt(fCandidateVerts.point(i), depth,
|
newIdx = this->addPt(fCandidateVerts.point(i), depth,
|
||||||
fCandidateVerts.originatingIdx(i) != -1);
|
fCandidateVerts.originatingIdx(i) != -1, false);
|
||||||
} else {
|
} else {
|
||||||
SkASSERT(fCandidateVerts.originatingIdx(i) != -1);
|
SkASSERT(fCandidateVerts.originatingIdx(i) != -1);
|
||||||
this->updatePt(fCandidateVerts.originatingIdx(i), fCandidateVerts.point(i), depth);
|
this->updatePt(fCandidateVerts.originatingIdx(i), fCandidateVerts.point(i), depth);
|
||||||
@ -768,6 +787,84 @@ void GrAAConvexTessellator::checkAllDepths() const {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define kQuadTolerance 0.2f
|
||||||
|
#define kCubicTolerance 0.2f
|
||||||
|
#define kConicTolerance 0.5f
|
||||||
|
|
||||||
|
void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, bool isCurve) {
|
||||||
|
m.mapPoints(&p, 1);
|
||||||
|
if (this->numPts() > 0 && duplicate_pt(p, this->lastPoint())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkASSERT(fPts.count() <= 1 || fPts.count() == fNorms.count()+1);
|
||||||
|
if (this->numPts() >= 2 &&
|
||||||
|
abs_dist_from_line(fPts.top(), fNorms.top(), p) < kClose) {
|
||||||
|
// The old last point is on the line from the second to last to the new point
|
||||||
|
this->popLastPt();
|
||||||
|
fNorms.pop();
|
||||||
|
fIsCurve.pop();
|
||||||
|
}
|
||||||
|
this->addPt(p, 0.0f, false, isCurve);
|
||||||
|
if (this->numPts() > 1) {
|
||||||
|
*fNorms.push() = fPts.top() - fPts[fPts.count()-2];
|
||||||
|
SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
|
||||||
|
SkASSERT(len > 0.0f);
|
||||||
|
SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length()));
|
||||||
|
}
|
||||||
|
SkDEBUGCODE(
|
||||||
|
if (this->numPts() >= 3) {
|
||||||
|
int cur = this->numPts()-1;
|
||||||
|
SkScalar cross = SkPoint::CrossProduct(fNorms[cur-1], fNorms[cur-2]);
|
||||||
|
fMaxCross = SkTMax(fMaxCross, cross);
|
||||||
|
fMinCross = SkTMin(fMinCross, cross);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrAAConvexTessellator::quadTo(const SkMatrix& m, SkPoint pts[3]) {
|
||||||
|
int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
|
||||||
|
fPointBuffer.setReserve(maxCount);
|
||||||
|
SkPoint* target = fPointBuffer.begin();
|
||||||
|
int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
|
||||||
|
kQuadTolerance, &target, maxCount);
|
||||||
|
fPointBuffer.setCount(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
lineTo(m, fPointBuffer[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrAAConvexTessellator::cubicTo(const SkMatrix& m, SkPoint pts[4]) {
|
||||||
|
int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
|
||||||
|
fPointBuffer.setReserve(maxCount);
|
||||||
|
SkPoint* target = fPointBuffer.begin();
|
||||||
|
int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
|
||||||
|
kCubicTolerance, &target, maxCount);
|
||||||
|
fPointBuffer.setCount(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
lineTo(m, fPointBuffer[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// include down here to avoid compilation errors caused by "-" overload in SkGeometry.h
|
||||||
|
#include "SkGeometry.h"
|
||||||
|
|
||||||
|
void GrAAConvexTessellator::conicTo(const SkMatrix& m, SkPoint* pts, SkScalar w) {
|
||||||
|
SkAutoConicToQuads quadder;
|
||||||
|
const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
|
||||||
|
SkPoint lastPoint = *(quads++);
|
||||||
|
int count = quadder.countQuads();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
SkPoint quadPts[3];
|
||||||
|
quadPts[0] = lastPoint;
|
||||||
|
quadPts[1] = quads[0];
|
||||||
|
quadPts[2] = i == count - 1 ? pts[2] : quads[1];
|
||||||
|
quadTo(m, quadPts);
|
||||||
|
lastPoint = quadPts[2];
|
||||||
|
quads += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
#if GR_AA_CONVEX_TESSELLATOR_VIZ
|
#if GR_AA_CONVEX_TESSELLATOR_VIZ
|
||||||
static const SkScalar kPointRadius = 0.02f;
|
static const SkScalar kPointRadius = 0.02f;
|
||||||
|
@ -165,7 +165,7 @@ private:
|
|||||||
// Movable points are those that can be slid along their bisector.
|
// Movable points are those that can be slid along their bisector.
|
||||||
// Basically, a point is immovable if it is part of the original
|
// Basically, a point is immovable if it is part of the original
|
||||||
// polygon or it results from the fusing of two bisectors.
|
// polygon or it results from the fusing of two bisectors.
|
||||||
int addPt(const SkPoint& pt, SkScalar depth, bool movable);
|
int addPt(const SkPoint& pt, SkScalar depth, bool movable, bool isCurve);
|
||||||
void popLastPt();
|
void popLastPt();
|
||||||
void popFirstPtShuffle();
|
void popFirstPtShuffle();
|
||||||
|
|
||||||
@ -185,6 +185,14 @@ private:
|
|||||||
int edgeIdx, SkScalar desiredDepth,
|
int edgeIdx, SkScalar desiredDepth,
|
||||||
SkPoint* result) const;
|
SkPoint* result) const;
|
||||||
|
|
||||||
|
void lineTo(const SkMatrix& m, SkPoint p, bool isCurve);
|
||||||
|
|
||||||
|
void quadTo(const SkMatrix& m, SkPoint pts[3]);
|
||||||
|
|
||||||
|
void cubicTo(const SkMatrix& m, SkPoint pts[4]);
|
||||||
|
|
||||||
|
void conicTo(const SkMatrix& m, SkPoint pts[3], SkScalar w);
|
||||||
|
|
||||||
void terminate(const Ring& lastRing);
|
void terminate(const Ring& lastRing);
|
||||||
|
|
||||||
// return false on failure/degenerate path
|
// return false on failure/degenerate path
|
||||||
@ -217,6 +225,11 @@ private:
|
|||||||
// The inward facing bisector at each point in the original polygon. Only
|
// The inward facing bisector at each point in the original polygon. Only
|
||||||
// needed for exterior ring creation and then handed off to the initial ring.
|
// needed for exterior ring creation and then handed off to the initial ring.
|
||||||
SkTDArray<SkVector> fBisectors;
|
SkTDArray<SkVector> fBisectors;
|
||||||
|
|
||||||
|
// Tracks whether a given point is interior to a curve. Such points are
|
||||||
|
// assumed to have shallow curvature.
|
||||||
|
SkTDArray<bool> fIsCurve;
|
||||||
|
|
||||||
SkPoint::Side fSide; // winding of the original polygon
|
SkPoint::Side fSide; // winding of the original polygon
|
||||||
|
|
||||||
// The triangulation of the points
|
// The triangulation of the points
|
||||||
@ -233,10 +246,16 @@ private:
|
|||||||
|
|
||||||
SkScalar fTargetDepth;
|
SkScalar fTargetDepth;
|
||||||
|
|
||||||
|
SkTDArray<SkPoint> fPointBuffer;
|
||||||
|
|
||||||
// If some goes wrong with the inset computation the tessellator will
|
// If some goes wrong with the inset computation the tessellator will
|
||||||
// truncate the creation of the inset polygon. In this case the depth
|
// truncate the creation of the inset polygon. In this case the depth
|
||||||
// check will complain.
|
// check will complain.
|
||||||
SkDEBUGCODE(bool fShouldCheckDepths;)
|
SkDEBUGCODE(bool fShouldCheckDepths;)
|
||||||
|
|
||||||
|
SkDEBUGCODE(SkScalar fMinCross;)
|
||||||
|
|
||||||
|
SkDEBUGCODE(SkScalar fMaxCross;)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
320
src/gpu/GrAALinearizingConvexPathRenderer.cpp
Normal file
320
src/gpu/GrAALinearizingConvexPathRenderer.cpp
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GrAALinearizingConvexPathRenderer.h"
|
||||||
|
|
||||||
|
#include "GrAAConvexTessellator.h"
|
||||||
|
#include "GrBatch.h"
|
||||||
|
#include "GrBatchTarget.h"
|
||||||
|
#include "GrBatchTest.h"
|
||||||
|
#include "GrContext.h"
|
||||||
|
#include "GrDefaultGeoProcFactory.h"
|
||||||
|
#include "GrGeometryProcessor.h"
|
||||||
|
#include "GrInvariantOutput.h"
|
||||||
|
#include "GrPathUtils.h"
|
||||||
|
#include "GrProcessor.h"
|
||||||
|
#include "GrPipelineBuilder.h"
|
||||||
|
#include "GrStrokeInfo.h"
|
||||||
|
#include "SkGeometry.h"
|
||||||
|
#include "SkString.h"
|
||||||
|
#include "SkTraceEvent.h"
|
||||||
|
#include "gl/GrGLProcessor.h"
|
||||||
|
#include "gl/GrGLSL.h"
|
||||||
|
#include "gl/GrGLGeometryProcessor.h"
|
||||||
|
#include "gl/builders/GrGLProgramBuilder.h"
|
||||||
|
|
||||||
|
#define DEFAULT_BUFFER_SIZE 100
|
||||||
|
|
||||||
|
GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool GrAALinearizingConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
|
||||||
|
const GrPipelineBuilder*,
|
||||||
|
const SkMatrix& viewMatrix,
|
||||||
|
const SkPath& path,
|
||||||
|
const GrStrokeInfo& stroke,
|
||||||
|
bool antiAlias) const {
|
||||||
|
return (antiAlias && stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the result vertices and indices from the GrAAConvexTessellator
|
||||||
|
static void extract_verts(const GrAAConvexTessellator& tess,
|
||||||
|
void* vertices,
|
||||||
|
size_t vertexStride,
|
||||||
|
GrColor color,
|
||||||
|
uint16_t firstIndex,
|
||||||
|
uint16_t* idxs,
|
||||||
|
bool tweakAlphaForCoverage) {
|
||||||
|
intptr_t verts = reinterpret_cast<intptr_t>(vertices);
|
||||||
|
|
||||||
|
for (int i = 0; i < tess.numPts(); ++i) {
|
||||||
|
*((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make 'verts' point to the colors
|
||||||
|
verts += sizeof(SkPoint);
|
||||||
|
for (int i = 0; i < tess.numPts(); ++i) {
|
||||||
|
SkASSERT(tess.depth(i) >= -0.5f && tess.depth(i) <= 0.5f);
|
||||||
|
if (tweakAlphaForCoverage) {
|
||||||
|
SkASSERT(SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)) <= 255);
|
||||||
|
unsigned scale = SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f));
|
||||||
|
GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
|
||||||
|
*reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
|
||||||
|
} else {
|
||||||
|
*reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
|
||||||
|
*reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
|
||||||
|
tess.depth(i) + 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tess.numIndices(); ++i) {
|
||||||
|
idxs[i] = tess.index(i) + firstIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
|
||||||
|
const SkMatrix& localMatrix,
|
||||||
|
bool usesLocalCoords,
|
||||||
|
bool coverageIgnored) {
|
||||||
|
uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
|
||||||
|
if (!tweakAlphaForCoverage) {
|
||||||
|
flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, usesLocalCoords, coverageIgnored,
|
||||||
|
SkMatrix::I(), localMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AAFlatteningConvexPathBatch : public GrBatch {
|
||||||
|
public:
|
||||||
|
struct Geometry {
|
||||||
|
GrColor fColor;
|
||||||
|
SkMatrix fViewMatrix;
|
||||||
|
SkPath fPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GrBatch* Create(const Geometry& geometry) {
|
||||||
|
return SkNEW_ARGS(AAFlatteningConvexPathBatch, (geometry));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name() const override { return "AAConvexBatch"; }
|
||||||
|
|
||||||
|
void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
|
||||||
|
// When this is called on a batch, there is only one geometry bundle
|
||||||
|
out->setKnownFourComponents(fGeoData[0].fColor);
|
||||||
|
}
|
||||||
|
void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
|
||||||
|
out->setUnknownSingleComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initBatchTracker(const GrPipelineInfo& init) override {
|
||||||
|
// Handle any color overrides
|
||||||
|
if (init.fColorIgnored) {
|
||||||
|
fGeoData[0].fColor = GrColor_ILLEGAL;
|
||||||
|
} else if (GrColor_ILLEGAL != init.fOverrideColor) {
|
||||||
|
fGeoData[0].fColor = init.fOverrideColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup batch properties
|
||||||
|
fBatch.fColorIgnored = init.fColorIgnored;
|
||||||
|
fBatch.fColor = fGeoData[0].fColor;
|
||||||
|
fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
|
||||||
|
fBatch.fCoverageIgnored = init.fCoverageIgnored;
|
||||||
|
fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
|
||||||
|
fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(GrBatchTarget* batchTarget, const GrPipeline* pipeline, int vertexCount,
|
||||||
|
size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) {
|
||||||
|
if (vertexCount == 0 || indexCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const GrVertexBuffer* vertexBuffer;
|
||||||
|
GrVertices info;
|
||||||
|
int firstVertex;
|
||||||
|
void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer,
|
||||||
|
&firstVertex);
|
||||||
|
if (!verts) {
|
||||||
|
SkDebugf("Could not allocate vertices\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(verts, vertices, vertexCount * vertexStride);
|
||||||
|
|
||||||
|
const GrIndexBuffer* indexBuffer;
|
||||||
|
int firstIndex;
|
||||||
|
uint16_t* idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
|
||||||
|
if (!idxs) {
|
||||||
|
SkDebugf("Could not allocate indices\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(idxs, indices, indexCount * sizeof(uint16_t));
|
||||||
|
info.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
|
||||||
|
firstIndex, vertexCount, indexCount);
|
||||||
|
batchTarget->draw(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
|
||||||
|
bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
|
||||||
|
|
||||||
|
SkMatrix invert;
|
||||||
|
if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
|
||||||
|
SkDebugf("Could not invert viewmatrix\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup GrGeometryProcessor
|
||||||
|
SkAutoTUnref<const GrGeometryProcessor> gp(
|
||||||
|
create_fill_gp(canTweakAlphaForCoverage, invert,
|
||||||
|
this->usesLocalCoords(),
|
||||||
|
this->coverageIgnored()));
|
||||||
|
|
||||||
|
batchTarget->initDraw(gp, pipeline);
|
||||||
|
|
||||||
|
size_t vertexStride = gp->getVertexStride();
|
||||||
|
|
||||||
|
SkASSERT(canTweakAlphaForCoverage ?
|
||||||
|
vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
|
||||||
|
vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
|
||||||
|
|
||||||
|
GrAAConvexTessellator tess;
|
||||||
|
|
||||||
|
int instanceCount = fGeoData.count();
|
||||||
|
|
||||||
|
int vertexCount = 0;
|
||||||
|
int indexCount = 0;
|
||||||
|
int maxVertices = DEFAULT_BUFFER_SIZE;
|
||||||
|
int maxIndices = DEFAULT_BUFFER_SIZE;
|
||||||
|
uint8_t* vertices = (uint8_t*) malloc(maxVertices * vertexStride);
|
||||||
|
uint16_t* indices = (uint16_t*) malloc(maxIndices * sizeof(uint16_t));
|
||||||
|
for (int i = 0; i < instanceCount; i++) {
|
||||||
|
tess.rewind();
|
||||||
|
|
||||||
|
Geometry& args = fGeoData[i];
|
||||||
|
|
||||||
|
if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentIndices = tess.numIndices();
|
||||||
|
SkASSERT(currentIndices <= UINT16_MAX);
|
||||||
|
if (indexCount + currentIndices > UINT16_MAX) {
|
||||||
|
// if we added the current instance, we would overflow the indices we can store in a
|
||||||
|
// uint16_t. Draw what we've got so far and reset.
|
||||||
|
draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount,
|
||||||
|
indices);
|
||||||
|
vertexCount = 0;
|
||||||
|
indexCount = 0;
|
||||||
|
}
|
||||||
|
int currentVertices = tess.numPts();
|
||||||
|
if (vertexCount + currentVertices > maxVertices) {
|
||||||
|
maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
|
||||||
|
vertices = (uint8_t*) realloc(vertices, maxVertices * vertexStride);
|
||||||
|
}
|
||||||
|
if (indexCount + currentIndices > maxIndices) {
|
||||||
|
maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
|
||||||
|
indices = (uint16_t*) realloc(indices, maxIndices * sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor,
|
||||||
|
vertexCount, indices + indexCount, canTweakAlphaForCoverage);
|
||||||
|
vertexCount += currentVertices;
|
||||||
|
indexCount += currentIndices;
|
||||||
|
}
|
||||||
|
draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount, indices);
|
||||||
|
free(vertices);
|
||||||
|
free(indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AAFlatteningConvexPathBatch(const Geometry& geometry) {
|
||||||
|
this->initClassID<AAFlatteningConvexPathBatch>();
|
||||||
|
fGeoData.push_back(geometry);
|
||||||
|
|
||||||
|
// compute bounds
|
||||||
|
fBounds = geometry.fPath.getBounds();
|
||||||
|
geometry.fViewMatrix.mapRect(&fBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onCombineIfPossible(GrBatch* t) override {
|
||||||
|
AAFlatteningConvexPathBatch* that = t->cast<AAFlatteningConvexPathBatch>();
|
||||||
|
|
||||||
|
SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
|
||||||
|
if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the event of two batches, one who can tweak, one who cannot, we just fall back to
|
||||||
|
// not tweaking
|
||||||
|
if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
|
||||||
|
fBatch.fCanTweakAlphaForCoverage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
|
||||||
|
this->joinBounds(that->bounds());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrColor color() const { return fBatch.fColor; }
|
||||||
|
bool linesOnly() const { return fBatch.fLinesOnly; }
|
||||||
|
bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
|
||||||
|
bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
|
||||||
|
const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
|
||||||
|
bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
|
||||||
|
|
||||||
|
struct BatchTracker {
|
||||||
|
GrColor fColor;
|
||||||
|
bool fUsesLocalCoords;
|
||||||
|
bool fColorIgnored;
|
||||||
|
bool fCoverageIgnored;
|
||||||
|
bool fLinesOnly;
|
||||||
|
bool fCanTweakAlphaForCoverage;
|
||||||
|
};
|
||||||
|
|
||||||
|
BatchTracker fBatch;
|
||||||
|
SkSTArray<1, Geometry, true> fGeoData;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GrAALinearizingConvexPathRenderer::onDrawPath(GrDrawTarget* target,
|
||||||
|
GrPipelineBuilder* pipelineBuilder,
|
||||||
|
GrColor color,
|
||||||
|
const SkMatrix& vm,
|
||||||
|
const SkPath& path,
|
||||||
|
const GrStrokeInfo&,
|
||||||
|
bool antiAlias) {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AAFlatteningConvexPathBatch::Geometry geometry;
|
||||||
|
geometry.fColor = color;
|
||||||
|
geometry.fViewMatrix = vm;
|
||||||
|
geometry.fPath = path;
|
||||||
|
|
||||||
|
SkAutoTUnref<GrBatch> batch(AAFlatteningConvexPathBatch::Create(geometry));
|
||||||
|
target->drawBatch(pipelineBuilder, batch);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifdef GR_TEST_UTILS
|
||||||
|
|
||||||
|
BATCH_TEST_DEFINE(AAFlatteningConvexPathBatch) {
|
||||||
|
AAFlatteningConvexPathBatch::Geometry geometry;
|
||||||
|
geometry.fColor = GrRandomColor(random);
|
||||||
|
geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
|
||||||
|
geometry.fPath = GrTest::TestPathConvex(random);
|
||||||
|
|
||||||
|
return AAFlatteningConvexPathBatch::Create(geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
35
src/gpu/GrAALinearizingConvexPathRenderer.h
Normal file
35
src/gpu/GrAALinearizingConvexPathRenderer.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GrAALinearizingConvexPathRenderer_DEFINED
|
||||||
|
#define GrAALinearizingConvexPathRenderer_DEFINED
|
||||||
|
|
||||||
|
#include "GrPathRenderer.h"
|
||||||
|
|
||||||
|
class GrAALinearizingConvexPathRenderer : public GrPathRenderer {
|
||||||
|
public:
|
||||||
|
GrAALinearizingConvexPathRenderer();
|
||||||
|
|
||||||
|
virtual bool canDrawPath(const GrDrawTarget*,
|
||||||
|
const GrPipelineBuilder*,
|
||||||
|
const SkMatrix& viewMatrix,
|
||||||
|
const SkPath&,
|
||||||
|
const GrStrokeInfo&,
|
||||||
|
bool antiAlias) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool onDrawPath(GrDrawTarget*,
|
||||||
|
GrPipelineBuilder*,
|
||||||
|
GrColor,
|
||||||
|
const SkMatrix& viewMatrix,
|
||||||
|
const SkPath&,
|
||||||
|
const GrStrokeInfo&,
|
||||||
|
bool antiAlias) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user