| /* |
| * Copyright 2026 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #ifndef skgpu_graphite_sparse_strips_Oracle_DEFINED |
| #define skgpu_graphite_sparse_strips_Oracle_DEFINED |
| |
| #include "include/core/SkPath.h" |
| #include "include/core/SkPoint.h" |
| #include "src/core/SkVx.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <vector> |
| |
| namespace skgpu::graphite { |
| |
| // Used as the source of ground truth for testing the entire SparseStrips pipeline end to end. |
| // Builds the subsample windings for a single pixel row by raycasting at each of the 8 subsample |
| // locations in the SparseStrips MSAA pattern. |
| class ScanlineOracle8x { |
| public: |
| struct CurveIntersection { |
| skvx::float8 x; |
| skvx::int8 dir; // +1 for downward, -1 for upward, 0 for no hit in this lane |
| }; |
| |
| struct RowWindingInterval { |
| int x; |
| skvx::int8 winding; |
| }; |
| |
| static skvx::int8 GetOracleWinding(int px, const std::vector<RowWindingInterval>& intervals) { |
| if (intervals.empty() || px < intervals.front().x || px >= intervals.back().x) { |
| return skvx::int8(0); |
| } |
| auto it = std::upper_bound( |
| intervals.begin(), |
| intervals.end(), |
| px, |
| [](int val, const RowWindingInterval& interval) { return val < interval.x; }); |
| if (it == intervals.begin()) { |
| return skvx::int8(0); |
| } |
| return std::prev(it)->winding; |
| } |
| |
| ScanlineOracle8x() = default; |
| explicit ScanlineOracle8x(const SkPath& path) : fPath(path) {} |
| |
| void setPath(const SkPath& path) { |
| if (fPath != path) { |
| fPath = path; |
| fBuilt = false; |
| fCurrentRow = -1; |
| fHits.clear(); |
| fIntervals.clear(); |
| } |
| } |
| |
| const SkPath& path() const { return fPath; } |
| |
| void buildRow(int py); |
| void buildRow(int py, const SkPath& path); |
| |
| skvx::int8 evaluatePixelWinding(float px) const; |
| skvx::int8 evaluate(float px) const { return this->evaluatePixelWinding(px); } |
| |
| skvx::int8 evaluate(SkPoint pt); |
| skvx::int8 evaluate(SkPoint pt, const SkPath& path); |
| |
| bool isBuilt() const { return fBuilt; } |
| int currentRow() const { return fCurrentRow; } |
| const std::vector<CurveIntersection>& hits() const { |
| SkASSERT(fBuilt); |
| return fHits; |
| } |
| const std::vector<RowWindingInterval>& rowIntervals() const { |
| SkASSERT(fBuilt); |
| return fIntervals; |
| } |
| |
| std::vector<RowWindingInterval> buildRowIntervals(int py) { |
| this->buildRow(py); |
| return fIntervals; |
| } |
| std::vector<RowWindingInterval> buildRowIntervals(int py, const SkPath& path) { |
| this->buildRow(py, path); |
| return fIntervals; |
| } |
| |
| private: |
| void intersectLine(const SkPoint pts[2], const skvx::float8& targetY); |
| void intersectQuad(const SkPoint pts[3], const skvx::float8& targetY); |
| void intersectConic(const SkPoint pts[3], float weight, const skvx::float8& targetY); |
| void intersectCubic(const SkPoint pts[4], const skvx::float8& targetY); |
| void collectPathIntersections(const SkPath& path, const skvx::float8& targetY); |
| |
| SkPath fPath; |
| int fCurrentRow = -1; |
| bool fBuilt = false; |
| std::vector<CurveIntersection> fHits; |
| std::vector<RowWindingInterval> fIntervals; |
| }; |
| |
| } // namespace skgpu::graphite |
| |
| #endif // skgpu_graphite_sparse_strips_Oracle_DEFINED |