/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file.
*/
// 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;
}
} returntrue;
}
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");
line = line->nextScanline();
}
} #endif private: /* * Scanline mimics a row in the region, nearly. A row in a region is: * [Bottom IntervalCount [L R]... Sentinel] * while a Scanline is * [LastY XCount [L R]... uninitialized] * The two are the same length (which is good), but we have to transmute * the scanline a little when we convert it to a region-row. * * Potentially we could recode this to exactly match the row format, in * which case copyToRgn() could be a single memcpy. Not sure that is worth * the effort.
*/ struct Scanline {
SkRegion::RunType fLastY;
SkRegion::RunType fXCount;
SkRegion::RunType* firstX() { return (SkRegion::RunType*)(this + 1); }
Scanline* nextScanline() { // add final +1 for the x-sentinel return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1);
}
};
SkRegion::RunType* fStorage;
Scanline* fCurrScanline;
Scanline* fPrevScanline; // points at next avialable x[] in fCurrScanline
SkRegion::RunType* fCurrXPtr;
SkRegion::RunType fTop; // first Y value
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 returntrue;
}
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) { return 0;
}
if (clip.isEmpty() || !path.isFinite() || path.isEmpty()) { // This treats non-finite paths 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;
tile.setPath(path.makeTransform(SkMatrix::Translate(-left, -top)),
SkRegion(tileClipBounds));
tile.translate(left, top);
this->op(tile, kUnion_Op);
}
} // 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::getBoundaryPath(SkPath* path) const { // path could safely be nullptr if we're empty, but the caller shouldn't // *know* that
SkASSERT(path);
if (this->isEmpty()) { returnfalse;
}
const SkIRect& bounds = this->getBounds();
if (this->isRect()) {
SkRect r;
r.set(bounds); // this converts the ints to scalars
path->addRect(r); returntrue;
}
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.