static uint32_t next_pathdata_unique_id() {
constexpr int kHighBitsToMakeRoomForFillType = 2;
static std::atomic<int32_t> nextID{1};
uint32_t id; do {
id = nextID.fetch_add(1, std::memory_order_relaxed); // clear the high bits to make room for filltype
id <<= kHighBitsToMakeRoomForFillType;
id >>= kHighBitsToMakeRoomForFillType;
} while (id == 0); return id;
}
class SkSafeAccumulator {
public:
SkSafeAccumulator(size_t n = 0) : fTotal(n) {}
// We must begin with a Move (unless we're empty) if (vbs.front() != SkPathVerb::kMove) { returnfalse;
}
SkPathVerb prev = SkPathVerb::kMove;
size_t point_count = 1,
conic_count = 0;
// Check that we have a valid sequence. for (size_t i = 1; i < vbs.size(); ++i) {
SkPathVerb curr = vbs[i];
if (static_cast<unsigned>(curr) > static_cast<unsigned>(SkPathVerb::kLast_Verb)) { returnfalse;
}
// Previous verb Valid next verb // ----------------------------------------- // Move --> not Move // Line/Quad/Conic/Cubic --> * // Close --> Move // if (prev == SkPathVerb::kMove && curr == SkPathVerb::kMove) { returnfalse;
} if (prev == SkPathVerb::kClose && curr != SkPathVerb::kMove) { returnfalse;
}
if (pts.size() != point_count || conics.size() != conic_count){ returnfalse;
}
for (auto w : conics) { if (!valid_conic_weight(w)) { returnfalse;
}
}
return true;
}
// Handing in debugging, to set a break-point here if you want to know why we // failed to create a pathdta // staticvoid report_pathdata_make_failure(constchar reason[]) { // ` SkDEBUGF("SkPathData::Make failed: %s\n", reason);
}
// This just sets-up the spans to point inside our allocation //
SkPathData::SkPathData(size_t npts, size_t nvbs, size_t ncns)
: fUniqueID(next_pathdata_unique_id())
, fConvexity((uint8_t)SkPathConvexity::kUnknown)
, fType(SkPathIsAType::kGeneral)
{
SkASSERT((npts == 0 && nvbs == 0 && ncns == 0) ||
(npts != 0 && nvbs != 0));
SkPathData::~SkPathData() { // We will implicitly call our IDChangeList here, notifying them that we are // being dstroyed.
SkDEBUGCODE(fUniqueID = 0xEEEEEEEE;)
}
void SkPathData::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const { // our empty singleton is never deleted, so we don't want to add any listeners to it. if (this != SkPathData::PeekEmptySingleton()) { // this method on the list is thread-safe
fGenIDChangeListeners.add(std::move(listener));
}
}
// NOTE: This only allocates and initializes the span pointers (points, verbs), // it does NOT set the other fields
sk_sp<SkPathData> SkPathData::Alloc(size_t npts, size_t nvbs, size_t ncns) {
SkSafeAccumulator accum(sizeof(SkPathData));
if (auto size = accum.total()) { // This trick allows us to just make one allocation, for us and our buffer // rather than allocating us and also allocating the buffer (via malloc or new[]) // We have the corresponding operator delete() specified as well. void* storage = ::operatornew (*size);
sk_sp<SkPathData> path(new (storage) SkPathData(npts, nvbs, ncns));
if (mx.hasPerspective()) { return SkPathBuilder()
.addRaw(src, SkPathBuilder::Reserve::kExact)
.transform(mx)
.detachData();
}
// Allocate our result, so we can map the new points directly into it auto result = Alloc(src.points().size(), src.verbs().size(), src.conics().size());
mx.mapPoints(result->fPoints, src.points());
SkSpanPriv::Copy(result->fConics, src.conics());
SkSpanPriv::Copy(result->fVerbs, src.verbs());
std::optional<SkRect> transformedBounds; if (mx.rectStaysRect()) { // safe us from having to compute our transformed bounds in finishInit()
transformedBounds = mx.mapRect(src.bounds()); if (!transformedBounds.value().isFinite()) {
report_pathdata_make_failure("transform created non-finite bounds"); return nullptr;
}
}
if (!result->finishInit(transformedBounds, src.fSegmentMask)) { return nullptr;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.