Add convex path renderer (disabled)

Review URL: http://codereview.appspot.com/5533061/



git-svn-id: http://skia.googlecode.com/svn/trunk@3040 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2012-01-17 14:25:10 +00:00
Родитель 0eb7576c65
Коммит 69cc6ad20e
12 изменённых файлов: 714 добавлений и 94 удалений

136
gm/convexpaths.cpp Normal file
Просмотреть файл

@ -0,0 +1,136 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.h"
#include "SkRandom.h"
#include "SkTArray.h"
namespace skiagm {
class ConvexPathsGM : public GM {
public:
ConvexPathsGM() {
this->setBGColor(0xFFDDDDDD);
this->makePaths();
}
protected:
virtual SkString onShortName() {
return SkString("convexpaths");
}
virtual SkISize onISize() {
return make_isize(1200, 900);
}
void makePaths() {
fPaths.push_back().addRect(0, 0,
100 * SK_Scalar1, 100 * SK_Scalar1,
SkPath::kCW_Direction);
fPaths.push_back().addRect(0, 0,
100 * SK_Scalar1, 100 * SK_Scalar1,
SkPath::kCCW_Direction);
fPaths.push_back().addCircle(50 * SK_Scalar1, 50 * SK_Scalar1,
50 * SK_Scalar1, SkPath::kCW_Direction);
fPaths.push_back().addCircle(50 * SK_Scalar1, 50 * SK_Scalar1,
40 * SK_Scalar1, SkPath::kCCW_Direction);
fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
50 * SK_Scalar1,
100 * SK_Scalar1),
SkPath::kCW_Direction);
fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
100 * SK_Scalar1,
50 * SK_Scalar1),
SkPath::kCCW_Direction);
fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
100 * SK_Scalar1,
5 * SK_Scalar1),
SkPath::kCCW_Direction);
fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
SK_Scalar1,
100 * SK_Scalar1),
SkPath::kCCW_Direction);
fPaths.push_back().addRect(0, 0,
100 * SK_Scalar1, 100 * SK_Scalar1,
SkPath::kCCW_Direction);
fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
SK_Scalar1 * 100,
SK_Scalar1 * 100),
40 * SK_Scalar1, 20 * SK_Scalar1,
SkPath::kCW_Direction);
fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
SK_Scalar1 * 100,
SK_Scalar1 * 100),
20 * SK_Scalar1, 40 * SK_Scalar1,
SkPath::kCCW_Direction);
/*
fPaths.push_back().arcTo(SkRect::MakeXYWH(0, 0,
50 * SK_Scalar1,
100 * SK_Scalar1),
25 * SK_Scalar1, 130 * SK_Scalar1, false);
*/
// point degenerate
fPaths.push_back().lineTo(0,0);
fPaths.push_back().quadTo(0,0,0,0);
fPaths.push_back().cubicTo(0,0,0,0,0,0);
// line degenerate
fPaths.push_back().lineTo(100 * SK_Scalar1, 100 * SK_Scalar1);
fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1, 0, 0);
fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1,
50 * SK_Scalar1, 50 * SK_Scalar1);
fPaths.push_back().quadTo(50 * SK_Scalar1, 50 * SK_Scalar1,
100 * SK_Scalar1, 100 * SK_Scalar1);
fPaths.push_back().cubicTo(0, 0,
0, 0,
100 * SK_Scalar1, 100 * SK_Scalar1);
}
virtual void onDraw(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
SkRandom rand;
canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
for (int i = 0 ; i < fPaths.count(); ++i) {
canvas->save();
// position the path, and make it at off-integer coords.
canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 4,
SK_Scalar1 * 200 * (i / 5) + 3 * SK_Scalar1 / 4);
SkColor color = rand.nextU();
color |= 0xff000000;
paint.setColor(color);
SkASSERT(fPaths[i].isConvex());
canvas->drawPath(fPaths[i], paint);
canvas->restore();
}
}
private:
typedef GM INHERITED;
SkTArray<SkPath> fPaths;
};
//////////////////////////////////////////////////////////////////////////////
static GM* MyFactory(void*) { return new ConvexPathsGM; }
static GMRegistry reg(MyFactory);
}

Просмотреть файл

@ -11,6 +11,7 @@
'../gm/colormatrix.cpp', '../gm/colormatrix.cpp',
'../gm/complexclip.cpp', '../gm/complexclip.cpp',
'../gm/complexclip2.cpp', '../gm/complexclip2.cpp',
'../gm/convexpaths.cpp',
'../gm/cubicpaths.cpp', '../gm/cubicpaths.cpp',
'../gm/degeneratesegments.cpp', '../gm/degeneratesegments.cpp',
'../gm/drawbitmaprect.cpp', '../gm/drawbitmaprect.cpp',

Просмотреть файл

@ -186,7 +186,9 @@
'../src/gpu/GrAAHairLinePathRenderer.cpp', '../src/gpu/GrAAHairLinePathRenderer.cpp',
'../src/gpu/GrAAHairLinePathRenderer.h', '../src/gpu/GrAAHairLinePathRenderer.h',
'../src/gpu/GrAddPathRenderers_aahairline.cpp', '../src/gpu/GrAAConvexPathRenderer.cpp',
'../src/gpu/GrAAConvexPathRenderer.h',
'../src/gpu/GrAddPathRenderers_default.cpp',
'../src/gpu/GrAllocator.h', '../src/gpu/GrAllocator.h',
'../src/gpu/GrAllocPool.h', '../src/gpu/GrAllocPool.h',
'../src/gpu/GrAllocPool.cpp', '../src/gpu/GrAllocPool.cpp',

Просмотреть файл

@ -0,0 +1,401 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrAAConvexPathRenderer.h"
#include "GrContext.h"
#include "GrDrawState.h"
#include "GrPathUtils.h"
#include "SkString.h"
#include "SkTrace.h"
GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
}
bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget::Caps& targetCaps,
const SkPath& path,
GrPathFill fill,
bool antiAlias) const {
return targetCaps.fShaderDerivativeSupport && antiAlias &&
kHairLine_PathFill != fill && !GrIsFillInverted(fill) &&
path.isConvex();
}
namespace {
struct Segment {
enum {
kLine,
kQuad
} fType;
// line uses a, quad uses a and b (first point comes from prev. segment)
GrPoint fA, fB;
// normal to edge ending at a and b
GrVec fANorm, fBNorm;
// mid vector at a that splits angle with previous edge
GrVec fPrevMid;
};
typedef SkTArray<Segment, true> SegmentArray;
bool is_path_degenerate(const GrPath& path) {
int n = path.countPoints();
if (n < 3) {
return true;
}
// compute a line from the first two points that are not equal, look for
// a third pt that is off the line.
static const SkScalar TOL = (SK_Scalar1 / 16);
bool foundLine = false;
GrPoint firstPoint = path.getPoint(0);
GrVec lineV;
SkScalar lineC;
int i = 1;
do {
GrPoint pt = path.getPoint(i);
if (!foundLine) {
if (pt != firstPoint) {
lineV = pt - firstPoint;
lineV.normalize();
lineV.setOrthog(lineV);
lineC = lineV.dot(firstPoint);
foundLine = true;
}
} else {
if (SkScalarAbs(lineV.dot(pt) - lineC) > TOL) {
return false;
}
}
++i;
} while (i < n);
return true;
}
bool get_segments(const GrPath& path,
SegmentArray* segments,
int* quadCnt,
int* lineCnt) {
*quadCnt = 0;
*lineCnt = 0;
SkPath::Iter iter(path, true);
// This renderer overemphasis very thin paths (every pixel intersected by
// the path will be at least 1/2 on). When the path degenerates to a line
// this makes the path draw as a hairline. This is a pretty glaring error
// so we detect this case and will not draw.
if (is_path_degenerate(path)) {
return false;
}
for (;;) {
GrPoint pts[4];
GrPathCmd cmd = (GrPathCmd)iter.next(pts);
switch (cmd) {
case kLine_PathCmd: {
segments->push_back();
segments->back().fType = Segment::kLine;
segments->back().fA = pts[1];
++(*lineCnt);
break;
}
case kQuadratic_PathCmd:
segments->push_back();
segments->back().fType = Segment::kQuad;
segments->back().fA = pts[1];
segments->back().fB = pts[2];
++(*quadCnt);
break;
case kCubic_PathCmd: {
SkSTArray<15, SkPoint, true> quads;
GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
int count = quads.count();
for (int q = 0; q < count; q += 3) {
segments->push_back();
segments->back().fType = Segment::kQuad;
segments->back().fA = quads[q + 1];
segments->back().fB = quads[q + 2];
++(*quadCnt);
}
break;
};
case kEnd_PathCmd:
GrAssert(*quadCnt + *lineCnt == segments->count());
return true;
default:
break;
}
}
}
struct QuadVertex {
GrPoint fPos;
union {
GrPoint fQuadUV;
GrScalar fEdge[4];
};
};
void get_counts(int quadCount, int lineCount, int* vCount, int* iCount) {
*vCount = 9 * lineCount + 11 * quadCount;
*iCount = 15 * lineCount + 24 * quadCount;
}
// for visual debugging, exagerate the AA smear at the edges
// requires modifying the distance calc in the shader actually shade differently
//#define STRETCH_AA
#define STRETCH_FACTOR (20 * SK_Scalar1)
void create_vertices(SegmentArray* segments,
const GrPoint& fanPt,
QuadVertex* verts,
uint16_t* idxs) {
int count = segments->count();
GrAssert(count > 1);
int prevS = count - 1;
const Segment& lastSeg = (*segments)[prevS];
// walk the segments and compute normals to each edge and
// bisectors at vertices. The loop relies on having the end point and normal
// from previous segment so we first compute that. Also, we determine
// whether normals point left or right to face outside the path.
GrVec prevPt;
GrPoint prevPrevPt;
GrVec prevNorm;
if (Segment::kLine == lastSeg.fType) {
prevPt = lastSeg.fA;
const Segment& secondLastSeg = (*segments)[prevS - 1];
prevPrevPt = (Segment::kLine == secondLastSeg.fType) ?
secondLastSeg.fA :
secondLastSeg.fB;
} else {
prevPt = lastSeg.fB;
prevPrevPt = lastSeg.fA;
}
GrVec::Side outside;
// we will compute our edge vectors so that they are pointing along the
// direction in which we are iterating the path. So here we take an opposite
// vector and get the side that the fan pt lies relative to it.
fanPt.distanceToLineBetweenSqd(prevPrevPt, prevPt, &outside);
prevNorm = prevPt - prevPrevPt;
prevNorm.normalize();
prevNorm.setOrthog(prevNorm, outside);
#ifdef STRETCH_AA
prevNorm.scale(STRETCH_FACTOR);
#endif
// compute the normals and bisectors
for (int s = 0; s < count; ++s, ++prevS) {
Segment& curr = (*segments)[s];
GrVec currVec = curr.fA - prevPt;
currVec.normalize();
curr.fANorm.setOrthog(currVec, outside);
#ifdef STRETCH_AA
curr.fANorm.scale(STRETCH_FACTOR);
#endif
curr.fPrevMid = prevNorm + curr.fANorm;
curr.fPrevMid.normalize();
#ifdef STRETCH_AA
curr.fPrevMid.scale(STRETCH_FACTOR);
#endif
if (Segment::kLine == curr.fType) {
prevPt = curr.fA;
prevNorm = curr.fANorm;
} else {
currVec = curr.fB - curr.fA;
currVec.normalize();
curr.fBNorm.setOrthog(currVec, outside);
#ifdef STRETCH_AA
curr.fBNorm.scale(STRETCH_FACTOR);
#endif
prevPt = curr.fB;
prevNorm = curr.fBNorm;
}
}
// compute the vertices / indices
if (Segment::kLine == lastSeg.fType) {
prevPt = lastSeg.fA;
prevNorm = lastSeg.fANorm;
} else {
prevPt = lastSeg.fB;
prevNorm = lastSeg.fBNorm;
}
int v = 0;
int i = 0;
for (int s = 0; s < count; ++s, ++prevS) {
Segment& curr = (*segments)[s];
verts[v + 0].fPos = prevPt;
verts[v + 1].fPos = prevPt + prevNorm;
verts[v + 2].fPos = prevPt + curr.fPrevMid;
verts[v + 3].fPos = prevPt + curr.fANorm;
verts[v + 0].fQuadUV.set(0, 0);
verts[v + 1].fQuadUV.set(0, -SK_Scalar1);
verts[v + 2].fQuadUV.set(0, -SK_Scalar1);
verts[v + 3].fQuadUV.set(0, -SK_Scalar1);
idxs[i + 0] = v + 0;
idxs[i + 1] = v + 1;
idxs[i + 2] = v + 2;
idxs[i + 3] = v + 0;
idxs[i + 4] = v + 2;
idxs[i + 5] = v + 3;
v += 4;
i += 6;
if (Segment::kLine == curr.fType) {
verts[v + 0].fPos = fanPt;
verts[v + 1].fPos = prevPt;
verts[v + 2].fPos = curr.fA;
verts[v + 3].fPos = prevPt + curr.fANorm;
verts[v + 4].fPos = curr.fA + curr.fANorm;
GrScalar lineC = -curr.fANorm.dot(curr.fA);
GrScalar fanDist = curr.fANorm.dot(fanPt) - lineC;
verts[v + 0].fQuadUV.set(0, SkScalarAbs(fanDist));
verts[v + 1].fQuadUV.set(0, 0);
verts[v + 2].fQuadUV.set(0, 0);
verts[v + 3].fQuadUV.set(0, -GR_Scalar1);
verts[v + 4].fQuadUV.set(0, -GR_Scalar1);
idxs[i + 0] = v + 0;
idxs[i + 1] = v + 1;
idxs[i + 2] = v + 2;
idxs[i + 3] = v + 1;
idxs[i + 4] = v + 3;
idxs[i + 5] = v + 4;
idxs[i + 6] = v + 1;
idxs[i + 7] = v + 4;
idxs[i + 8] = v + 2;
i += 9;
v += 5;
prevPt = curr.fA;
prevNorm = curr.fANorm;
} else {
GrVec splitVec = curr.fANorm + curr.fBNorm;
splitVec.normalize();
#ifdef STRETCH_AA
splitVec.scale(STRETCH_FACTOR);
#endif
verts[v + 0].fPos = prevPt;
verts[v + 1].fPos = curr.fA;
verts[v + 2].fPos = curr.fB;
verts[v + 3].fPos = fanPt;
verts[v + 4].fPos = prevPt + curr.fANorm;
verts[v + 5].fPos = curr.fA + splitVec;
verts[v + 6].fPos = curr.fB + curr.fBNorm;
verts[v + 0].fQuadUV.set(0, 0);
verts[v + 1].fQuadUV.set(GR_ScalarHalf, 0);
verts[v + 2].fQuadUV.set(GR_Scalar1, GR_Scalar1);
GrMatrix toUV;
GrPoint pts[] = {prevPt, curr.fA, curr.fB};
GrPathUtils::quadDesignSpaceToUVCoordsMatrix(pts, &toUV);
toUV.mapPointsWithStride(&verts[v + 3].fQuadUV,
&verts[v + 3].fPos,
sizeof(QuadVertex), 4);
idxs[i + 0] = v + 3;
idxs[i + 1] = v + 0;
idxs[i + 2] = v + 1;
idxs[i + 3] = v + 3;
idxs[i + 4] = v + 1;
idxs[i + 5] = v + 2;
idxs[i + 6] = v + 0;
idxs[i + 7] = v + 4;
idxs[i + 8] = v + 1;
idxs[i + 9] = v + 4;
idxs[i + 10] = v + 1;
idxs[i + 11] = v + 5;
idxs[i + 12] = v + 5;
idxs[i + 13] = v + 1;
idxs[i + 14] = v + 2;
idxs[i + 15] = v + 5;
idxs[i + 16] = v + 2;
idxs[i + 17] = v + 6;
i += 18;
v += 7;
prevPt = curr.fB;
prevNorm = curr.fBNorm;
}
}
}
}
void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
GrAssert(fPath->isConvex());
if (fPath->isEmpty()) {
return;
}
GrDrawState* drawState = fTarget->drawState();
GrDrawTarget::AutoStateRestore asr;
GrMatrix vm = drawState->getViewMatrix();
vm.postTranslate(fTranslate.fX, fTranslate.fY);
asr.set(fTarget);
GrMatrix ivm;
if (vm.invert(&ivm)) {
drawState->preConcatSamplerMatrices(stageMask, ivm);
}
drawState->setViewMatrix(GrMatrix::I());
SkPath path;
fPath->transform(vm, &path);
SkPoint fanPt = {path.getBounds().centerX(),
path.getBounds().centerY()};
GrVertexLayout layout = 0;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if ((1 << s) & stageMask) {
layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
}
}
layout |= GrDrawTarget::kEdge_VertexLayoutBit;
QuadVertex *verts;
uint16_t* idxs;
int nQuads;
int nLines;
SegmentArray segments;
if (!get_segments(path, &segments, &nQuads, &nLines)) {
return;
}
int vCount;
int iCount;
get_counts(nQuads, nLines, &vCount, &iCount);
if (!fTarget->reserveVertexSpace(layout,
vCount,
reinterpret_cast<void**>(&verts))) {
return;
}
if (!fTarget->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
fTarget->resetVertexSource();
return;
}
create_vertices(&segments, fanPt, verts, idxs);
drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
fTarget->drawIndexed(kTriangles_PrimitiveType,
0, // start vertex
0, // start index
vCount,
iCount);
}

Просмотреть файл

@ -0,0 +1,20 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrPathRenderer.h"
class GrAAConvexPathRenderer : public GrPathRenderer {
public:
GrAAConvexPathRenderer();
bool canDrawPath(const GrDrawTarget::Caps& targetCaps,
const SkPath& path,
GrPathFill fill,
bool antiAlias) const;
void drawPath(GrDrawState::StageMask stageMask);
};

Просмотреть файл

@ -140,65 +140,6 @@ typedef SkTArray<SkPoint, true> PtArray;
#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true> #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
typedef SkTArray<int, true> IntArray; typedef SkTArray<int, true> IntArray;
/**
* We convert cubics to quadratics (for now).
*/
void convert_noninflect_cubic_to_quads(const SkPoint p[4],
SkScalar tolScale,
PtArray* quads,
int sublevel = 0) {
SkVector ab = p[1];
ab -= p[0];
SkVector dc = p[2];
dc -= p[3];
static const SkScalar gLengthScale = 3 * SK_Scalar1 / 2;
// base tolerance is 2 pixels in dev coords.
const SkScalar distanceSqdTol = SkScalarMul(tolScale, 2 * SK_Scalar1);
static const int kMaxSubdivs = 10;
ab.scale(gLengthScale);
dc.scale(gLengthScale);
SkVector c0 = p[0];
c0 += ab;
SkVector c1 = p[3];
c1 += dc;
SkScalar dSqd = c0.distanceToSqd(c1);
if (sublevel > kMaxSubdivs || dSqd <= distanceSqdTol) {
SkPoint cAvg = c0;
cAvg += c1;
cAvg.scale(SK_ScalarHalf);
SkPoint* pts = quads->push_back_n(3);
pts[0] = p[0];
pts[1] = cAvg;
pts[2] = p[3];
return;
} else {
SkPoint choppedPts[7];
SkChopCubicAtHalf(p, choppedPts);
convert_noninflect_cubic_to_quads(choppedPts + 0, tolScale,
quads, sublevel + 1);
convert_noninflect_cubic_to_quads(choppedPts + 3, tolScale,
quads, sublevel + 1);
}
}
void convert_cubic_to_quads(const SkPoint p[4],
SkScalar tolScale,
PtArray* quads) {
SkPoint chopped[13];
int count = SkChopCubicAtInflections(p, chopped);
for (int i = 0; i < count; ++i) {
SkPoint* cubic = chopped + 3*i;
convert_noninflect_cubic_to_quads(cubic, tolScale, quads);
}
}
// Takes 178th time of logf on Z600 / VC2010 // Takes 178th time of logf on Z600 / VC2010
int get_float_exp(float x) { int get_float_exp(float x) {
GR_STATIC_ASSERT(sizeof(int) == sizeof(float)); GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
@ -350,14 +291,15 @@ int generate_lines_and_quads(const SkPath& path,
bounds.roundOut(&ibounds); bounds.roundOut(&ibounds);
if (SkIRect::Intersects(clip, ibounds)) { if (SkIRect::Intersects(clip, ibounds)) {
PREALLOC_PTARRAY(32) q; PREALLOC_PTARRAY(32) q;
// in perspective have to do conversion in src space // We convert cubics to quadratics (for now).
// In perspective have to do conversion in src space.
if (persp) { if (persp) {
SkScalar tolScale = SkScalar tolScale =
GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m, GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
path.getBounds()); path.getBounds());
convert_cubic_to_quads(pts, tolScale, &q); GrPathUtils::convertCubicToQuads(pts, tolScale, &q);
} else { } else {
convert_cubic_to_quads(devPts, SK_Scalar1, &q); GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, &q);
} }
for (int i = 0; i < q.count(); i += 3) { for (int i = 0; i < q.count(); i += 3) {
SkPoint* qInDevSpace; SkPoint* qInDevSpace;
@ -447,24 +389,9 @@ void bloat_quad(const SkPoint qpts[3], const GrMatrix* toDevice,
SkPoint b = qpts[1]; SkPoint b = qpts[1];
SkPoint c = qpts[2]; SkPoint c = qpts[2];
// compute a matrix that goes from device coords to U,V quad params
// this should be in the src space, not dev coords, when we have perspective // this should be in the src space, not dev coords, when we have perspective
SkMatrix DevToUV; SkMatrix DevToUV;
DevToUV.setAll(a.fX, b.fX, c.fX, GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &DevToUV);
a.fY, b.fY, c.fY,
SK_Scalar1, SK_Scalar1, SK_Scalar1);
DevToUV.invert(&DevToUV);
// can't make this static, no cons :(
SkMatrix UVpts;
UVpts.setAll(0, SK_ScalarHalf, SK_Scalar1,
0, 0, SK_Scalar1,
SK_Scalar1, SK_Scalar1, SK_Scalar1);
DevToUV.postConcat(UVpts);
// We really want to avoid perspective matrix muls.
// These may wind up really close to zero
DevToUV.setPerspX(0);
DevToUV.setPerspY(0);
if (toDevice) { if (toDevice) {
toDevice->mapPoints(&a, 1); toDevice->mapPoints(&a, 1);

Просмотреть файл

@ -1,20 +1,26 @@
/* /*
* Copyright 2011 Google Inc. * Copyright 2012 Google Inc.
* *
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#include "GrAAHairLinePathRenderer.h" #include "GrAAHairLinePathRenderer.h"
#include "GrAAConvexPathRenderer.h"
void GrPathRenderer::AddPathRenderers(GrContext* ctx, void GrPathRenderer::AddPathRenderers(GrContext* ctx,
GrPathRendererChain::UsageFlags flags, GrPathRendererChain::UsageFlags flags,
GrPathRendererChain* chain) { GrPathRendererChain* chain) {
if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) { if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) {
if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) { if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
chain->addPathRenderer(pr)->unref(); chain->addPathRenderer(pr)->unref();
} }
// Disabled for now. Need to fix issue where some hairlines don't
// wind up going to the hairline renderer and get rendered by this
// PR looking speckly.
//chain->addPathRenderer(new GrAAConvexPathRenderer())->unref();
} }
} }

Просмотреть файл

@ -489,9 +489,15 @@ struct GrDrawState {
/* 1-pixel wide line /* 1-pixel wide line
2D implicit line eq (a*x + b*y +c = 0). 4th component unused */ 2D implicit line eq (a*x + b*y +c = 0). 4th component unused */
kHairLine_EdgeType, kHairLine_EdgeType,
/* 1-pixel wide quadratic /* Quadratic specified by u^2-v canonical coords (only 2
u^2-v canonical coords (only 2 components used) */ components used). Coverage based on signed distance with negative
kHairQuad_EdgeType being inside, positive outside.*/
kQuad_EdgeType,
/* Same as above but for hairline quadratics. Uses unsigned distance.
Coverage is min(0, 1-distance). */
kHairQuad_EdgeType,
kVertexEdgeTypeCnt
}; };
/** /**
@ -500,6 +506,7 @@ struct GrDrawState {
* are not specified the value of this setting has no effect. * are not specified the value of this setting has no effect.
*/ */
void setVertexEdgeType(VertexEdgeType type) { void setVertexEdgeType(VertexEdgeType type) {
GrAssert(type >=0 && type < kVertexEdgeTypeCnt);
fVertexEdgeType = type; fVertexEdgeType = type;
} }

Просмотреть файл

@ -526,21 +526,28 @@ void GrGLProgram::genEdgeCoverage(const GrGLInterface* gl,
segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName); segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) { if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName); segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
} else { } else {
GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType); GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType ||
GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType);
// for now we know we're not in perspective, so we could compute this // for now we know we're not in perspective, so we could compute this
// per-quadratic rather than per pixel // per-quadratic rather than per pixel
segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName); segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName); segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tfloat dfdx = 2.0*%s.x*duvdx.x - duvdx.y;\n", fsName); segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
segments->fFSCode.appendf("\tfloat dfdy = 2.0*%s.x*duvdy.x - duvdy.y;\n", fsName); "\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
fsName, fsName);
segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName); segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / (dfdx*dfdx + dfdy*dfdy));\n"); if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
segments->fFSCode.append("\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n");
} else {
segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
}
if (gl->supportsES2()) { if (gl->supportsES2()) {
segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n"); segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
} }
} }
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
*coverageVar = "edgeAlpha"; *coverageVar = "edgeAlpha";
} else { } else {
coverageVar->reset(); coverageVar->reset();

Просмотреть файл

@ -219,9 +219,7 @@ bool GrGpuGLShaders::programUnitTest() {
if (vertexEdgeAA) { if (vertexEdgeAA) {
pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit; pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
if (this->getCaps().fShaderDerivativeSupport) { if (this->getCaps().fShaderDerivativeSupport) {
pdesc.fVertexEdgeType = random_bool(&random) ? pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
GrDrawState::kHairQuad_EdgeType :
GrDrawState::kHairLine_EdgeType;
} else { } else {
pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType; pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
} }

Просмотреть файл

@ -8,8 +8,8 @@
#include "GrPathUtils.h" #include "GrPathUtils.h"
#include "GrPoint.h" #include "GrPoint.h"
#include "SkGeometry.h"
GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol, GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
const GrMatrix& viewM, const GrMatrix& viewM,
@ -186,3 +186,107 @@ int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
} }
return pointCount; return pointCount;
} }
namespace {
// The matrix computed for quadDesignSpaceToUVCoordsMatrix should never really
// have perspective and we really want to avoid perspective matrix muls.
// However, the first two entries of the perspective row may be really close to
// 0 and the third may not be 1 due to a scale on the entire matrix.
inline void fixup_matrix(GrMatrix* mat) {
GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
static const GrScalar gTOL = 1.f / 100.f;
GrAssert(GrScalarAbs(mat->get(SkMatrix::kMPersp0)) < gTOL);
GrAssert(GrScalarAbs(mat->get(SkMatrix::kMPersp1)) < gTOL);
float m33 = mat->get(SkMatrix::kMPersp2);
if (1.f != m33) {
m33 = 1.f / m33;
mat->setAll(m33 * mat->get(SkMatrix::kMScaleX),
m33 * mat->get(SkMatrix::kMSkewX),
m33 * mat->get(SkMatrix::kMTransX),
m33 * mat->get(SkMatrix::kMSkewY),
m33 * mat->get(SkMatrix::kMScaleY),
m33 * mat->get(SkMatrix::kMTransY),
0.f, 0.f, 1.f);
} else {
mat->setPerspX(0);
mat->setPerspY(0);
}
}
}
// Compute a matrix that goes from the 2d space coordinates to UV space where
// u^2-v = 0 specifies the quad.
void GrPathUtils::quadDesignSpaceToUVCoordsMatrix(const SkPoint qPts[3],
GrMatrix* matrix) {
// can't make this static, no cons :(
SkMatrix UVpts;
GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
UVpts.setAll(0, 0.5f, 1.f,
0, 0, 1.f,
1.f, 1.f, 1.f);
matrix->setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX,
qPts[0].fY, qPts[1].fY, qPts[2].fY,
1.f, 1.f, 1.f);
matrix->invert(matrix);
matrix->postConcat(UVpts);
fixup_matrix(matrix);
}
namespace {
void convert_noninflect_cubic_to_quads(const SkPoint p[4],
SkScalar tolScale,
SkTArray<SkPoint, true>* quads,
int sublevel = 0) {
SkVector ab = p[1];
ab -= p[0];
SkVector dc = p[2];
dc -= p[3];
static const SkScalar gLengthScale = 3 * SK_Scalar1 / 2;
// base tolerance is 2 pixels in dev coords.
const SkScalar distanceSqdTol = SkScalarMul(tolScale, 1 * SK_Scalar1);
static const int kMaxSubdivs = 10;
ab.scale(gLengthScale);
dc.scale(gLengthScale);
SkVector c0 = p[0];
c0 += ab;
SkVector c1 = p[3];
c1 += dc;
SkScalar dSqd = c0.distanceToSqd(c1);
if (sublevel > kMaxSubdivs || dSqd <= distanceSqdTol) {
SkPoint cAvg = c0;
cAvg += c1;
cAvg.scale(SK_ScalarHalf);
SkPoint* pts = quads->push_back_n(3);
pts[0] = p[0];
pts[1] = cAvg;
pts[2] = p[3];
return;
} else {
SkPoint choppedPts[7];
SkChopCubicAtHalf(p, choppedPts);
convert_noninflect_cubic_to_quads(choppedPts + 0, tolScale,
quads, sublevel + 1);
convert_noninflect_cubic_to_quads(choppedPts + 3, tolScale,
quads, sublevel + 1);
}
}
}
void GrPathUtils::convertCubicToQuads(const GrPoint p[4],
SkScalar tolScale,
SkTArray<SkPoint, true>* quads) {
SkPoint chopped[10];
int count = SkChopCubicAtInflections(p, chopped);
for (int i = 0; i < count; ++i) {
SkPoint* cubic = chopped + 3*i;
convert_noninflect_cubic_to_quads(cubic, tolScale, quads);
}
}

Просмотреть файл

@ -12,6 +12,7 @@
#include "GrMatrix.h" #include "GrMatrix.h"
#include "GrPath.h" #include "GrPath.h"
#include "SkTArray.h"
/** /**
* Utilities for evaluating paths. * Utilities for evaluating paths.
@ -45,6 +46,16 @@ namespace GrPathUtils {
GrScalar tolSqd, GrScalar tolSqd,
GrPoint** points, GrPoint** points,
uint32_t pointsLeft); uint32_t pointsLeft);
// Compute a matrix that goes from the 2d space coordinates to UV space
// where u^2-v = 0 specifies the quad.
void quadDesignSpaceToUVCoordsMatrix(const GrPoint qPts[3],
GrMatrix* matrix);
// Converts a cubic into a sequence of quads. If working in device space
// use tolScale = 1, otherwise set based on stretchiness of the matrix. The
// result is sets of 3 points in quads (TODO: share endpoints in returned
// array)
void convertCubicToQuads(const GrPoint p[4],
SkScalar tolScale,
SkTArray<SkPoint, true>* quads);
}; };
#endif #endif