1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2019 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathEffect.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkDashPathEffect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/pathops/SkPathOps.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrQuad.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/QuadPerEdgeAA.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "tools/viewer/ClickHandlerSlide.h"
17*c8dee2aaSAndroid Build Coastguard Worker
18*c8dee2aaSAndroid Build Coastguard Worker using VertexSpec = skgpu::ganesh::QuadPerEdgeAA::VertexSpec;
19*c8dee2aaSAndroid Build Coastguard Worker using ColorType = skgpu::ganesh::QuadPerEdgeAA::ColorType;
20*c8dee2aaSAndroid Build Coastguard Worker using Subset = skgpu::ganesh::QuadPerEdgeAA::Subset;
21*c8dee2aaSAndroid Build Coastguard Worker using IndexBufferOption = skgpu::ganesh::QuadPerEdgeAA::IndexBufferOption;
22*c8dee2aaSAndroid Build Coastguard Worker
23*c8dee2aaSAndroid Build Coastguard Worker // Draw a line through the two points, outset by a fixed length in screen space
draw_extended_line(SkCanvas * canvas,const SkPaint & paint,const SkPoint & p0,const SkPoint & p1)24*c8dee2aaSAndroid Build Coastguard Worker static void draw_extended_line(SkCanvas* canvas, const SkPaint& paint,
25*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& p0, const SkPoint& p1) {
26*c8dee2aaSAndroid Build Coastguard Worker SkVector v = p1 - p0;
27*c8dee2aaSAndroid Build Coastguard Worker v.setLength(v.length() + 3.f);
28*c8dee2aaSAndroid Build Coastguard Worker canvas->drawLine(p1 - v, p0 + v, paint);
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker // Draw normal vector too
31*c8dee2aaSAndroid Build Coastguard Worker SkPaint normalPaint = paint;
32*c8dee2aaSAndroid Build Coastguard Worker normalPaint.setPathEffect(nullptr);
33*c8dee2aaSAndroid Build Coastguard Worker normalPaint.setStrokeWidth(paint.getStrokeWidth() / 4.f);
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker SkVector n = {v.fY, -v.fX};
36*c8dee2aaSAndroid Build Coastguard Worker n.setLength(.25f);
37*c8dee2aaSAndroid Build Coastguard Worker SkPoint m = (p0 + p1) * 0.5f;
38*c8dee2aaSAndroid Build Coastguard Worker canvas->drawLine(m, m + n, normalPaint);
39*c8dee2aaSAndroid Build Coastguard Worker }
40*c8dee2aaSAndroid Build Coastguard Worker
make_aa_line(const SkPoint & p0,const SkPoint & p1,bool aaOn,bool outset,SkPoint line[2])41*c8dee2aaSAndroid Build Coastguard Worker static void make_aa_line(const SkPoint& p0, const SkPoint& p1, bool aaOn,
42*c8dee2aaSAndroid Build Coastguard Worker bool outset, SkPoint line[2]) {
43*c8dee2aaSAndroid Build Coastguard Worker SkVector n = {0.f, 0.f};
44*c8dee2aaSAndroid Build Coastguard Worker if (aaOn) {
45*c8dee2aaSAndroid Build Coastguard Worker SkVector v = p1 - p0;
46*c8dee2aaSAndroid Build Coastguard Worker n = outset ? SkVector::Make(v.fY, -v.fX) : SkVector::Make(-v.fY, v.fX);
47*c8dee2aaSAndroid Build Coastguard Worker n.setLength(0.5f);
48*c8dee2aaSAndroid Build Coastguard Worker }
49*c8dee2aaSAndroid Build Coastguard Worker
50*c8dee2aaSAndroid Build Coastguard Worker line[0] = p0 + n;
51*c8dee2aaSAndroid Build Coastguard Worker line[1] = p1 + n;
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker // To the line through l0-l1, not capped at the end points of the segment
signed_distance(const SkPoint & p,const SkPoint & l0,const SkPoint & l1)55*c8dee2aaSAndroid Build Coastguard Worker static SkScalar signed_distance(const SkPoint& p, const SkPoint& l0, const SkPoint& l1) {
56*c8dee2aaSAndroid Build Coastguard Worker SkVector v = l1 - l0;
57*c8dee2aaSAndroid Build Coastguard Worker v.normalize();
58*c8dee2aaSAndroid Build Coastguard Worker SkVector n = {v.fY, -v.fX};
59*c8dee2aaSAndroid Build Coastguard Worker SkScalar c = -n.dot(l0);
60*c8dee2aaSAndroid Build Coastguard Worker return n.dot(p) + c;
61*c8dee2aaSAndroid Build Coastguard Worker }
62*c8dee2aaSAndroid Build Coastguard Worker
get_area_coverage(const bool edgeAA[4],const SkPoint corners[4],const SkPoint & point)63*c8dee2aaSAndroid Build Coastguard Worker static SkScalar get_area_coverage(const bool edgeAA[4], const SkPoint corners[4],
64*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& point) {
65*c8dee2aaSAndroid Build Coastguard Worker SkPath shape;
66*c8dee2aaSAndroid Build Coastguard Worker shape.addPoly(corners, 4, true);
67*c8dee2aaSAndroid Build Coastguard Worker SkPath pixel;
68*c8dee2aaSAndroid Build Coastguard Worker pixel.addRect(SkRect::MakeXYWH(point.fX - 0.5f, point.fY - 0.5f, 1.f, 1.f));
69*c8dee2aaSAndroid Build Coastguard Worker
70*c8dee2aaSAndroid Build Coastguard Worker SkPath intersection;
71*c8dee2aaSAndroid Build Coastguard Worker if (!Op(shape, pixel, kIntersect_SkPathOp, &intersection) || intersection.isEmpty()) {
72*c8dee2aaSAndroid Build Coastguard Worker return 0.f;
73*c8dee2aaSAndroid Build Coastguard Worker }
74*c8dee2aaSAndroid Build Coastguard Worker
75*c8dee2aaSAndroid Build Coastguard Worker // Calculate area of the convex polygon
76*c8dee2aaSAndroid Build Coastguard Worker SkScalar area = 0.f;
77*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < intersection.countPoints(); ++i) {
78*c8dee2aaSAndroid Build Coastguard Worker SkPoint p0 = intersection.getPoint(i);
79*c8dee2aaSAndroid Build Coastguard Worker SkPoint p1 = intersection.getPoint((i + 1) % intersection.countPoints());
80*c8dee2aaSAndroid Build Coastguard Worker SkScalar det = p0.fX * p1.fY - p1.fX * p0.fY;
81*c8dee2aaSAndroid Build Coastguard Worker area += det;
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker // Scale by 1/2, then take abs value (this area formula is signed based on point winding, but
85*c8dee2aaSAndroid Build Coastguard Worker // since it's convex, just make it positive).
86*c8dee2aaSAndroid Build Coastguard Worker area = SkScalarAbs(0.5f * area);
87*c8dee2aaSAndroid Build Coastguard Worker
88*c8dee2aaSAndroid Build Coastguard Worker // Now account for the edge AA. If the pixel center is outside of a non-AA edge, turn of its
89*c8dee2aaSAndroid Build Coastguard Worker // coverage. If the pixel only intersects non-AA edges, then set coverage to 1.
90*c8dee2aaSAndroid Build Coastguard Worker bool needsNonAA = false;
91*c8dee2aaSAndroid Build Coastguard Worker SkScalar edgeD[4];
92*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
93*c8dee2aaSAndroid Build Coastguard Worker SkPoint e0 = corners[i];
94*c8dee2aaSAndroid Build Coastguard Worker SkPoint e1 = corners[(i + 1) % 4];
95*c8dee2aaSAndroid Build Coastguard Worker edgeD[i] = -signed_distance(point, e0, e1);
96*c8dee2aaSAndroid Build Coastguard Worker if (!edgeAA[i]) {
97*c8dee2aaSAndroid Build Coastguard Worker if (edgeD[i] < -1e-4f) {
98*c8dee2aaSAndroid Build Coastguard Worker return 0.f; // Outside of non-AA line
99*c8dee2aaSAndroid Build Coastguard Worker }
100*c8dee2aaSAndroid Build Coastguard Worker needsNonAA = true;
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker }
103*c8dee2aaSAndroid Build Coastguard Worker // Otherwise inside the shape, so check if any AA edge exerts influence over nonAA
104*c8dee2aaSAndroid Build Coastguard Worker if (needsNonAA) {
105*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; i++) {
106*c8dee2aaSAndroid Build Coastguard Worker if (edgeAA[i] && edgeD[i] < 0.5f) {
107*c8dee2aaSAndroid Build Coastguard Worker needsNonAA = false;
108*c8dee2aaSAndroid Build Coastguard Worker break;
109*c8dee2aaSAndroid Build Coastguard Worker }
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker return needsNonAA ? 1.f : area;
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker // FIXME take into account max coverage properly,
get_edge_dist_coverage(const bool edgeAA[4],const SkPoint corners[4],const SkPoint outsetLines[8],const SkPoint insetLines[8],const SkPoint & point)116*c8dee2aaSAndroid Build Coastguard Worker static SkScalar get_edge_dist_coverage(const bool edgeAA[4], const SkPoint corners[4],
117*c8dee2aaSAndroid Build Coastguard Worker const SkPoint outsetLines[8], const SkPoint insetLines[8],
118*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& point) {
119*c8dee2aaSAndroid Build Coastguard Worker bool flip = false;
120*c8dee2aaSAndroid Build Coastguard Worker // If the quad has been inverted, the original corners will not all be on the negative side of
121*c8dee2aaSAndroid Build Coastguard Worker // every outset line. When that happens, calculate coverage using the "inset" lines and flip
122*c8dee2aaSAndroid Build Coastguard Worker // the signed distance
123*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
124*c8dee2aaSAndroid Build Coastguard Worker for (int j = 0; j < 4; ++j) {
125*c8dee2aaSAndroid Build Coastguard Worker SkScalar d = signed_distance(corners[i], outsetLines[j * 2], outsetLines[j * 2 + 1]);
126*c8dee2aaSAndroid Build Coastguard Worker if (d > 1e-4f) {
127*c8dee2aaSAndroid Build Coastguard Worker flip = true;
128*c8dee2aaSAndroid Build Coastguard Worker break;
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker }
131*c8dee2aaSAndroid Build Coastguard Worker if (flip) {
132*c8dee2aaSAndroid Build Coastguard Worker break;
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker
136*c8dee2aaSAndroid Build Coastguard Worker const SkPoint* lines = flip ? insetLines : outsetLines;
137*c8dee2aaSAndroid Build Coastguard Worker
138*c8dee2aaSAndroid Build Coastguard Worker SkScalar minCoverage = 1.f;
139*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
140*c8dee2aaSAndroid Build Coastguard Worker // Multiply by negative 1 so that outside points have negative distances
141*c8dee2aaSAndroid Build Coastguard Worker SkScalar d = (flip ? 1 : -1) * signed_distance(point, lines[i * 2], lines[i * 2 + 1]);
142*c8dee2aaSAndroid Build Coastguard Worker if (!edgeAA[i] && d >= -1e-4f) {
143*c8dee2aaSAndroid Build Coastguard Worker d = 1.f;
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker if (d < minCoverage) {
146*c8dee2aaSAndroid Build Coastguard Worker minCoverage = d;
147*c8dee2aaSAndroid Build Coastguard Worker if (minCoverage < 0.f) {
148*c8dee2aaSAndroid Build Coastguard Worker break; // Outside the shape
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker return minCoverage < 0.f ? 0.f : minCoverage;
153*c8dee2aaSAndroid Build Coastguard Worker }
154*c8dee2aaSAndroid Build Coastguard Worker
inside_triangle(const SkPoint & point,const SkPoint & t0,const SkPoint & t1,const SkPoint & t2,SkScalar bary[3])155*c8dee2aaSAndroid Build Coastguard Worker static bool inside_triangle(const SkPoint& point, const SkPoint& t0, const SkPoint& t1,
156*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& t2, SkScalar bary[3]) {
157*c8dee2aaSAndroid Build Coastguard Worker // Check sign of t0 to (t1,t2). If it is positive, that means the normals point into the
158*c8dee2aaSAndroid Build Coastguard Worker // triangle otherwise the normals point outside the triangle so update edge distances as
159*c8dee2aaSAndroid Build Coastguard Worker // necessary
160*c8dee2aaSAndroid Build Coastguard Worker bool flip = signed_distance(t0, t1, t2) < 0.f;
161*c8dee2aaSAndroid Build Coastguard Worker
162*c8dee2aaSAndroid Build Coastguard Worker SkScalar d0 = (flip ? -1 : 1) * signed_distance(point, t0, t1);
163*c8dee2aaSAndroid Build Coastguard Worker SkScalar d1 = (flip ? -1 : 1) * signed_distance(point, t1, t2);
164*c8dee2aaSAndroid Build Coastguard Worker SkScalar d2 = (flip ? -1 : 1) * signed_distance(point, t2, t0);
165*c8dee2aaSAndroid Build Coastguard Worker // Be a little forgiving
166*c8dee2aaSAndroid Build Coastguard Worker if (d0 < -1e-4f || d1 < -1e-4f || d2 < -1e-4f) {
167*c8dee2aaSAndroid Build Coastguard Worker return false;
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker
170*c8dee2aaSAndroid Build Coastguard Worker // Inside, so calculate barycentric coords from the sideline distances
171*c8dee2aaSAndroid Build Coastguard Worker SkScalar d01 = (t0 - t1).length();
172*c8dee2aaSAndroid Build Coastguard Worker SkScalar d12 = (t1 - t2).length();
173*c8dee2aaSAndroid Build Coastguard Worker SkScalar d20 = (t2 - t0).length();
174*c8dee2aaSAndroid Build Coastguard Worker
175*c8dee2aaSAndroid Build Coastguard Worker if (SkScalarNearlyZero(d12) || SkScalarNearlyZero(d20) || SkScalarNearlyZero(d01)) {
176*c8dee2aaSAndroid Build Coastguard Worker // Empty degenerate triangle
177*c8dee2aaSAndroid Build Coastguard Worker return false;
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker
180*c8dee2aaSAndroid Build Coastguard Worker // Coordinates for a vertex use distances to the opposite edge
181*c8dee2aaSAndroid Build Coastguard Worker bary[0] = d1 * d12;
182*c8dee2aaSAndroid Build Coastguard Worker bary[1] = d2 * d20;
183*c8dee2aaSAndroid Build Coastguard Worker bary[2] = d0 * d01;
184*c8dee2aaSAndroid Build Coastguard Worker // And normalize
185*c8dee2aaSAndroid Build Coastguard Worker SkScalar sum = bary[0] + bary[1] + bary[2];
186*c8dee2aaSAndroid Build Coastguard Worker bary[0] /= sum;
187*c8dee2aaSAndroid Build Coastguard Worker bary[1] /= sum;
188*c8dee2aaSAndroid Build Coastguard Worker bary[2] /= sum;
189*c8dee2aaSAndroid Build Coastguard Worker
190*c8dee2aaSAndroid Build Coastguard Worker return true;
191*c8dee2aaSAndroid Build Coastguard Worker }
192*c8dee2aaSAndroid Build Coastguard Worker
get_framed_coverage(const SkPoint outer[4],const SkScalar outerCoverages[4],const SkPoint inner[4],const SkScalar innerCoverages[4],const SkRect & geomDomain,const SkPoint & point)193*c8dee2aaSAndroid Build Coastguard Worker static SkScalar get_framed_coverage(const SkPoint outer[4], const SkScalar outerCoverages[4],
194*c8dee2aaSAndroid Build Coastguard Worker const SkPoint inner[4], const SkScalar innerCoverages[4],
195*c8dee2aaSAndroid Build Coastguard Worker const SkRect& geomDomain, const SkPoint& point) {
196*c8dee2aaSAndroid Build Coastguard Worker // Triangles are ordered clock wise. Indices >= 4 refer to inner[i - 4]. Otherwise its outer[i].
197*c8dee2aaSAndroid Build Coastguard Worker static const int kFrameTris[] = {
198*c8dee2aaSAndroid Build Coastguard Worker 0, 1, 4, 4, 1, 5,
199*c8dee2aaSAndroid Build Coastguard Worker 1, 2, 5, 5, 2, 6,
200*c8dee2aaSAndroid Build Coastguard Worker 2, 3, 6, 6, 3, 7,
201*c8dee2aaSAndroid Build Coastguard Worker 3, 0, 7, 7, 0, 4,
202*c8dee2aaSAndroid Build Coastguard Worker 4, 5, 7, 7, 5, 6
203*c8dee2aaSAndroid Build Coastguard Worker };
204*c8dee2aaSAndroid Build Coastguard Worker static const int kNumTris = 10;
205*c8dee2aaSAndroid Build Coastguard Worker
206*c8dee2aaSAndroid Build Coastguard Worker SkScalar bary[3];
207*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kNumTris; ++i) {
208*c8dee2aaSAndroid Build Coastguard Worker int i0 = kFrameTris[i * 3];
209*c8dee2aaSAndroid Build Coastguard Worker int i1 = kFrameTris[i * 3 + 1];
210*c8dee2aaSAndroid Build Coastguard Worker int i2 = kFrameTris[i * 3 + 2];
211*c8dee2aaSAndroid Build Coastguard Worker
212*c8dee2aaSAndroid Build Coastguard Worker SkPoint t0 = i0 >= 4 ? inner[i0 - 4] : outer[i0];
213*c8dee2aaSAndroid Build Coastguard Worker SkPoint t1 = i1 >= 4 ? inner[i1 - 4] : outer[i1];
214*c8dee2aaSAndroid Build Coastguard Worker SkPoint t2 = i2 >= 4 ? inner[i2 - 4] : outer[i2];
215*c8dee2aaSAndroid Build Coastguard Worker if (inside_triangle(point, t0, t1, t2, bary)) {
216*c8dee2aaSAndroid Build Coastguard Worker // Calculate coverage by barycentric interpolation of coverages
217*c8dee2aaSAndroid Build Coastguard Worker SkScalar c0 = i0 >= 4 ? innerCoverages[i0 - 4] : outerCoverages[i0];
218*c8dee2aaSAndroid Build Coastguard Worker SkScalar c1 = i1 >= 4 ? innerCoverages[i1 - 4] : outerCoverages[i1];
219*c8dee2aaSAndroid Build Coastguard Worker SkScalar c2 = i2 >= 4 ? innerCoverages[i2 - 4] : outerCoverages[i2];
220*c8dee2aaSAndroid Build Coastguard Worker
221*c8dee2aaSAndroid Build Coastguard Worker SkScalar coverage = bary[0] * c0 + bary[1] * c1 + bary[2] * c2;
222*c8dee2aaSAndroid Build Coastguard Worker if (coverage < 0.5f) {
223*c8dee2aaSAndroid Build Coastguard Worker // Check distances to domain
224*c8dee2aaSAndroid Build Coastguard Worker SkScalar l = SkTPin(point.fX - geomDomain.fLeft, 0.f, 1.f);
225*c8dee2aaSAndroid Build Coastguard Worker SkScalar t = SkTPin(point.fY - geomDomain.fTop, 0.f, 1.f);
226*c8dee2aaSAndroid Build Coastguard Worker SkScalar r = SkTPin(geomDomain.fRight - point.fX, 0.f, 1.f);
227*c8dee2aaSAndroid Build Coastguard Worker SkScalar b = SkTPin(geomDomain.fBottom - point.fY, 0.f, 1.f);
228*c8dee2aaSAndroid Build Coastguard Worker coverage = std::min(coverage, l * t * r * b);
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker return coverage;
231*c8dee2aaSAndroid Build Coastguard Worker }
232*c8dee2aaSAndroid Build Coastguard Worker }
233*c8dee2aaSAndroid Build Coastguard Worker // Not inside any triangle
234*c8dee2aaSAndroid Build Coastguard Worker return 0.f;
235*c8dee2aaSAndroid Build Coastguard Worker }
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkScalar kViewScale = 100.f;
238*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkScalar kViewOffset = 200.f;
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker class DegenerateQuadSlide : public ClickHandlerSlide {
241*c8dee2aaSAndroid Build Coastguard Worker public:
DegenerateQuadSlide(const SkRect & rect)242*c8dee2aaSAndroid Build Coastguard Worker DegenerateQuadSlide(const SkRect& rect)
243*c8dee2aaSAndroid Build Coastguard Worker : fOuterRect(rect)
244*c8dee2aaSAndroid Build Coastguard Worker , fCoverageMode(CoverageMode::kArea) {
245*c8dee2aaSAndroid Build Coastguard Worker fOuterRect.toQuad(fCorners);
246*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
247*c8dee2aaSAndroid Build Coastguard Worker fEdgeAA[i] = true;
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker fName = "DegenerateQuad";
250*c8dee2aaSAndroid Build Coastguard Worker }
251*c8dee2aaSAndroid Build Coastguard Worker
draw(SkCanvas * canvas)252*c8dee2aaSAndroid Build Coastguard Worker void draw(SkCanvas* canvas) override {
253*c8dee2aaSAndroid Build Coastguard Worker static const SkScalar kDotParams[2] = {1.f / kViewScale, 12.f / kViewScale};
254*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPathEffect> dots = SkDashPathEffect::Make(kDotParams, 2, 0.f);
255*c8dee2aaSAndroid Build Coastguard Worker static const SkScalar kDashParams[2] = {8.f / kViewScale, 12.f / kViewScale};
256*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPathEffect> dashes = SkDashPathEffect::Make(kDashParams, 2, 0.f);
257*c8dee2aaSAndroid Build Coastguard Worker
258*c8dee2aaSAndroid Build Coastguard Worker SkPaint circlePaint;
259*c8dee2aaSAndroid Build Coastguard Worker circlePaint.setAntiAlias(true);
260*c8dee2aaSAndroid Build Coastguard Worker
261*c8dee2aaSAndroid Build Coastguard Worker SkPaint linePaint;
262*c8dee2aaSAndroid Build Coastguard Worker linePaint.setAntiAlias(true);
263*c8dee2aaSAndroid Build Coastguard Worker linePaint.setStyle(SkPaint::kStroke_Style);
264*c8dee2aaSAndroid Build Coastguard Worker linePaint.setStrokeWidth(4.f / kViewScale);
265*c8dee2aaSAndroid Build Coastguard Worker linePaint.setStrokeJoin(SkPaint::kRound_Join);
266*c8dee2aaSAndroid Build Coastguard Worker linePaint.setStrokeCap(SkPaint::kRound_Cap);
267*c8dee2aaSAndroid Build Coastguard Worker
268*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(kViewOffset, kViewOffset);
269*c8dee2aaSAndroid Build Coastguard Worker canvas->scale(kViewScale, kViewScale);
270*c8dee2aaSAndroid Build Coastguard Worker
271*c8dee2aaSAndroid Build Coastguard Worker // Draw the outer rectangle as a dotted line
272*c8dee2aaSAndroid Build Coastguard Worker linePaint.setPathEffect(dots);
273*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(fOuterRect, linePaint);
274*c8dee2aaSAndroid Build Coastguard Worker
275*c8dee2aaSAndroid Build Coastguard Worker bool valid = this->isValid();
276*c8dee2aaSAndroid Build Coastguard Worker
277*c8dee2aaSAndroid Build Coastguard Worker if (valid) {
278*c8dee2aaSAndroid Build Coastguard Worker SkPoint outsets[8];
279*c8dee2aaSAndroid Build Coastguard Worker SkPoint insets[8];
280*c8dee2aaSAndroid Build Coastguard Worker // Calculate inset and outset lines for edge-distance visualization
281*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
282*c8dee2aaSAndroid Build Coastguard Worker make_aa_line(fCorners[i], fCorners[(i + 1) % 4], fEdgeAA[i], true, outsets + i * 2);
283*c8dee2aaSAndroid Build Coastguard Worker make_aa_line(fCorners[i], fCorners[(i + 1) % 4], fEdgeAA[i], false, insets + i * 2);
284*c8dee2aaSAndroid Build Coastguard Worker }
285*c8dee2aaSAndroid Build Coastguard Worker
286*c8dee2aaSAndroid Build Coastguard Worker // Calculate inner and outer meshes for GPU visualization
287*c8dee2aaSAndroid Build Coastguard Worker SkPoint gpuOutset[4];
288*c8dee2aaSAndroid Build Coastguard Worker SkScalar gpuOutsetCoverage[4];
289*c8dee2aaSAndroid Build Coastguard Worker SkPoint gpuInset[4];
290*c8dee2aaSAndroid Build Coastguard Worker SkScalar gpuInsetCoverage[4];
291*c8dee2aaSAndroid Build Coastguard Worker SkRect gpuDomain;
292*c8dee2aaSAndroid Build Coastguard Worker this->getTessellatedPoints(gpuInset, gpuInsetCoverage, gpuOutset, gpuOutsetCoverage,
293*c8dee2aaSAndroid Build Coastguard Worker &gpuDomain);
294*c8dee2aaSAndroid Build Coastguard Worker
295*c8dee2aaSAndroid Build Coastguard Worker // Visualize the coverage values across the clamping rectangle, but test pixels outside
296*c8dee2aaSAndroid Build Coastguard Worker // of the "outer" rect since some quad edges can be outset extra far.
297*c8dee2aaSAndroid Build Coastguard Worker SkPaint pixelPaint;
298*c8dee2aaSAndroid Build Coastguard Worker pixelPaint.setAntiAlias(true);
299*c8dee2aaSAndroid Build Coastguard Worker SkRect covRect = fOuterRect.makeOutset(2.f, 2.f);
300*c8dee2aaSAndroid Build Coastguard Worker for (SkScalar py = covRect.fTop; py < covRect.fBottom; py += 1.f) {
301*c8dee2aaSAndroid Build Coastguard Worker for (SkScalar px = covRect.fLeft; px < covRect.fRight; px += 1.f) {
302*c8dee2aaSAndroid Build Coastguard Worker // px and py are the top-left corner of the current pixel, so get center's
303*c8dee2aaSAndroid Build Coastguard Worker // coordinate
304*c8dee2aaSAndroid Build Coastguard Worker SkPoint pixelCenter = {px + 0.5f, py + 0.5f};
305*c8dee2aaSAndroid Build Coastguard Worker SkScalar coverage;
306*c8dee2aaSAndroid Build Coastguard Worker if (fCoverageMode == CoverageMode::kArea) {
307*c8dee2aaSAndroid Build Coastguard Worker coverage = get_area_coverage(fEdgeAA, fCorners, pixelCenter);
308*c8dee2aaSAndroid Build Coastguard Worker } else if (fCoverageMode == CoverageMode::kEdgeDistance) {
309*c8dee2aaSAndroid Build Coastguard Worker coverage = get_edge_dist_coverage(fEdgeAA, fCorners, outsets, insets,
310*c8dee2aaSAndroid Build Coastguard Worker pixelCenter);
311*c8dee2aaSAndroid Build Coastguard Worker } else {
312*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fCoverageMode == CoverageMode::kGPUMesh);
313*c8dee2aaSAndroid Build Coastguard Worker coverage = get_framed_coverage(gpuOutset, gpuOutsetCoverage,
314*c8dee2aaSAndroid Build Coastguard Worker gpuInset, gpuInsetCoverage, gpuDomain,
315*c8dee2aaSAndroid Build Coastguard Worker pixelCenter);
316*c8dee2aaSAndroid Build Coastguard Worker }
317*c8dee2aaSAndroid Build Coastguard Worker
318*c8dee2aaSAndroid Build Coastguard Worker SkRect pixelRect = SkRect::MakeXYWH(px, py, 1.f, 1.f);
319*c8dee2aaSAndroid Build Coastguard Worker pixelRect.inset(0.1f, 0.1f);
320*c8dee2aaSAndroid Build Coastguard Worker
321*c8dee2aaSAndroid Build Coastguard Worker SkScalar a = 1.f - 0.5f * coverage;
322*c8dee2aaSAndroid Build Coastguard Worker pixelPaint.setColor4f({a, a, a, 1.f}, nullptr);
323*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(pixelRect, pixelPaint);
324*c8dee2aaSAndroid Build Coastguard Worker
325*c8dee2aaSAndroid Build Coastguard Worker pixelPaint.setColor(coverage > 0.f ? SK_ColorGREEN : SK_ColorRED);
326*c8dee2aaSAndroid Build Coastguard Worker pixelRect.inset(0.38f, 0.38f);
327*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(pixelRect, pixelPaint);
328*c8dee2aaSAndroid Build Coastguard Worker }
329*c8dee2aaSAndroid Build Coastguard Worker }
330*c8dee2aaSAndroid Build Coastguard Worker
331*c8dee2aaSAndroid Build Coastguard Worker linePaint.setPathEffect(dashes);
332*c8dee2aaSAndroid Build Coastguard Worker // Draw the inset/outset "infinite" lines
333*c8dee2aaSAndroid Build Coastguard Worker if (fCoverageMode == CoverageMode::kEdgeDistance) {
334*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
335*c8dee2aaSAndroid Build Coastguard Worker if (fEdgeAA[i]) {
336*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorBLUE);
337*c8dee2aaSAndroid Build Coastguard Worker draw_extended_line(canvas, linePaint, outsets[i * 2], outsets[i * 2 + 1]);
338*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorGREEN);
339*c8dee2aaSAndroid Build Coastguard Worker draw_extended_line(canvas, linePaint, insets[i * 2], insets[i * 2 + 1]);
340*c8dee2aaSAndroid Build Coastguard Worker } else {
341*c8dee2aaSAndroid Build Coastguard Worker // Both outset and inset are the same line, so only draw one in cyan
342*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorCYAN);
343*c8dee2aaSAndroid Build Coastguard Worker draw_extended_line(canvas, linePaint, outsets[i * 2], outsets[i * 2 + 1]);
344*c8dee2aaSAndroid Build Coastguard Worker }
345*c8dee2aaSAndroid Build Coastguard Worker }
346*c8dee2aaSAndroid Build Coastguard Worker }
347*c8dee2aaSAndroid Build Coastguard Worker
348*c8dee2aaSAndroid Build Coastguard Worker linePaint.setPathEffect(nullptr);
349*c8dee2aaSAndroid Build Coastguard Worker // What is tessellated using GrQuadPerEdgeAA
350*c8dee2aaSAndroid Build Coastguard Worker if (fCoverageMode == CoverageMode::kGPUMesh) {
351*c8dee2aaSAndroid Build Coastguard Worker SkPath outsetPath;
352*c8dee2aaSAndroid Build Coastguard Worker outsetPath.addPoly(gpuOutset, 4, true);
353*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorBLUE);
354*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(outsetPath, linePaint);
355*c8dee2aaSAndroid Build Coastguard Worker
356*c8dee2aaSAndroid Build Coastguard Worker SkPath insetPath;
357*c8dee2aaSAndroid Build Coastguard Worker insetPath.addPoly(gpuInset, 4, true);
358*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorGREEN);
359*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(insetPath, linePaint);
360*c8dee2aaSAndroid Build Coastguard Worker
361*c8dee2aaSAndroid Build Coastguard Worker SkPaint domainPaint = linePaint;
362*c8dee2aaSAndroid Build Coastguard Worker domainPaint.setStrokeWidth(2.f / kViewScale);
363*c8dee2aaSAndroid Build Coastguard Worker domainPaint.setPathEffect(dashes);
364*c8dee2aaSAndroid Build Coastguard Worker domainPaint.setColor(SK_ColorMAGENTA);
365*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(gpuDomain, domainPaint);
366*c8dee2aaSAndroid Build Coastguard Worker }
367*c8dee2aaSAndroid Build Coastguard Worker
368*c8dee2aaSAndroid Build Coastguard Worker // Draw the edges of the true quad as a solid line
369*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
370*c8dee2aaSAndroid Build Coastguard Worker path.addPoly(fCorners, 4, true);
371*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorBLACK);
372*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, linePaint);
373*c8dee2aaSAndroid Build Coastguard Worker } else {
374*c8dee2aaSAndroid Build Coastguard Worker // Draw the edges of the true quad as a solid *red* line
375*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
376*c8dee2aaSAndroid Build Coastguard Worker path.addPoly(fCorners, 4, true);
377*c8dee2aaSAndroid Build Coastguard Worker linePaint.setColor(SK_ColorRED);
378*c8dee2aaSAndroid Build Coastguard Worker linePaint.setPathEffect(nullptr);
379*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, linePaint);
380*c8dee2aaSAndroid Build Coastguard Worker }
381*c8dee2aaSAndroid Build Coastguard Worker
382*c8dee2aaSAndroid Build Coastguard Worker // Draw the four clickable corners as circles
383*c8dee2aaSAndroid Build Coastguard Worker circlePaint.setColor(valid ? SK_ColorBLACK : SK_ColorRED);
384*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
385*c8dee2aaSAndroid Build Coastguard Worker canvas->drawCircle(fCorners[i], 5.f / kViewScale, circlePaint);
386*c8dee2aaSAndroid Build Coastguard Worker }
387*c8dee2aaSAndroid Build Coastguard Worker }
388*c8dee2aaSAndroid Build Coastguard Worker
389*c8dee2aaSAndroid Build Coastguard Worker bool onChar(SkUnichar) override;
390*c8dee2aaSAndroid Build Coastguard Worker
391*c8dee2aaSAndroid Build Coastguard Worker
392*c8dee2aaSAndroid Build Coastguard Worker protected:
393*c8dee2aaSAndroid Build Coastguard Worker Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
394*c8dee2aaSAndroid Build Coastguard Worker bool onClick(Click*) override;
395*c8dee2aaSAndroid Build Coastguard Worker
396*c8dee2aaSAndroid Build Coastguard Worker private:
397*c8dee2aaSAndroid Build Coastguard Worker class Click;
398*c8dee2aaSAndroid Build Coastguard Worker
399*c8dee2aaSAndroid Build Coastguard Worker enum class CoverageMode {
400*c8dee2aaSAndroid Build Coastguard Worker kArea, kEdgeDistance, kGPUMesh
401*c8dee2aaSAndroid Build Coastguard Worker };
402*c8dee2aaSAndroid Build Coastguard Worker
403*c8dee2aaSAndroid Build Coastguard Worker const SkRect fOuterRect;
404*c8dee2aaSAndroid Build Coastguard Worker SkPoint fCorners[4]; // TL, TR, BR, BL
405*c8dee2aaSAndroid Build Coastguard Worker bool fEdgeAA[4]; // T, R, B, L
406*c8dee2aaSAndroid Build Coastguard Worker CoverageMode fCoverageMode;
407*c8dee2aaSAndroid Build Coastguard Worker
isValid() const408*c8dee2aaSAndroid Build Coastguard Worker bool isValid() const {
409*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
410*c8dee2aaSAndroid Build Coastguard Worker path.addPoly(fCorners, 4, true);
411*c8dee2aaSAndroid Build Coastguard Worker return path.isConvex();
412*c8dee2aaSAndroid Build Coastguard Worker }
413*c8dee2aaSAndroid Build Coastguard Worker
getTessellatedPoints(SkPoint inset[4],SkScalar insetCoverage[4],SkPoint outset[4],SkScalar outsetCoverage[4],SkRect * domain) const414*c8dee2aaSAndroid Build Coastguard Worker void getTessellatedPoints(SkPoint inset[4], SkScalar insetCoverage[4], SkPoint outset[4],
415*c8dee2aaSAndroid Build Coastguard Worker SkScalar outsetCoverage[4], SkRect* domain) const {
416*c8dee2aaSAndroid Build Coastguard Worker // Fixed vertex spec for extracting the picture frame geometry
417*c8dee2aaSAndroid Build Coastguard Worker static const VertexSpec kSpec =
418*c8dee2aaSAndroid Build Coastguard Worker {GrQuad::Type::kGeneral, ColorType::kNone,
419*c8dee2aaSAndroid Build Coastguard Worker GrQuad::Type::kAxisAligned, false, Subset::kNo,
420*c8dee2aaSAndroid Build Coastguard Worker GrAAType::kCoverage, false, IndexBufferOption::kPictureFramed};
421*c8dee2aaSAndroid Build Coastguard Worker static const GrQuad kIgnored(SkRect::MakeEmpty());
422*c8dee2aaSAndroid Build Coastguard Worker
423*c8dee2aaSAndroid Build Coastguard Worker GrQuadAAFlags flags = GrQuadAAFlags::kNone;
424*c8dee2aaSAndroid Build Coastguard Worker flags |= fEdgeAA[0] ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
425*c8dee2aaSAndroid Build Coastguard Worker flags |= fEdgeAA[1] ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
426*c8dee2aaSAndroid Build Coastguard Worker flags |= fEdgeAA[2] ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
427*c8dee2aaSAndroid Build Coastguard Worker flags |= fEdgeAA[3] ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
428*c8dee2aaSAndroid Build Coastguard Worker
429*c8dee2aaSAndroid Build Coastguard Worker GrQuad quad = GrQuad::MakeFromSkQuad(fCorners, SkMatrix::I());
430*c8dee2aaSAndroid Build Coastguard Worker
431*c8dee2aaSAndroid Build Coastguard Worker float vertices[56]; // 2 quads, with x, y, coverage, and geometry domain (7 floats x 8 vert)
432*c8dee2aaSAndroid Build Coastguard Worker skgpu::ganesh::QuadPerEdgeAA::Tessellator tessellator(kSpec, (char*)vertices);
433*c8dee2aaSAndroid Build Coastguard Worker tessellator.append(&quad, nullptr, {1.f, 1.f, 1.f, 1.f},
434*c8dee2aaSAndroid Build Coastguard Worker SkRect::MakeEmpty(), flags);
435*c8dee2aaSAndroid Build Coastguard Worker
436*c8dee2aaSAndroid Build Coastguard Worker // The first quad in vertices is the inset, then the outset, but they
437*c8dee2aaSAndroid Build Coastguard Worker // are ordered TL, BL, TR, BR so un-interleave coverage and re-arrange
438*c8dee2aaSAndroid Build Coastguard Worker inset[0] = {vertices[0], vertices[1]}; // TL
439*c8dee2aaSAndroid Build Coastguard Worker insetCoverage[0] = vertices[2];
440*c8dee2aaSAndroid Build Coastguard Worker inset[3] = {vertices[7], vertices[8]}; // BL
441*c8dee2aaSAndroid Build Coastguard Worker insetCoverage[3] = vertices[9];
442*c8dee2aaSAndroid Build Coastguard Worker inset[1] = {vertices[14], vertices[15]}; // TR
443*c8dee2aaSAndroid Build Coastguard Worker insetCoverage[1] = vertices[16];
444*c8dee2aaSAndroid Build Coastguard Worker inset[2] = {vertices[21], vertices[22]}; // BR
445*c8dee2aaSAndroid Build Coastguard Worker insetCoverage[2] = vertices[23];
446*c8dee2aaSAndroid Build Coastguard Worker
447*c8dee2aaSAndroid Build Coastguard Worker outset[0] = {vertices[28], vertices[29]}; // TL
448*c8dee2aaSAndroid Build Coastguard Worker outsetCoverage[0] = vertices[30];
449*c8dee2aaSAndroid Build Coastguard Worker outset[3] = {vertices[35], vertices[36]}; // BL
450*c8dee2aaSAndroid Build Coastguard Worker outsetCoverage[3] = vertices[37];
451*c8dee2aaSAndroid Build Coastguard Worker outset[1] = {vertices[42], vertices[43]}; // TR
452*c8dee2aaSAndroid Build Coastguard Worker outsetCoverage[1] = vertices[44];
453*c8dee2aaSAndroid Build Coastguard Worker outset[2] = {vertices[49], vertices[50]}; // BR
454*c8dee2aaSAndroid Build Coastguard Worker outsetCoverage[2] = vertices[51];
455*c8dee2aaSAndroid Build Coastguard Worker
456*c8dee2aaSAndroid Build Coastguard Worker *domain = {vertices[52], vertices[53], vertices[54], vertices[55]};
457*c8dee2aaSAndroid Build Coastguard Worker }
458*c8dee2aaSAndroid Build Coastguard Worker };
459*c8dee2aaSAndroid Build Coastguard Worker
460*c8dee2aaSAndroid Build Coastguard Worker class DegenerateQuadSlide::Click : public ClickHandlerSlide::Click {
461*c8dee2aaSAndroid Build Coastguard Worker public:
Click(const SkRect & clamp,int index)462*c8dee2aaSAndroid Build Coastguard Worker Click(const SkRect& clamp, int index)
463*c8dee2aaSAndroid Build Coastguard Worker : fOuterRect(clamp)
464*c8dee2aaSAndroid Build Coastguard Worker , fIndex(index) {}
465*c8dee2aaSAndroid Build Coastguard Worker
doClick(SkPoint points[4])466*c8dee2aaSAndroid Build Coastguard Worker void doClick(SkPoint points[4]) {
467*c8dee2aaSAndroid Build Coastguard Worker if (fIndex >= 0) {
468*c8dee2aaSAndroid Build Coastguard Worker this->drag(&points[fIndex]);
469*c8dee2aaSAndroid Build Coastguard Worker } else {
470*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
471*c8dee2aaSAndroid Build Coastguard Worker this->drag(&points[i]);
472*c8dee2aaSAndroid Build Coastguard Worker }
473*c8dee2aaSAndroid Build Coastguard Worker }
474*c8dee2aaSAndroid Build Coastguard Worker }
475*c8dee2aaSAndroid Build Coastguard Worker
476*c8dee2aaSAndroid Build Coastguard Worker private:
477*c8dee2aaSAndroid Build Coastguard Worker SkRect fOuterRect;
478*c8dee2aaSAndroid Build Coastguard Worker int fIndex;
479*c8dee2aaSAndroid Build Coastguard Worker
drag(SkPoint * point)480*c8dee2aaSAndroid Build Coastguard Worker void drag(SkPoint* point) {
481*c8dee2aaSAndroid Build Coastguard Worker SkPoint delta = fCurr - fPrev;
482*c8dee2aaSAndroid Build Coastguard Worker *point += SkPoint::Make(delta.x() / kViewScale, delta.y() / kViewScale);
483*c8dee2aaSAndroid Build Coastguard Worker point->fX = std::min(fOuterRect.fRight, std::max(point->fX, fOuterRect.fLeft));
484*c8dee2aaSAndroid Build Coastguard Worker point->fY = std::min(fOuterRect.fBottom, std::max(point->fY, fOuterRect.fTop));
485*c8dee2aaSAndroid Build Coastguard Worker }
486*c8dee2aaSAndroid Build Coastguard Worker };
487*c8dee2aaSAndroid Build Coastguard Worker
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey)488*c8dee2aaSAndroid Build Coastguard Worker ClickHandlerSlide::Click* DegenerateQuadSlide::onFindClickHandler(SkScalar x, SkScalar y,
489*c8dee2aaSAndroid Build Coastguard Worker skui::ModifierKey) {
490*c8dee2aaSAndroid Build Coastguard Worker SkPoint inCTM = SkPoint::Make((x - kViewOffset) / kViewScale, (y - kViewOffset) / kViewScale);
491*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
492*c8dee2aaSAndroid Build Coastguard Worker if ((fCorners[i] - inCTM).length() < 10.f / kViewScale) {
493*c8dee2aaSAndroid Build Coastguard Worker return new Click(fOuterRect, i);
494*c8dee2aaSAndroid Build Coastguard Worker }
495*c8dee2aaSAndroid Build Coastguard Worker }
496*c8dee2aaSAndroid Build Coastguard Worker return new Click(fOuterRect, -1);
497*c8dee2aaSAndroid Build Coastguard Worker }
498*c8dee2aaSAndroid Build Coastguard Worker
onClick(ClickHandlerSlide::Click * click)499*c8dee2aaSAndroid Build Coastguard Worker bool DegenerateQuadSlide::onClick(ClickHandlerSlide::Click* click) {
500*c8dee2aaSAndroid Build Coastguard Worker Click* myClick = (Click*) click;
501*c8dee2aaSAndroid Build Coastguard Worker myClick->doClick(fCorners);
502*c8dee2aaSAndroid Build Coastguard Worker return true;
503*c8dee2aaSAndroid Build Coastguard Worker }
504*c8dee2aaSAndroid Build Coastguard Worker
onChar(SkUnichar code)505*c8dee2aaSAndroid Build Coastguard Worker bool DegenerateQuadSlide::onChar(SkUnichar code) {
506*c8dee2aaSAndroid Build Coastguard Worker switch(code) {
507*c8dee2aaSAndroid Build Coastguard Worker case '1':
508*c8dee2aaSAndroid Build Coastguard Worker fEdgeAA[0] = !fEdgeAA[0];
509*c8dee2aaSAndroid Build Coastguard Worker return true;
510*c8dee2aaSAndroid Build Coastguard Worker case '2':
511*c8dee2aaSAndroid Build Coastguard Worker fEdgeAA[1] = !fEdgeAA[1];
512*c8dee2aaSAndroid Build Coastguard Worker return true;
513*c8dee2aaSAndroid Build Coastguard Worker case '3':
514*c8dee2aaSAndroid Build Coastguard Worker fEdgeAA[2] = !fEdgeAA[2];
515*c8dee2aaSAndroid Build Coastguard Worker return true;
516*c8dee2aaSAndroid Build Coastguard Worker case '4':
517*c8dee2aaSAndroid Build Coastguard Worker fEdgeAA[3] = !fEdgeAA[3];
518*c8dee2aaSAndroid Build Coastguard Worker return true;
519*c8dee2aaSAndroid Build Coastguard Worker case 'q':
520*c8dee2aaSAndroid Build Coastguard Worker fCoverageMode = CoverageMode::kArea;
521*c8dee2aaSAndroid Build Coastguard Worker return true;
522*c8dee2aaSAndroid Build Coastguard Worker case 'w':
523*c8dee2aaSAndroid Build Coastguard Worker fCoverageMode = CoverageMode::kEdgeDistance;
524*c8dee2aaSAndroid Build Coastguard Worker return true;
525*c8dee2aaSAndroid Build Coastguard Worker case 'e':
526*c8dee2aaSAndroid Build Coastguard Worker fCoverageMode = CoverageMode::kGPUMesh;
527*c8dee2aaSAndroid Build Coastguard Worker return true;
528*c8dee2aaSAndroid Build Coastguard Worker }
529*c8dee2aaSAndroid Build Coastguard Worker return false;
530*c8dee2aaSAndroid Build Coastguard Worker }
531*c8dee2aaSAndroid Build Coastguard Worker
532*c8dee2aaSAndroid Build Coastguard Worker DEF_SLIDE(return new DegenerateQuadSlide(SkRect::MakeWH(4.f, 4.f));)
533