зеркало из https://github.com/mozilla/moz-skia.git
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:
Родитель
0eb7576c65
Коммит
69cc6ad20e
|
@ -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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче