blob: 0f2bfe957bfa3521c93f16ab1dcd137afa2309a4 [file] [log] [blame]
Chris Daltonb832ce62020-01-06 19:49:37 -07001/*
2 * Copyright 2019 Google LLC.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkCanvas.h"
9#include "samplecode/Sample.h"
Chris Dalton0363f052020-11-24 13:43:11 -070010#include "src/core/SkGeometry.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070011#include "src/core/SkPathPriv.h"
12#include "tools/ToolUtils.h"
13
14#if SK_SUPPORT_GPU
15
Robert Phillips30ebcf72020-07-09 13:25:17 -040016#include "include/gpu/GrRecordingContext.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070017#include "src/gpu/GrClip.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070018#include "src/gpu/GrMemoryPool.h"
Robert Phillips30ebcf72020-07-09 13:25:17 -040019#include "src/gpu/GrRecordingContextPriv.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070020#include "src/gpu/GrRenderTargetContext.h"
Chris Dalton078f8752020-07-30 19:50:46 -060021#include "src/gpu/tessellate/GrPathTessellateOp.h"
Chris Dalton0363f052020-11-24 13:43:11 -070022#include "src/gpu/tessellate/GrWangsFormula.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070023
Chris Daltonb27f39c2020-11-23 09:30:24 -070024static float kConicWeight = .5;
25
Chris Daltonb832ce62020-01-06 19:49:37 -070026// This sample enables wireframe and visualizes the triangulation generated by
27// GrTessellateWedgeShader.
Chris Dalton0d0758e2020-05-29 10:51:08 -060028class TessellatedWedge : public Sample {
Chris Daltonb832ce62020-01-06 19:49:37 -070029public:
Chris Dalton0d0758e2020-05-29 10:51:08 -060030 TessellatedWedge() {
Chris Daltonf9aea7f2020-01-21 11:19:26 -070031#if 0
32 fPath.moveTo(1, 0);
33 int numSides = 32 * 3;
34 for (int i = 1; i < numSides; ++i) {
35 float theta = 2*3.1415926535897932384626433832785 * i / numSides;
36 fPath.lineTo(std::cos(theta), std::sin(theta));
37 }
Mike Reed1f607332020-05-21 12:11:27 -040038 fPath.transform(SkMatrix::Scale(200, 200));
39 fPath.transform(SkMatrix::Translate(300, 300));
Chris Daltonf9aea7f2020-01-21 11:19:26 -070040#else
Chris Daltonb27f39c2020-11-23 09:30:24 -070041 fPath.moveTo(100, 300);
42 fPath.conicTo(300, 100, 500, 300, kConicWeight);
43 fPath.cubicTo(433, 366, 366, 433, 300, 500);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070044#endif
Chris Daltonb832ce62020-01-06 19:49:37 -070045 }
46
47private:
48 void onDrawContent(SkCanvas*) override;
49 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
50 bool onClick(Sample::Click*) override;
51 bool onChar(SkUnichar) override;
52
53 SkString name() override { return SkString("TessellatedWedge"); }
54
55 SkMatrix fLastViewMatrix = SkMatrix::I();
56 SkPath fPath;
Chris Daltonb96995d2020-06-04 16:44:29 -060057 GrTessellationPathRenderer::OpFlags fOpFlags = GrTessellationPathRenderer::OpFlags::kWireframe;
Chris Daltonb832ce62020-01-06 19:49:37 -070058
59 class Click;
60};
61
Chris Dalton0d0758e2020-05-29 10:51:08 -060062void TessellatedWedge::onDrawContent(SkCanvas* canvas) {
Chris Daltonb832ce62020-01-06 19:49:37 -070063 canvas->clear(SK_ColorBLACK);
64
Robert Phillips30ebcf72020-07-09 13:25:17 -040065 auto ctx = canvas->recordingContext();
Chris Daltonb832ce62020-01-06 19:49:37 -070066 GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
67
68 SkString error;
69 if (!rtc || !ctx) {
70 error = "GPU Only.";
Chris Dalton0d0758e2020-05-29 10:51:08 -060071 } else if (!ctx->priv().caps()->drawInstancedSupport()) {
72 error = "Instanced rendering not supported.";
Chris Daltonb832ce62020-01-06 19:49:37 -070073 } else if (1 == rtc->numSamples() && !ctx->priv().caps()->mixedSamplesSupport()) {
74 error = "MSAA/mixed samples only.";
75 }
76 if (!error.isEmpty()) {
77 SkFont font(nullptr, 20);
78 SkPaint captionPaint;
79 captionPaint.setColor(SK_ColorWHITE);
80 canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
81 return;
82 }
83
84 GrPaint paint;
85 paint.setColor4f({1,0,1,1});
86
87 GrAAType aa;
88 if (rtc->numSamples() > 1) {
89 aa = GrAAType::kMSAA;
90 } else if (rtc->asRenderTargetProxy()->canUseMixedSamples(*ctx->priv().caps())) {
91 aa = GrAAType::kCoverage;
92 } else {
93 aa = GrAAType::kNone;
94 }
95
Brian Salomon70fe17e2020-11-30 14:33:58 -050096 rtc->addDrawOp(GrOp::Make<GrPathTessellateOp>(ctx, canvas->getTotalMatrix(), fPath,
97 std::move(paint), aa, fOpFlags));
Chris Daltonb832ce62020-01-06 19:49:37 -070098
99 // Draw the path points.
100 SkPaint pointsPaint;
101 pointsPaint.setColor(SK_ColorBLUE);
102 pointsPaint.setStrokeWidth(8);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700103 SkPath devPath = fPath;
104 devPath.transform(canvas->getTotalMatrix());
105 {
106 SkAutoCanvasRestore acr(canvas, true);
107 canvas->setMatrix(SkMatrix::I());
108 canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
109 SkPathPriv::PointData(devPath), pointsPaint);
110 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700111
112 fLastViewMatrix = canvas->getTotalMatrix();
Chris Daltonb27f39c2020-11-23 09:30:24 -0700113
114
115 SkString caption;
116 caption.printf("w=%f (=/- and +/_ to change)", kConicWeight);
117 SkFont font(nullptr, 20);
118 SkPaint captionPaint;
119 captionPaint.setColor(SK_ColorWHITE);
120 canvas->drawString(caption, 10, 30, font, captionPaint);
Chris Daltonb832ce62020-01-06 19:49:37 -0700121}
122
Chris Dalton0d0758e2020-05-29 10:51:08 -0600123class TessellatedWedge::Click : public Sample::Click {
Chris Daltonb832ce62020-01-06 19:49:37 -0700124public:
125 Click(int ptIdx) : fPtIdx(ptIdx) {}
126
127 void doClick(SkPath* path) {
128 if (fPtIdx >= 0) {
129 SkPoint pt = path->getPoint(fPtIdx);
Chris Dalton8d3eb242020-05-04 10:43:33 -0600130 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
Chris Daltonb832ce62020-01-06 19:49:37 -0700131 } else {
132 path->transform(
Mike Reed1f607332020-05-21 12:11:27 -0400133 SkMatrix::Translate(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()), path);
Chris Daltonb832ce62020-01-06 19:49:37 -0700134 }
135 }
136
137private:
138 int fPtIdx;
139};
140
Chris Dalton0d0758e2020-05-29 10:51:08 -0600141Sample::Click* TessellatedWedge::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700142 const SkPoint* pts = SkPathPriv::PointData(fPath);
143 float fuzz = 20 / fLastViewMatrix.getMaxScale();
144 for (int i = 0; i < fPath.countPoints(); ++i) {
145 SkPoint screenPoint = pts[i];
146 if (fabs(x - screenPoint.x()) < fuzz && fabsf(y - screenPoint.y()) < fuzz) {
147 return new Click(i);
148 }
149 }
150 return new Click(-1);
151}
152
Chris Dalton0363f052020-11-24 13:43:11 -0700153static float find_conic_max_error(const SkConic& conic, int numChops) {
154 if (numChops > 1) {
155 int leftChops = numChops / 2;
156 SkConic halves[2];
157 if (conic.chopAt((float)leftChops/numChops, halves)) {
158 return std::max(find_conic_max_error(halves[0], leftChops),
159 find_conic_max_error(halves[1], numChops - leftChops));
160 }
161 }
162
163 const SkPoint* p = conic.fPts;
164 float w = conic.fW;
165 SkVector n = {p[2].fY - p[0].fY, p[0].fX - p[2].fX};
166 float h1 = (p[1] - p[0]).dot(n) / n.length();
167 float h = h1*w / (1 + w);
168 return h;
169}
170
171static void dump_conic_max_errors(const SkPath& path) {
172 SkPath path_;
173 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
174 if (verb == SkPathVerb::kConic) {
175 int n = GrWangsFormula::quadratic(4, pts);
176 float err = find_conic_max_error(SkConic(pts, *w), n);
177 SkDebugf("CONIC MAX ERROR: %f\n", err);
178 }
179 }
180}
181
Chris Dalton0d0758e2020-05-29 10:51:08 -0600182bool TessellatedWedge::onClick(Sample::Click* click) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700183 Click* myClick = (Click*)click;
184 myClick->doClick(&fPath);
Chris Dalton0363f052020-11-24 13:43:11 -0700185 dump_conic_max_errors(fPath);
Chris Daltonb832ce62020-01-06 19:49:37 -0700186 return true;
187}
188
Chris Daltonb27f39c2020-11-23 09:30:24 -0700189static SkPath update_weight(const SkPath& path) {
190 SkPath path_;
191 for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
192 switch (verb) {
193 case SkPathVerb::kMove:
194 path_.moveTo(pts[0]);
195 break;
196 case SkPathVerb::kLine:
197 path_.lineTo(pts[1]);
198 break;
199 case SkPathVerb::kQuad:
200 path_.quadTo(pts[1], pts[2]);
201 break;
202 case SkPathVerb::kCubic:
203 path_.cubicTo(pts[1], pts[2], pts[3]);
204 break;
205 case SkPathVerb::kConic:
206 path_.conicTo(pts[1], pts[2], (kConicWeight != 1) ? kConicWeight : .99f);
207 break;
208 default:
209 SkUNREACHABLE;
210 }
211 }
Chris Dalton0363f052020-11-24 13:43:11 -0700212 dump_conic_max_errors(path);
Chris Daltonb27f39c2020-11-23 09:30:24 -0700213 return path_;
214}
215
Chris Dalton0d0758e2020-05-29 10:51:08 -0600216bool TessellatedWedge::onChar(SkUnichar unichar) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700217 switch (unichar) {
218 case 'w':
Chris Daltonb96995d2020-06-04 16:44:29 -0600219 fOpFlags = (GrTessellationPathRenderer::OpFlags)(
220 (int)fOpFlags ^ (int)GrTessellationPathRenderer::OpFlags::kWireframe);
Chris Daltonb832ce62020-01-06 19:49:37 -0700221 return true;
222 case 'D': {
223 fPath.dump();
224 return true;
225 }
Chris Daltonb27f39c2020-11-23 09:30:24 -0700226 case '+':
227 kConicWeight *= 2;
228 fPath = update_weight(fPath);
229 return true;
230 case '=':
231 kConicWeight *= 5/4.f;
232 fPath = update_weight(fPath);
233 return true;
234 case '_':
235 kConicWeight *= .5f;
236 fPath = update_weight(fPath);
237 return true;
238 case '-':
239 kConicWeight *= 4/5.f;
240 fPath = update_weight(fPath);
241 return true;
Chris Daltonb832ce62020-01-06 19:49:37 -0700242 }
243 return false;
244}
245
Chris Dalton0d0758e2020-05-29 10:51:08 -0600246Sample* MakeTessellatedWedgeSample() { return new TessellatedWedge; }
247static SampleRegistry gTessellatedWedgeSample(MakeTessellatedWedgeSample);
Chris Daltonb832ce62020-01-06 19:49:37 -0700248
249#endif // SK_SUPPORT_GPU