| /* |
| * Copyright 2021 Google LLC. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/tessellate/Tessellation.h" |
| |
| #include "include/core/SkPath.h" |
| #include "src/core/SkGeometry.h" |
| #include "src/core/SkPathPriv.h" |
| #include "src/gpu/BufferWriter.h" |
| #include "src/gpu/tessellate/CullTest.h" |
| #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h" |
| #include "src/gpu/tessellate/WangsFormula.h" |
| |
| namespace skgpu { |
| |
| namespace { |
| |
| // Writes a new path, chopping as necessary so no verbs require more segments than |
| // kMaxTessellationSegmentsPerCurve. Curves completely outside the viewport are flattened into |
| // lines. |
| class PathChopper { |
| public: |
| PathChopper(const SkMatrix& matrix, const SkRect& viewport) |
| : fCullTest(viewport, matrix) |
| , fVectorXform(matrix) { |
| fPath.setIsVolatile(true); |
| } |
| |
| SkPath path() const { return fPath; } |
| |
| void moveTo(SkPoint p) { fPath.moveTo(p); } |
| void lineTo(SkPoint p1) { fPath.lineTo(p1); } |
| void close() { fPath.close(); } |
| |
| void quadTo(const SkPoint p[3]) { |
| if (!fCullTest.areVisible3(p)) { |
| this->lineTo(p[2]); |
| return; |
| } |
| float n = wangs_formula::quadratic_pow4(kTessellationPrecision, p, fVectorXform); |
| if (n > pow4(kMaxTessellationSegmentsPerCurve)) { |
| SkPoint chops[5]; |
| SkChopQuadAtHalf(p, chops); |
| this->quadTo(chops); |
| this->quadTo(chops + 2); |
| return; |
| } |
| fPath.quadTo(p[1], p[2]); |
| } |
| |
| void conicTo(const SkPoint p[3], float w) { |
| if (!fCullTest.areVisible3(p)) { |
| this->lineTo(p[2]); |
| return; |
| } |
| float n = wangs_formula::conic_pow2(kTessellationPrecision, p, w, fVectorXform); |
| if (n > pow2(kMaxTessellationSegmentsPerCurve)) { |
| SkConic chops[2]; |
| if (!SkConic(p,w).chopAt(.5, chops)) { |
| this->lineTo(p[2]); |
| return; |
| } |
| this->conicTo(chops[0].fPts, chops[0].fW); |
| this->conicTo(chops[1].fPts, chops[1].fW); |
| return; |
| } |
| fPath.conicTo(p[1], p[2], w); |
| } |
| |
| void cubicTo(const SkPoint p[4]) { |
| if (!fCullTest.areVisible4(p)) { |
| this->lineTo(p[3]); |
| return; |
| } |
| float n = wangs_formula::cubic_pow4(kTessellationPrecision, p, fVectorXform); |
| if (n > pow4(kMaxTessellationSegmentsPerCurve)) { |
| SkPoint chops[7]; |
| SkChopCubicAtHalf(p, chops); |
| this->cubicTo(chops); |
| this->cubicTo(chops + 3); |
| return; |
| } |
| fPath.cubicTo(p[1], p[2], p[3]); |
| } |
| |
| private: |
| const CullTest fCullTest; |
| const wangs_formula::VectorXform fVectorXform; |
| SkPath fPath; |
| }; |
| |
| } // namespace |
| |
| SkPath PreChopPathCurves(const SkPath& path, const SkMatrix& matrix, const SkRect& viewport) { |
| PathChopper chopper(matrix, viewport); |
| #ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT |
| for (auto [verb, p, w] : SkPathPriv::Iterate(path)) { |
| #else |
| for (auto item : SkPathPriv::Iterate(path)) { |
| STRUCTURED_BINDING_3(verb, p, w, std::move(item)); |
| #endif |
| switch (verb) { |
| case SkPathVerb::kMove: |
| chopper.moveTo(p[0]); |
| break; |
| case SkPathVerb::kLine: |
| chopper.lineTo(p[1]); |
| break; |
| case SkPathVerb::kQuad: |
| chopper.quadTo(p); |
| break; |
| case SkPathVerb::kConic: |
| chopper.conicTo(p, *w); |
| break; |
| case SkPathVerb::kCubic: |
| chopper.cubicTo(p); |
| break; |
| case SkPathVerb::kClose: |
| chopper.close(); |
| break; |
| } |
| } |
| return chopper.path(); |
| } |
| |
| } // namespace skgpu |