blob: 21e6d9c2a366c28c6454ced72de4cf7605df9268 [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"
Brian Salomon8f7d9532020-12-23 09:16:59 -050017#include "src/core/SkCanvasPriv.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070018#include "src/gpu/GrClip.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070019#include "src/gpu/GrMemoryPool.h"
Robert Phillips30ebcf72020-07-09 13:25:17 -040020#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomoneebe7352020-12-09 16:37:04 -050021#include "src/gpu/GrSurfaceDrawContext.h"
Chris Daltonb03f4a12021-01-27 17:45:52 -070022#include "src/gpu/tessellate/GrTessellatingStencilFillOp.h"
Chris Dalton0363f052020-11-24 13:43:11 -070023#include "src/gpu/tessellate/GrWangsFormula.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070024
Chris Daltonb27f39c2020-11-23 09:30:24 -070025static float kConicWeight = .5;
26
Chris Daltonb832ce62020-01-06 19:49:37 -070027// This sample enables wireframe and visualizes the triangulation generated by
28// GrTessellateWedgeShader.
Chris Dalton0d0758e2020-05-29 10:51:08 -060029class TessellatedWedge : public Sample {
Chris Daltonb832ce62020-01-06 19:49:37 -070030public:
Chris Dalton0d0758e2020-05-29 10:51:08 -060031 TessellatedWedge() {
Chris Daltonf9aea7f2020-01-21 11:19:26 -070032#if 0
33 fPath.moveTo(1, 0);
34 int numSides = 32 * 3;
35 for (int i = 1; i < numSides; ++i) {
36 float theta = 2*3.1415926535897932384626433832785 * i / numSides;
37 fPath.lineTo(std::cos(theta), std::sin(theta));
38 }
Mike Reed1f607332020-05-21 12:11:27 -040039 fPath.transform(SkMatrix::Scale(200, 200));
40 fPath.transform(SkMatrix::Translate(300, 300));
Chris Daltonf9aea7f2020-01-21 11:19:26 -070041#else
Chris Daltonb27f39c2020-11-23 09:30:24 -070042 fPath.moveTo(100, 300);
43 fPath.conicTo(300, 100, 500, 300, kConicWeight);
44 fPath.cubicTo(433, 366, 366, 433, 300, 500);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070045#endif
Chris Daltonb832ce62020-01-06 19:49:37 -070046 }
47
48private:
49 void onDrawContent(SkCanvas*) override;
50 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
51 bool onClick(Sample::Click*) override;
52 bool onChar(SkUnichar) override;
53
54 SkString name() override { return SkString("TessellatedWedge"); }
55
56 SkMatrix fLastViewMatrix = SkMatrix::I();
57 SkPath fPath;
Chris Daltonb96995d2020-06-04 16:44:29 -060058 GrTessellationPathRenderer::OpFlags fOpFlags = GrTessellationPathRenderer::OpFlags::kWireframe;
Chris Daltonb832ce62020-01-06 19:49:37 -070059
60 class Click;
61};
62
Chris Dalton0d0758e2020-05-29 10:51:08 -060063void TessellatedWedge::onDrawContent(SkCanvas* canvas) {
Chris Daltonb832ce62020-01-06 19:49:37 -070064 canvas->clear(SK_ColorBLACK);
65
Robert Phillips30ebcf72020-07-09 13:25:17 -040066 auto ctx = canvas->recordingContext();
Brian Salomon8f7d9532020-12-23 09:16:59 -050067 GrSurfaceDrawContext* sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
Chris Daltonb832ce62020-01-06 19:49:37 -070068
69 SkString error;
Brian Salomon8f7d9532020-12-23 09:16:59 -050070 if (!sdc || !ctx) {
Chris Daltonb832ce62020-01-06 19:49:37 -070071 error = "GPU Only.";
Chris Dalton0d0758e2020-05-29 10:51:08 -060072 } else if (!ctx->priv().caps()->drawInstancedSupport()) {
73 error = "Instanced rendering not supported.";
Brian Salomon8f7d9532020-12-23 09:16:59 -050074 } else if (sdc->numSamples() == 1 && !ctx->priv().caps()->mixedSamplesSupport()) {
Chris Daltonb832ce62020-01-06 19:49:37 -070075 error = "MSAA/mixed samples only.";
76 }
77 if (!error.isEmpty()) {
78 SkFont font(nullptr, 20);
79 SkPaint captionPaint;
80 captionPaint.setColor(SK_ColorWHITE);
81 canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
82 return;
83 }
84
85 GrPaint paint;
86 paint.setColor4f({1,0,1,1});
87
88 GrAAType aa;
Brian Salomon8f7d9532020-12-23 09:16:59 -050089 if (sdc->numSamples() > 1) {
Chris Daltonb832ce62020-01-06 19:49:37 -070090 aa = GrAAType::kMSAA;
Brian Salomon8f7d9532020-12-23 09:16:59 -050091 } else if (sdc->asRenderTargetProxy()->canUseMixedSamples(*ctx->priv().caps())) {
Chris Daltonb832ce62020-01-06 19:49:37 -070092 aa = GrAAType::kCoverage;
93 } else {
94 aa = GrAAType::kNone;
95 }
96
Chris Daltonb03f4a12021-01-27 17:45:52 -070097 sdc->addDrawOp(GrOp::Make<GrTessellatingStencilFillOp>(ctx, canvas->getTotalMatrix(), fPath,
98 std::move(paint), aa, fOpFlags));
Chris Daltonb832ce62020-01-06 19:49:37 -070099
100 // Draw the path points.
101 SkPaint pointsPaint;
102 pointsPaint.setColor(SK_ColorBLUE);
103 pointsPaint.setStrokeWidth(8);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700104 SkPath devPath = fPath;
105 devPath.transform(canvas->getTotalMatrix());
106 {
107 SkAutoCanvasRestore acr(canvas, true);
108 canvas->setMatrix(SkMatrix::I());
109 canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
110 SkPathPriv::PointData(devPath), pointsPaint);
111 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700112
113 fLastViewMatrix = canvas->getTotalMatrix();
Chris Daltonb27f39c2020-11-23 09:30:24 -0700114
115
116 SkString caption;
117 caption.printf("w=%f (=/- and +/_ to change)", kConicWeight);
118 SkFont font(nullptr, 20);
119 SkPaint captionPaint;
120 captionPaint.setColor(SK_ColorWHITE);
121 canvas->drawString(caption, 10, 30, font, captionPaint);
Chris Daltonb832ce62020-01-06 19:49:37 -0700122}
123
Chris Dalton0d0758e2020-05-29 10:51:08 -0600124class TessellatedWedge::Click : public Sample::Click {
Chris Daltonb832ce62020-01-06 19:49:37 -0700125public:
126 Click(int ptIdx) : fPtIdx(ptIdx) {}
127
128 void doClick(SkPath* path) {
129 if (fPtIdx >= 0) {
130 SkPoint pt = path->getPoint(fPtIdx);
Chris Dalton8d3eb242020-05-04 10:43:33 -0600131 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
Chris Daltonb832ce62020-01-06 19:49:37 -0700132 } else {
133 path->transform(
Mike Reed1f607332020-05-21 12:11:27 -0400134 SkMatrix::Translate(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()), path);
Chris Daltonb832ce62020-01-06 19:49:37 -0700135 }
136 }
137
138private:
139 int fPtIdx;
140};
141
Chris Dalton0d0758e2020-05-29 10:51:08 -0600142Sample::Click* TessellatedWedge::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700143 const SkPoint* pts = SkPathPriv::PointData(fPath);
144 float fuzz = 20 / fLastViewMatrix.getMaxScale();
145 for (int i = 0; i < fPath.countPoints(); ++i) {
146 SkPoint screenPoint = pts[i];
147 if (fabs(x - screenPoint.x()) < fuzz && fabsf(y - screenPoint.y()) < fuzz) {
148 return new Click(i);
149 }
150 }
151 return new Click(-1);
152}
153
Chris Dalton0363f052020-11-24 13:43:11 -0700154static float find_conic_max_error(const SkConic& conic, int numChops) {
155 if (numChops > 1) {
156 int leftChops = numChops / 2;
157 SkConic halves[2];
158 if (conic.chopAt((float)leftChops/numChops, halves)) {
159 return std::max(find_conic_max_error(halves[0], leftChops),
160 find_conic_max_error(halves[1], numChops - leftChops));
161 }
162 }
163
164 const SkPoint* p = conic.fPts;
165 float w = conic.fW;
166 SkVector n = {p[2].fY - p[0].fY, p[0].fX - p[2].fX};
167 float h1 = (p[1] - p[0]).dot(n) / n.length();
168 float h = h1*w / (1 + w);
169 return h;
170}
171
172static void dump_conic_max_errors(const SkPath& path) {
173 SkPath path_;
174 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
175 if (verb == SkPathVerb::kConic) {
176 int n = GrWangsFormula::quadratic(4, pts);
177 float err = find_conic_max_error(SkConic(pts, *w), n);
178 SkDebugf("CONIC MAX ERROR: %f\n", err);
179 }
180 }
181}
182
Chris Dalton0d0758e2020-05-29 10:51:08 -0600183bool TessellatedWedge::onClick(Sample::Click* click) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700184 Click* myClick = (Click*)click;
185 myClick->doClick(&fPath);
Chris Dalton0363f052020-11-24 13:43:11 -0700186 dump_conic_max_errors(fPath);
Chris Daltonb832ce62020-01-06 19:49:37 -0700187 return true;
188}
189
Chris Daltonb27f39c2020-11-23 09:30:24 -0700190static SkPath update_weight(const SkPath& path) {
191 SkPath path_;
192 for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
193 switch (verb) {
194 case SkPathVerb::kMove:
195 path_.moveTo(pts[0]);
196 break;
197 case SkPathVerb::kLine:
198 path_.lineTo(pts[1]);
199 break;
200 case SkPathVerb::kQuad:
201 path_.quadTo(pts[1], pts[2]);
202 break;
203 case SkPathVerb::kCubic:
204 path_.cubicTo(pts[1], pts[2], pts[3]);
205 break;
206 case SkPathVerb::kConic:
207 path_.conicTo(pts[1], pts[2], (kConicWeight != 1) ? kConicWeight : .99f);
208 break;
209 default:
210 SkUNREACHABLE;
211 }
212 }
Chris Dalton0363f052020-11-24 13:43:11 -0700213 dump_conic_max_errors(path);
Chris Daltonb27f39c2020-11-23 09:30:24 -0700214 return path_;
215}
216
Chris Dalton0d0758e2020-05-29 10:51:08 -0600217bool TessellatedWedge::onChar(SkUnichar unichar) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700218 switch (unichar) {
219 case 'w':
Chris Daltonb96995d2020-06-04 16:44:29 -0600220 fOpFlags = (GrTessellationPathRenderer::OpFlags)(
221 (int)fOpFlags ^ (int)GrTessellationPathRenderer::OpFlags::kWireframe);
Chris Daltonb832ce62020-01-06 19:49:37 -0700222 return true;
223 case 'D': {
224 fPath.dump();
225 return true;
226 }
Chris Daltonb27f39c2020-11-23 09:30:24 -0700227 case '+':
228 kConicWeight *= 2;
229 fPath = update_weight(fPath);
230 return true;
231 case '=':
232 kConicWeight *= 5/4.f;
233 fPath = update_weight(fPath);
234 return true;
235 case '_':
236 kConicWeight *= .5f;
237 fPath = update_weight(fPath);
238 return true;
239 case '-':
240 kConicWeight *= 4/5.f;
241 fPath = update_weight(fPath);
242 return true;
Chris Daltonb832ce62020-01-06 19:49:37 -0700243 }
244 return false;
245}
246
Chris Dalton0d0758e2020-05-29 10:51:08 -0600247Sample* MakeTessellatedWedgeSample() { return new TessellatedWedge; }
248static SampleRegistry gTessellatedWedgeSample(MakeTessellatedWedgeSample);
Chris Daltonb832ce62020-01-06 19:49:37 -0700249
250#endif // SK_SUPPORT_GPU