blob: e7834d481bcc921097f83b4e1d2bedaac3d00e1a [file]
/*
* 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