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