// The rgnbuilder caller *seems* to pass short counts, possible often seens early failure, so // we may not want to promote this to a "std" routine just yet. staticbool sk_memeq32(const int32_t* SK_RESTRICT a, const int32_t* SK_RESTRICT b, int count) { for (int i = 0; i < count; ++i) { if (a[i] != b[i]) { returnfalse;
}
} return true;
}
class SkRgnBuilder : public SkBlitter {
public:
SkRgnBuilder();
~SkRgnBuilder() override;
// returns true if it could allocate the working storage needed bool init(int maxHeight, int maxTransitions, bool pathIsInverse);
void done() { if (fCurrScanline != nullptr) {
fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX())); if (!this->collapsWithPrev()) { // flush the last line
fCurrScanline = fCurrScanline->nextScanline();
}
}
}
int computeRunCount() const; void copyToRect(SkIRect*) const; void copyToRgn(SkRegion::RunType runs[]) const;
void blitH(int x, int y, int width) override; void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override {
SkDEBUGFAIL("blitAntiH not implemented");
}
#ifdef SK_DEBUG void dump() const {
SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
Scanline* line = (Scanline*)fStorage; while (line < fCurrScanline) {
SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount); for (int i = 0; i < line->fXCount; i++) {
SkDebugf(" %d", line->firstX()[i]);
}
SkDebugf("\n");
bool SkRgnBuilder::init(int maxHeight, int maxTransitions, bool pathIsInverse) { if ((maxHeight | maxTransitions) < 0) { returnfalse;
}
SkSafeMath safe;
if (pathIsInverse) { // allow for additional X transitions to "invert" each scanline // [ L' ... normal transitions ... R' ] //
maxTransitions = safe.addInt(maxTransitions, 2);
}
// compute the count with +1 and +3 slop for the working buffer
size_t count = safe.mul(safe.addInt(maxHeight, 1), safe.addInt(3, maxTransitions));
if (pathIsInverse) { // allow for two "empty" rows for the top and bottom // [ Y, 1, L, R, S] == 5 (*2 for top and bottom)
count = safe.add(count, 10);
}
if (!safe || !SkTFitsIn<int32_t>(count)) { returnfalse;
}
fStorageCount = SkToS32(count);
fCurrScanline = nullptr; // signal empty collection
fPrevScanline = nullptr; // signal first scanline return true;
}
void SkRgnBuilder::blitH(int x, int y, int width) { if (fCurrScanline == nullptr) { // first time
fTop = (SkRegion::RunType)(y);
fCurrScanline = (Scanline*)fStorage;
fCurrScanline->fLastY = (SkRegion::RunType)(y);
fCurrXPtr = fCurrScanline->firstX();
} else {
SkASSERT(y >= fCurrScanline->fLastY);
if (y > fCurrScanline->fLastY) { // if we get here, we're done with fCurrScanline
fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
int prevLastY = fCurrScanline->fLastY; if (!this->collapsWithPrev()) {
fPrevScanline = fCurrScanline;
fCurrScanline = fCurrScanline->nextScanline();
} if (y - 1 > prevLastY) { // insert empty run
fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
fCurrScanline->fXCount = 0;
fCurrScanline = fCurrScanline->nextScanline();
} // setup for the new curr line
fCurrScanline->fLastY = (SkRegion::RunType)(y);
fCurrXPtr = fCurrScanline->firstX();
}
} // check if we should extend the current run, or add a new one if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
} else {
fCurrXPtr[0] = (SkRegion::RunType)(x);
fCurrXPtr[1] = (SkRegion::RunType)(x + width);
fCurrXPtr += 2;
}
SkASSERT(fCurrXPtr - fStorage < fStorageCount);
}
int SkRgnBuilder::computeRunCount() const { if (fCurrScanline == nullptr) { return0;
}
constauto raw = SkPathPriv::Raw(path, SkResolveConvexity::kYes);
if (clip.isEmpty() || !raw.has_value() || path.isEmpty()) { // This treats non-finite paths (no raw) as empty as well, so this returns empty or 'clip' // if it's inverse-filled. If clip is also empty, path's fill type doesn't really matter // and this region ends up empty. return check_inverse_on_empty_return(this, path, clip);
}
// Our builder is very fragile, and can't be called with spans/rects out of Y->X order. // To ensure this, we only "fill" clipped to a rect (the clip's bounds), and if the // clip is more complex than that, we just post-intersect the result with the clip. const SkIRect clipBounds = clip.getBounds(); if (clip.isComplex()) { if (!this->setPath(path, SkRegion(clipBounds))) { returnfalse;
} return this->op(clip, kIntersect_Op);
}
// SkScan::FillPath has limits on the coordinate range of the clipping SkRegion. If it's too // big, tile the clip bounds and union the pieces back together. if (SkScan::PathRequiresTiling(clipBounds)) { static constexpr int kTileSize = 32767 >> 1; // Limit so coords can fit into SkFixed (16.16) const SkIRect pathBounds = path.getBounds().roundOut();
this->setEmpty();
// Note: With large integers some intermediate calculations can overflow, but the // end results will still be in integer range. Using int64_t for the intermediate // values will handle this situation. for (int64_t top = clipBounds.fTop; top < clipBounds.fBottom; top += kTileSize) {
int64_t bot = std::min(top + kTileSize, (int64_t)clipBounds.fBottom); for (int64_t left = clipBounds.fLeft; left < clipBounds.fRight; left += kTileSize) {
int64_t right = std::min(left + kTileSize, (int64_t)clipBounds.fRight);
// Shift coordinates so the top left is (0,0) during scan conversion and then // translate the SkRegion afterwards.
tileClipBounds.offset(-left, -top);
SkASSERT(!SkScan::PathRequiresTiling(tileClipBounds));
SkRegion tile; if (auto newpath = path.tryMakeOffset(-left, -top)) {
tile.setPath(*newpath, SkRegion(tileClipBounds));
tile.translate(left, top);
this->op(tile, kUnion_Op);
} else { returnfalse;
}
}
} // During tiling we only applied the bounds of the tile, now that we have a full SkRegion, // apply the original clip. return this->op(clip, kIntersect_Op);
}
// compute worst-case rgn-size for the path int pathTop, pathBot; int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot); if (0 == pathTransitions) { return check_inverse_on_empty_return(this, path, clip);
}
int clipTop, clipBot; int clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
int top = std::max(pathTop, clipTop); int bot = std::min(pathBot, clipBot); if (top >= bot) { return check_inverse_on_empty_return(this, path, clip);
}
SkRgnBuilder builder;
if (!builder.init(bot - top,
std::max(pathTransitions, clipTransitions),
path.isInverseFillType())) { // can't allocate working space, so return false return this->setEmpty();
}
bool SkRegion::addBoundaryPath(SkPathBuilder* builder) const { // path could safely be nullptr if we're empty, but the caller shouldn't // *know* that
SkASSERT(builder);
if (this->isEmpty()) { returnfalse;
}
const SkIRect& bounds = this->getBounds();
if (this->isRect()) {
SkRect r;
r.set(bounds); // this converts the ints to scalars
builder->addRect(r); return true;
}
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.