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