2020-01-07 02:49:37 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "samplecode/Sample.h"
|
2020-11-24 20:43:11 +00:00
|
|
|
#include "src/core/SkGeometry.h"
|
2020-01-07 02:49:37 +00:00
|
|
|
#include "src/core/SkPathPriv.h"
|
|
|
|
#include "tools/ToolUtils.h"
|
|
|
|
|
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
|
2020-07-09 17:25:17 +00:00
|
|
|
#include "include/gpu/GrRecordingContext.h"
|
2020-01-07 02:49:37 +00:00
|
|
|
#include "src/gpu/GrClip.h"
|
|
|
|
#include "src/gpu/GrMemoryPool.h"
|
2020-07-09 17:25:17 +00:00
|
|
|
#include "src/gpu/GrRecordingContextPriv.h"
|
2020-01-07 02:49:37 +00:00
|
|
|
#include "src/gpu/GrRenderTargetContext.h"
|
2020-07-31 01:50:46 +00:00
|
|
|
#include "src/gpu/tessellate/GrPathTessellateOp.h"
|
2020-11-24 20:43:11 +00:00
|
|
|
#include "src/gpu/tessellate/GrWangsFormula.h"
|
2020-01-07 02:49:37 +00:00
|
|
|
|
2020-11-23 16:30:24 +00:00
|
|
|
static float kConicWeight = .5;
|
|
|
|
|
2020-01-07 02:49:37 +00:00
|
|
|
// This sample enables wireframe and visualizes the triangulation generated by
|
|
|
|
// GrTessellateWedgeShader.
|
2020-05-29 16:51:08 +00:00
|
|
|
class TessellatedWedge : public Sample {
|
2020-01-07 02:49:37 +00:00
|
|
|
public:
|
2020-05-29 16:51:08 +00:00
|
|
|
TessellatedWedge() {
|
2020-01-21 18:19:26 +00:00
|
|
|
#if 0
|
|
|
|
fPath.moveTo(1, 0);
|
|
|
|
int numSides = 32 * 3;
|
|
|
|
for (int i = 1; i < numSides; ++i) {
|
|
|
|
float theta = 2*3.1415926535897932384626433832785 * i / numSides;
|
|
|
|
fPath.lineTo(std::cos(theta), std::sin(theta));
|
|
|
|
}
|
2020-05-21 16:11:27 +00:00
|
|
|
fPath.transform(SkMatrix::Scale(200, 200));
|
|
|
|
fPath.transform(SkMatrix::Translate(300, 300));
|
2020-01-21 18:19:26 +00:00
|
|
|
#else
|
2020-11-23 16:30:24 +00:00
|
|
|
fPath.moveTo(100, 300);
|
|
|
|
fPath.conicTo(300, 100, 500, 300, kConicWeight);
|
|
|
|
fPath.cubicTo(433, 366, 366, 433, 300, 500);
|
2020-01-21 18:19:26 +00:00
|
|
|
#endif
|
2020-01-07 02:49:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void onDrawContent(SkCanvas*) override;
|
|
|
|
Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
|
|
|
|
bool onClick(Sample::Click*) override;
|
|
|
|
bool onChar(SkUnichar) override;
|
|
|
|
|
|
|
|
SkString name() override { return SkString("TessellatedWedge"); }
|
|
|
|
|
|
|
|
SkMatrix fLastViewMatrix = SkMatrix::I();
|
|
|
|
SkPath fPath;
|
2020-06-04 22:44:29 +00:00
|
|
|
GrTessellationPathRenderer::OpFlags fOpFlags = GrTessellationPathRenderer::OpFlags::kWireframe;
|
2020-01-07 02:49:37 +00:00
|
|
|
|
|
|
|
class Click;
|
|
|
|
};
|
|
|
|
|
2020-05-29 16:51:08 +00:00
|
|
|
void TessellatedWedge::onDrawContent(SkCanvas* canvas) {
|
2020-01-07 02:49:37 +00:00
|
|
|
canvas->clear(SK_ColorBLACK);
|
|
|
|
|
2020-07-09 17:25:17 +00:00
|
|
|
auto ctx = canvas->recordingContext();
|
2020-01-07 02:49:37 +00:00
|
|
|
GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
|
|
|
|
|
|
|
|
SkString error;
|
|
|
|
if (!rtc || !ctx) {
|
|
|
|
error = "GPU Only.";
|
2020-05-29 16:51:08 +00:00
|
|
|
} else if (!ctx->priv().caps()->drawInstancedSupport()) {
|
|
|
|
error = "Instanced rendering not supported.";
|
2020-01-07 02:49:37 +00:00
|
|
|
} else if (1 == rtc->numSamples() && !ctx->priv().caps()->mixedSamplesSupport()) {
|
|
|
|
error = "MSAA/mixed samples only.";
|
|
|
|
}
|
|
|
|
if (!error.isEmpty()) {
|
|
|
|
SkFont font(nullptr, 20);
|
|
|
|
SkPaint captionPaint;
|
|
|
|
captionPaint.setColor(SK_ColorWHITE);
|
|
|
|
canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
GrPaint paint;
|
|
|
|
paint.setColor4f({1,0,1,1});
|
|
|
|
|
|
|
|
GrAAType aa;
|
|
|
|
if (rtc->numSamples() > 1) {
|
|
|
|
aa = GrAAType::kMSAA;
|
|
|
|
} else if (rtc->asRenderTargetProxy()->canUseMixedSamples(*ctx->priv().caps())) {
|
|
|
|
aa = GrAAType::kCoverage;
|
|
|
|
} else {
|
|
|
|
aa = GrAAType::kNone;
|
|
|
|
}
|
|
|
|
|
2020-11-30 19:33:58 +00:00
|
|
|
rtc->addDrawOp(GrOp::Make<GrPathTessellateOp>(ctx, canvas->getTotalMatrix(), fPath,
|
|
|
|
std::move(paint), aa, fOpFlags));
|
2020-01-07 02:49:37 +00:00
|
|
|
|
|
|
|
// Draw the path points.
|
|
|
|
SkPaint pointsPaint;
|
|
|
|
pointsPaint.setColor(SK_ColorBLUE);
|
|
|
|
pointsPaint.setStrokeWidth(8);
|
2020-01-21 18:19:26 +00:00
|
|
|
SkPath devPath = fPath;
|
|
|
|
devPath.transform(canvas->getTotalMatrix());
|
|
|
|
{
|
|
|
|
SkAutoCanvasRestore acr(canvas, true);
|
|
|
|
canvas->setMatrix(SkMatrix::I());
|
|
|
|
canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
|
|
|
|
SkPathPriv::PointData(devPath), pointsPaint);
|
|
|
|
}
|
2020-01-07 02:49:37 +00:00
|
|
|
|
|
|
|
fLastViewMatrix = canvas->getTotalMatrix();
|
2020-11-23 16:30:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
SkString caption;
|
|
|
|
caption.printf("w=%f (=/- and +/_ to change)", kConicWeight);
|
|
|
|
SkFont font(nullptr, 20);
|
|
|
|
SkPaint captionPaint;
|
|
|
|
captionPaint.setColor(SK_ColorWHITE);
|
|
|
|
canvas->drawString(caption, 10, 30, font, captionPaint);
|
2020-01-07 02:49:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 16:51:08 +00:00
|
|
|
class TessellatedWedge::Click : public Sample::Click {
|
2020-01-07 02:49:37 +00:00
|
|
|
public:
|
|
|
|
Click(int ptIdx) : fPtIdx(ptIdx) {}
|
|
|
|
|
|
|
|
void doClick(SkPath* path) {
|
|
|
|
if (fPtIdx >= 0) {
|
|
|
|
SkPoint pt = path->getPoint(fPtIdx);
|
2020-05-04 16:43:33 +00:00
|
|
|
SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
|
2020-01-07 02:49:37 +00:00
|
|
|
} else {
|
|
|
|
path->transform(
|
2020-05-21 16:11:27 +00:00
|
|
|
SkMatrix::Translate(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()), path);
|
2020-01-07 02:49:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int fPtIdx;
|
|
|
|
};
|
|
|
|
|
2020-05-29 16:51:08 +00:00
|
|
|
Sample::Click* TessellatedWedge::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
|
2020-01-07 02:49:37 +00:00
|
|
|
const SkPoint* pts = SkPathPriv::PointData(fPath);
|
|
|
|
float fuzz = 20 / fLastViewMatrix.getMaxScale();
|
|
|
|
for (int i = 0; i < fPath.countPoints(); ++i) {
|
|
|
|
SkPoint screenPoint = pts[i];
|
|
|
|
if (fabs(x - screenPoint.x()) < fuzz && fabsf(y - screenPoint.y()) < fuzz) {
|
|
|
|
return new Click(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new Click(-1);
|
|
|
|
}
|
|
|
|
|
2020-11-24 20:43:11 +00:00
|
|
|
static float find_conic_max_error(const SkConic& conic, int numChops) {
|
|
|
|
if (numChops > 1) {
|
|
|
|
int leftChops = numChops / 2;
|
|
|
|
SkConic halves[2];
|
|
|
|
if (conic.chopAt((float)leftChops/numChops, halves)) {
|
|
|
|
return std::max(find_conic_max_error(halves[0], leftChops),
|
|
|
|
find_conic_max_error(halves[1], numChops - leftChops));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const SkPoint* p = conic.fPts;
|
|
|
|
float w = conic.fW;
|
|
|
|
SkVector n = {p[2].fY - p[0].fY, p[0].fX - p[2].fX};
|
|
|
|
float h1 = (p[1] - p[0]).dot(n) / n.length();
|
|
|
|
float h = h1*w / (1 + w);
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_conic_max_errors(const SkPath& path) {
|
|
|
|
SkPath path_;
|
|
|
|
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
|
|
|
|
if (verb == SkPathVerb::kConic) {
|
|
|
|
int n = GrWangsFormula::quadratic(4, pts);
|
|
|
|
float err = find_conic_max_error(SkConic(pts, *w), n);
|
|
|
|
SkDebugf("CONIC MAX ERROR: %f\n", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-29 16:51:08 +00:00
|
|
|
bool TessellatedWedge::onClick(Sample::Click* click) {
|
2020-01-07 02:49:37 +00:00
|
|
|
Click* myClick = (Click*)click;
|
|
|
|
myClick->doClick(&fPath);
|
2020-11-24 20:43:11 +00:00
|
|
|
dump_conic_max_errors(fPath);
|
2020-01-07 02:49:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-23 16:30:24 +00:00
|
|
|
static SkPath update_weight(const SkPath& path) {
|
|
|
|
SkPath path_;
|
|
|
|
for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
|
|
|
|
switch (verb) {
|
|
|
|
case SkPathVerb::kMove:
|
|
|
|
path_.moveTo(pts[0]);
|
|
|
|
break;
|
|
|
|
case SkPathVerb::kLine:
|
|
|
|
path_.lineTo(pts[1]);
|
|
|
|
break;
|
|
|
|
case SkPathVerb::kQuad:
|
|
|
|
path_.quadTo(pts[1], pts[2]);
|
|
|
|
break;
|
|
|
|
case SkPathVerb::kCubic:
|
|
|
|
path_.cubicTo(pts[1], pts[2], pts[3]);
|
|
|
|
break;
|
|
|
|
case SkPathVerb::kConic:
|
|
|
|
path_.conicTo(pts[1], pts[2], (kConicWeight != 1) ? kConicWeight : .99f);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SkUNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
2020-11-24 20:43:11 +00:00
|
|
|
dump_conic_max_errors(path);
|
2020-11-23 16:30:24 +00:00
|
|
|
return path_;
|
|
|
|
}
|
|
|
|
|
2020-05-29 16:51:08 +00:00
|
|
|
bool TessellatedWedge::onChar(SkUnichar unichar) {
|
2020-01-07 02:49:37 +00:00
|
|
|
switch (unichar) {
|
|
|
|
case 'w':
|
2020-06-04 22:44:29 +00:00
|
|
|
fOpFlags = (GrTessellationPathRenderer::OpFlags)(
|
|
|
|
(int)fOpFlags ^ (int)GrTessellationPathRenderer::OpFlags::kWireframe);
|
2020-01-07 02:49:37 +00:00
|
|
|
return true;
|
|
|
|
case 'D': {
|
|
|
|
fPath.dump();
|
|
|
|
return true;
|
|
|
|
}
|
2020-11-23 16:30:24 +00:00
|
|
|
case '+':
|
|
|
|
kConicWeight *= 2;
|
|
|
|
fPath = update_weight(fPath);
|
|
|
|
return true;
|
|
|
|
case '=':
|
|
|
|
kConicWeight *= 5/4.f;
|
|
|
|
fPath = update_weight(fPath);
|
|
|
|
return true;
|
|
|
|
case '_':
|
|
|
|
kConicWeight *= .5f;
|
|
|
|
fPath = update_weight(fPath);
|
|
|
|
return true;
|
|
|
|
case '-':
|
|
|
|
kConicWeight *= 4/5.f;
|
|
|
|
fPath = update_weight(fPath);
|
|
|
|
return true;
|
2020-01-07 02:49:37 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-29 16:51:08 +00:00
|
|
|
Sample* MakeTessellatedWedgeSample() { return new TessellatedWedge; }
|
|
|
|
static SampleRegistry gTessellatedWedgeSample(MakeTessellatedWedgeSample);
|
2020-01-07 02:49:37 +00:00
|
|
|
|
|
|
|
#endif // SK_SUPPORT_GPU
|