Add clipping for perspective triangles
more UI for halfplanes in SampleClip bug: skia:9698 Change-Id: I9463fe9860fa482ef05fc2113114e61524c38fc0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/260500 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
3f1a98b779
commit
190b82d67c
@ -1313,6 +1313,11 @@ public:
|
||||
*/
|
||||
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const;
|
||||
|
||||
/**
|
||||
* Returns homogeneous points, starting with 2D src points (with implied w = 1).
|
||||
*/
|
||||
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const;
|
||||
|
||||
/** Maps SkPoint (x, y) to result. SkPoint is mapped by multiplying by SkMatrix. Given:
|
||||
|
||||
| A B C | | x |
|
||||
|
@ -78,17 +78,13 @@ struct SK_API SkPoint3 {
|
||||
a and b (i.e., a - b)
|
||||
*/
|
||||
friend SkPoint3 operator-(const SkPoint3& a, const SkPoint3& b) {
|
||||
SkPoint3 v;
|
||||
v.set(a.fX - b.fX, a.fY - b.fY, a.fZ - b.fZ);
|
||||
return v;
|
||||
return { a.fX - b.fX, a.fY - b.fY, a.fZ - b.fZ };
|
||||
}
|
||||
|
||||
/** Returns a new point whose coordinates are the sum of a and b (a + b)
|
||||
*/
|
||||
friend SkPoint3 operator+(const SkPoint3& a, const SkPoint3& b) {
|
||||
SkPoint3 v;
|
||||
v.set(a.fX + b.fX, a.fY + b.fY, a.fZ + b.fZ);
|
||||
return v;
|
||||
return { a.fX + b.fX, a.fY + b.fY, a.fZ + b.fZ };
|
||||
}
|
||||
|
||||
/** Add v's coordinates to the point's
|
||||
@ -107,6 +103,10 @@ struct SK_API SkPoint3 {
|
||||
fZ -= v.fZ;
|
||||
}
|
||||
|
||||
friend SkPoint3 operator*(SkScalar t, SkPoint3 p) {
|
||||
return { t * p.fX, t * p.fY, t * p.fZ };
|
||||
}
|
||||
|
||||
/** Returns true if fX, fY, and fZ are measurable values.
|
||||
|
||||
@return true for values other than infinities and NaN
|
||||
|
@ -584,7 +584,13 @@ DEF_SAMPLE( return new HalfPlaneView3(); )
|
||||
|
||||
class HalfPlaneCoons : public SampleCameraView {
|
||||
SkPoint fPatch[12];
|
||||
SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
|
||||
SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
|
||||
SkPoint fTex[4] = {{0, 0}, {256, 0}, {256, 256}, {0, 256}};
|
||||
sk_sp<SkShader> fShader;
|
||||
|
||||
bool fShowHandles = false;
|
||||
bool fShowSkeleton = false;
|
||||
bool fShowTex = false;
|
||||
|
||||
SkString name() override { return SkString("halfplane-coons"); }
|
||||
|
||||
@ -601,6 +607,8 @@ class HalfPlaneCoons : public SampleCameraView {
|
||||
fPatch[9] = { 0, 300 };
|
||||
fPatch[10] = { 0, 200 };
|
||||
fPatch[11] = { 0, 100 };
|
||||
|
||||
fShader = GetResourceAsImage("images/mandrill_256.png")->makeShader();
|
||||
}
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
@ -610,8 +618,64 @@ class HalfPlaneCoons : public SampleCameraView {
|
||||
|
||||
canvas->save();
|
||||
canvas->concat(mx);
|
||||
canvas->drawPatch(fPatch, fColors, nullptr, SkBlendMode::kSrc, paint);
|
||||
|
||||
const SkPoint* tex = nullptr;
|
||||
const SkColor* col = nullptr;
|
||||
if (!fShowSkeleton) {
|
||||
if (fShowTex) {
|
||||
paint.setShader(fShader);
|
||||
tex = fTex;
|
||||
} else {
|
||||
col = fColors;
|
||||
}
|
||||
}
|
||||
canvas->drawPatch(fPatch, col, tex, SkBlendMode::kSrc, paint);
|
||||
paint.setShader(nullptr);
|
||||
|
||||
if (fShowHandles) {
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeCap(SkPaint::kRound_Cap);
|
||||
paint.setStrokeWidth(8);
|
||||
canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
|
||||
paint.setColor(SK_ColorWHITE);
|
||||
paint.setStrokeWidth(6);
|
||||
canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
|
||||
auto dist = [](SkPoint a, SkPoint b) { return (b - a).length(); };
|
||||
|
||||
const float tol = 15;
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
if (dist({x,y}, fPatch[i]) <= tol) {
|
||||
Click* c = new Click;
|
||||
c->fMeta.setS32("index", i);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool onClick(Click* click) override {
|
||||
int32_t index;
|
||||
SkAssertResult(click->fMeta.findS32("index", &index));
|
||||
SkASSERT(index >= 0 && index < 12);
|
||||
fPatch[index] = click->fCurr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onChar(SkUnichar uni) override {
|
||||
switch (uni) {
|
||||
case 'h': fShowHandles = !fShowHandles; return true;
|
||||
case 's': fShowSkeleton = !fShowSkeleton; return true;
|
||||
case 't': fShowTex = !fShowTex; return true;
|
||||
default: break;
|
||||
}
|
||||
return this->SampleCameraView::onChar(uni);
|
||||
}
|
||||
|
||||
};
|
||||
DEF_SAMPLE( return new HalfPlaneCoons(); )
|
||||
|
@ -19,6 +19,62 @@
|
||||
#include "src/shaders/SkComposeShader.h"
|
||||
#include "src/shaders/SkShaderBase.h"
|
||||
|
||||
// Compute the crossing point (across zero) for the two values, expressed as a
|
||||
// normalized 0...1 value. If curr is 0, returns 0. If next is 0, returns 1.
|
||||
//
|
||||
static float compute_t(float curr, float next) {
|
||||
SkASSERT((curr > 0 && next <= 0) || (curr <= 0 && next > 0));
|
||||
float t = curr / (curr - next);
|
||||
SkASSERT(t >= 0 && t <= 1);
|
||||
return t;
|
||||
}
|
||||
|
||||
static SkPoint3 lerp(SkPoint3 curr, SkPoint3 next, float t) {
|
||||
return curr + t * (next - curr);
|
||||
}
|
||||
|
||||
// tol is the nudge away from zero, to keep the numerics nice.
|
||||
// Think of it as our near-clipping-plane (or w-plane).
|
||||
static SkPoint3 clip(SkPoint3 curr, SkPoint3 next, float tol) {
|
||||
// Return the point between curr and next where the fZ value corses tol.
|
||||
// To be (really) perspective correct, we should be computing baesd on 1/Z, not Z.
|
||||
// For now, this is close enough (and faster).
|
||||
return lerp(curr, next, compute_t(curr.fZ - tol, next.fZ - tol));
|
||||
}
|
||||
|
||||
constexpr int kMaxClippedTrianglePointCount = 4;
|
||||
// Clip a triangle (based on its homogeneous W values), and return the projected polygon.
|
||||
// Since we only clip against one "edge"/plane, the max number of points in the clipped
|
||||
// polygon is 4.
|
||||
static int clip_triangle(SkPoint dst[], const int idx[3], const SkPoint3 pts[]) {
|
||||
SkPoint3 outPoints[4];
|
||||
SkPoint3* outP = outPoints;
|
||||
const float tol = 0.05f;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
int curr = idx[i];
|
||||
int next = idx[(i + 1) % 3];
|
||||
if (pts[curr].fZ > tol) {
|
||||
*outP++ = pts[curr];
|
||||
if (pts[next].fZ <= tol) { // curr is IN, next is OUT
|
||||
*outP++ = clip(pts[curr], pts[next], tol);
|
||||
}
|
||||
} else {
|
||||
if (pts[next].fZ > tol) { // curr is OUT, next is IN
|
||||
*outP++ = clip(pts[curr], pts[next], tol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int count = outP - outPoints;
|
||||
SkASSERT(count == 0 || count == 3 || count == 4);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
float scale = 1.0f / outPoints[i].fZ;
|
||||
dst[i].set(outPoints[i].fX * scale, outPoints[i].fY * scale);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
struct Matrix43 {
|
||||
float fMat[12]; // column major
|
||||
|
||||
@ -260,10 +316,16 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
vertices = deformed;
|
||||
}
|
||||
|
||||
SkPoint* devVerts = outerAlloc.makeArray<SkPoint>(vertexCount);
|
||||
fMatrix->mapPoints(devVerts, vertices, vertexCount);
|
||||
SkPoint* devVerts = nullptr;
|
||||
SkPoint3* dev3 = nullptr;
|
||||
|
||||
if (fMatrix->hasPerspective()) {
|
||||
dev3 = outerAlloc.makeArray<SkPoint3>(vertexCount);
|
||||
fMatrix->mapHomogeneousPoints(dev3, vertices, vertexCount);
|
||||
} else {
|
||||
devVerts = outerAlloc.makeArray<SkPoint>(vertexCount);
|
||||
fMatrix->mapPoints(devVerts, vertices, vertexCount);
|
||||
|
||||
{
|
||||
SkRect bounds;
|
||||
// this also sets bounds to empty if we see a non-finite value
|
||||
bounds.setBounds(devVerts, vertexCount);
|
||||
@ -275,6 +337,7 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
VertState state(vertexCount, indices, indexCount);
|
||||
VertState::Proc vertProc = state.chooseProc(vmode);
|
||||
|
||||
// Draw hairlines to show the skeleton
|
||||
if (!(colors || textures)) {
|
||||
// no colors[] and no texture, stroke hairlines with paint's color.
|
||||
SkPaint p;
|
||||
@ -287,10 +350,26 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
|
||||
const SkRasterClip& clip = *fRC;
|
||||
while (vertProc(&state)) {
|
||||
SkPoint array[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
|
||||
};
|
||||
hairProc(array, 4, clip, blitter.get());
|
||||
if (dev3) {
|
||||
SkPoint tmp[kMaxClippedTrianglePointCount + 2];
|
||||
int idx[] = { state.f0, state.f1, state.f2 };
|
||||
if (int n = clip_triangle(tmp, idx, dev3)) {
|
||||
tmp[n] = tmp[0]; // close the poly
|
||||
if (n == 3) {
|
||||
n = 4;
|
||||
} else {
|
||||
SkASSERT(n == 4);
|
||||
tmp[5] = tmp[2]; // add diagonal
|
||||
n = 6;
|
||||
}
|
||||
hairProc(tmp, n, clip, blitter.get());
|
||||
}
|
||||
} else {
|
||||
SkPoint array[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
|
||||
};
|
||||
hairProc(array, 4, clip, blitter.get());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -310,6 +389,28 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
}
|
||||
}
|
||||
|
||||
auto handle_devVerts = [&](SkBlitter* blitter) {
|
||||
SkPoint tmp[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
|
||||
};
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
};
|
||||
|
||||
auto handle_dev3 = [&](SkBlitter* blitter) {
|
||||
SkPoint tmp[kMaxClippedTrianglePointCount];
|
||||
int idx[] = { state.f0, state.f1, state.f2 };
|
||||
if (int n = clip_triangle(tmp, idx, dev3)) {
|
||||
// TODO: SkScan::FillConvexPoly(tmp, n, ...);
|
||||
SkASSERT(n == 3 || n == 4);
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
if (n == 4) {
|
||||
tmp[1] = tmp[2];
|
||||
tmp[2] = tmp[3];
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SkPaint p(paint);
|
||||
p.setShader(sk_ref_sp(shader));
|
||||
|
||||
@ -320,10 +421,11 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
continue;
|
||||
}
|
||||
|
||||
SkPoint tmp[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
|
||||
};
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
if (dev3) {
|
||||
handle_dev3(blitter);
|
||||
} else {
|
||||
handle_devVerts(blitter);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -352,10 +454,11 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
continue;
|
||||
}
|
||||
|
||||
SkPoint tmp[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
|
||||
};
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
if (dev3) {
|
||||
handle_dev3(blitter);
|
||||
} else {
|
||||
handle_devVerts(blitter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// must rebuild pipeline for each triangle, to pass in the computed ctm
|
||||
@ -378,11 +481,12 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
ctm = &tmpCtm;
|
||||
}
|
||||
|
||||
SkPoint tmp[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
|
||||
};
|
||||
auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *ctm, &innerAlloc);
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
if (dev3) {
|
||||
handle_dev3(blitter);
|
||||
} else {
|
||||
handle_devVerts(blitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1080,6 +1080,30 @@ void SkMatrix::mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int co
|
||||
sizeof(SkPoint3), count);
|
||||
}
|
||||
|
||||
void SkMatrix::mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const {
|
||||
if (this->isIdentity()) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dst[i] = { src[i].fX, src[i].fY, 1 };
|
||||
}
|
||||
} else if (this->hasPerspective()) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dst[i] = {
|
||||
fMat[0] * src[i].fX + fMat[1] * src[i].fY + fMat[2],
|
||||
fMat[3] * src[i].fX + fMat[4] * src[i].fY + fMat[5],
|
||||
fMat[6] * src[i].fX + fMat[7] * src[i].fY + fMat[8],
|
||||
};
|
||||
}
|
||||
} else { // affine
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dst[i] = {
|
||||
fMat[0] * src[i].fX + fMat[1] * src[i].fY + fMat[2],
|
||||
fMat[3] * src[i].fX + fMat[4] * src[i].fY + fMat[5],
|
||||
1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
|
||||
|
@ -699,7 +699,7 @@ static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
|
||||
|
||||
// doesn't crash with null dst, src, count == 0
|
||||
{
|
||||
mats[0].mapHomogeneousPoints(nullptr, nullptr, 0);
|
||||
mats[0].mapHomogeneousPoints(nullptr, (const SkPoint3*)nullptr, 0);
|
||||
}
|
||||
|
||||
// uniform scale of point
|
||||
|
Loading…
Reference in New Issue
Block a user