/* * 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.
*/
// This is a simple data structure which is like a STArray<N,T,true>, except that: // - It does not initialize memory. // - It does not distinguish between reserved space and initialized space. // - resizeToAtLeast() instead of resize() // - Uses sk_realloc_throw() // - Can never be made smaller. // Measurement: for the `region_union_16` benchmark, this is 6% faster. class RunArray { public:
RunArray() { fPtr = fStack; } #ifdef SK_DEBUG int count() const { return fCount; } #endif
SkRegionPriv::RunType& operator[](int i) {
SkASSERT((unsigned)i < (unsigned)fCount); return fPtr[i];
} /** Resize the array to a size greater-than-or-equal-to count. */ void resizeToAtLeast(int count) { if (count > fCount) { // leave at least 50% extra space for future growth (unless adding would overflow)
SkSafeMath safe; int newCount = safe.addInt(count, count >> 1);
count = safe ? newCount : SK_MaxS32;
fMalloc.realloc(count); if (fPtr == fStack) {
memcpy(fMalloc.get(), fStack, fCount * sizeof(SkRegionPriv::RunType));
}
fPtr = fMalloc.get();
fCount = count;
}
} private:
SkRegionPriv::RunType fStack[kRunArrayStackCount];
AutoTMalloc<SkRegionPriv::RunType> fMalloc; int fCount = kRunArrayStackCount;
SkRegionPriv::RunType* fPtr; // non-owning pointer
};
/* Pass in the beginning with the intervals. * We back up 1 to read the interval-count. * Return the beginning of the next scanline (i.e. the next Y-value)
*/ static SkRegionPriv::RunType* skip_intervals(const SkRegionPriv::RunType runs[]) { int intervals = runs[-1]; #ifdef SK_DEBUG if (intervals > 0) {
SkASSERT(runs[0] < runs[1]);
SkASSERT(runs[1] < SkRegion_kRunTypeSentinel);
} else {
SkASSERT(0 == intervals);
SkASSERT(SkRegion_kRunTypeSentinel == runs[0]);
} #endif
runs += intervals * 2 + 1; returnconst_cast<SkRegionPriv::RunType*>(runs);
}
bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
SkIRect* bounds) {
assert_sentinel(runs[0], false); // top
SkASSERT(count >= kRectRegionRuns);
if (count == kRectRegionRuns) {
assert_sentinel(runs[1], false); // bottom
SkASSERT(1 == runs[2]);
assert_sentinel(runs[3], false); // left
assert_sentinel(runs[4], false); // right
assert_sentinel(runs[5], true);
assert_sentinel(runs[6], true);
SkRegion::SkRegion(const SkRegion& src) {
fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
this->setRegion(src);
}
SkRegion::SkRegion(const SkIRect& rect) {
fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
this->setRect(rect);
}
SkRegion::~SkRegion() {
this->freeRuns();
}
void SkRegion::freeRuns() { if (this->isComplex()) {
SkASSERT(fRunHead->fRefCnt >= 1); if (--fRunHead->fRefCnt == 0) {
sk_free(fRunHead);
}
}
}
void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
}
// trim off any empty spans from the top and bottom // weird I should need this, perhaps op() could be smarter... if (count > kRectRegionRuns) {
RunType* stop = runs + count;
assert_sentinel(runs[0], false); // top
assert_sentinel(runs[1], false); // bottom // runs[2] is uncomputed intervalCount
if (runs[3] == SkRegion_kRunTypeSentinel) { // should be first left...
runs += 3; // skip empty initial span
runs[0] = runs[-2]; // set new top to prev bottom
assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
assert_sentinel(runs[2], false); // intervalcount
assert_sentinel(runs[3], false); // left
assert_sentinel(runs[4], false); // right
}
// now check for a trailing empty span if (stop[-5] == SkRegion_kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
stop[-4] = SkRegion_kRunTypeSentinel; // kill empty last span
stop -= 3;
assert_sentinel(stop[-1], true); // last y-sentinel
assert_sentinel(stop[-2], true); // last x-sentinel
assert_sentinel(stop[-3], false); // last right
assert_sentinel(stop[-4], false); // last left
assert_sentinel(stop[-5], false); // last interval-count
assert_sentinel(stop[-6], false); // last bottom
}
count = (int)(stop - runs);
}
SkASSERT(count >= kRectRegionRuns);
if (SkRegion::RunsAreARect(runs, count, &fBounds)) { return this->setRect(fBounds);
}
// if we get here, we need to become a complex region
// must call this before we can write directly into runs() // in case we are sharing the buffer with another region (copy on write)
fRunHead = fRunHead->ensureWritable();
memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
fRunHead->computeRunBounds(&fBounds);
// Our computed bounds might be too large, so we have to check here. if (fBounds.isEmpty()) { return this->setEmpty();
}
if (!fBounds.contains(x, y)) { returnfalse;
} if (this->isRect()) { returntrue;
}
SkASSERT(this->isComplex());
const RunType* runs = fRunHead->findScanline(y);
// Skip the Bottom and IntervalCount
runs += 2;
// Just walk this scanline, checking each interval. The X-sentinel will // appear as a left-inteval (runs[0]) and should abort the search. // // We could do a bsearch, using interval-count (runs[1]), but need to time // when that would be worthwhile. // for (;;) { if (x < runs[0]) { break;
} if (x < runs[1]) { returntrue;
}
runs += 2;
} returnfalse;
}
// this catches empties and rects being equal if (ah == bh) { returntrue;
} // now we insist that both are complex (but different ptrs) if (!this->isComplex() || !b.isComplex()) { returnfalse;
} return ah->fRunCount == bh->fRunCount &&
!memcmp(ah->readonly_runs(), bh->readonly_runs(),
ah->fRunCount * sizeof(SkRegion::RunType));
}
// Return a (new) offset such that when applied (+=) to min and max, we don't overflow/underflow static int32_t pin_offset_s32(int32_t min, int32_t max, int32_t offset) {
SkASSERT(min <= max); const int32_t lo = -SK_MaxS32-1,
hi = +SK_MaxS32; if ((int64_t)min + offset < lo) { offset = lo - min; } if ((int64_t)max + offset > hi) { offset = hi - max; } return offset;
}
void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
SkDEBUGCODE(SkRegionPriv::Validate(*this));
if (nullptr == dst) { return;
} if (this->isEmpty()) {
dst->setEmpty(); return;
} // pin dx and dy so we don't overflow our existing bounds
dx = pin_offset_s32(fBounds.fLeft, fBounds.fRight, dx);
dy = pin_offset_s32(fBounds.fTop, fBounds.fBottom, dy);
staticint operate_on_span(const SkRegionPriv::RunType a_runs[], const SkRegionPriv::RunType b_runs[],
RunArray* array, int dstOffset, int min, int max) { // This is a worst-case for this span plus two for TWO terminating sentinels.
array->resizeToAtLeast(
dstOffset + distance_to_sentinel(a_runs) + distance_to_sentinel(b_runs) + 2);
SkRegionPriv::RunType* dst = &(*array)[dstOffset]; // get pointer AFTER resizing.
spanRec rec; bool firstInterval = true;
rec.init(a_runs, b_runs);
while (!rec.done()) {
rec.next();
int left = rec.fLeft; int rite = rec.fRite;
// add left,rite to our dst buffer (checking for coincidence if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
left < rite) { // skip if equal if (firstInterval || *(dst - 1) < left) {
*dst++ = (SkRegionPriv::RunType)(left);
*dst++ = (SkRegionPriv::RunType)(rite);
firstInterval = false;
} else { // update the right edge
*(dst - 1) = (SkRegionPriv::RunType)(rite);
}
}
}
SkASSERT(dst < &(*array)[array->count() - 1]);
*dst++ = SkRegion_kRunTypeSentinel; return dst - &(*array)[0];
}
#ifdefined _WIN32 #pragma warning ( pop ) #endif
staticconststruct {
uint8_t fMin;
uint8_t fMax;
} gOpMinMax[] = {
{ 1, 1 }, // Difference
{ 3, 3 }, // Intersection
{ 1, 3 }, // Union
{ 1, 2 } // XOR
}; // need to ensure that the op enum lines up with our minmax array
static_assert(0 == SkRegion::kDifference_Op, "");
static_assert(1 == SkRegion::kIntersect_Op, "");
static_assert(2 == SkRegion::kUnion_Op, "");
static_assert(3 == SkRegion::kXOR_Op, "");
class RgnOper { public:
RgnOper(int top, RunArray* array, SkRegion::Op op)
: fMin(gOpMinMax[op].fMin)
, fMax(gOpMinMax[op].fMax)
, fArray(array)
, fTop((SkRegionPriv::RunType)top) // just a first guess, we might update this
{ SkASSERT((unsigned)op <= 3); }
void addSpan(int bottom, const SkRegionPriv::RunType a_runs[], const SkRegionPriv::RunType b_runs[]) { // skip X values and slots for the next Y+intervalCount int start = fPrevDst + fPrevLen + 2; // start points to beginning of dst interval int stop = operate_on_span(a_runs, b_runs, fArray, start, fMin, fMax);
size_t len = SkToSizeT(stop - start);
SkASSERT(len >= 1 && (len & 1) == 1);
SkASSERT(SkRegion_kRunTypeSentinel == (*fArray)[stop - 1]);
// Assert memcmp won't exceed fArray->count().
SkASSERT(fArray->count() >= SkToInt(start + len - 1)); if (fPrevLen == len &&
(1 == len || !memcmp(&(*fArray)[fPrevDst],
&(*fArray)[start],
(len - 1) * sizeof(SkRegionPriv::RunType)))) { // update Y value
(*fArray)[fPrevDst - 2] = (SkRegionPriv::RunType)bottom;
} else { // accept the new span if (len == 1 && fPrevLen == 0) {
fTop = (SkRegionPriv::RunType)bottom; // just update our bottom
} else {
(*fArray)[start - 2] = (SkRegionPriv::RunType)bottom;
(*fArray)[start - 1] = SkToS32(len >> 1);
fPrevDst = start;
fPrevLen = len;
}
}
}
private:
RunArray* fArray; int fStartDst = 0; int fPrevDst = 1;
size_t fPrevLen = 0; // will never match a length from operate_on_span
SkRegionPriv::RunType fTop;
};
// want a unique value to signal that we exited due to quickExit #define QUICK_EXIT_TRUE_COUNT (-1)
staticint operate(const SkRegionPriv::RunType a_runs[], const SkRegionPriv::RunType b_runs[],
RunArray* dst,
SkRegion::Op op, bool quickExit) { const SkRegionPriv::RunType gEmptyScanline[] = {
0, // fake bottom value
0, // zero intervals
SkRegion_kRunTypeSentinel, // just need a 2nd value, since spanRec.init() reads 2 values, even // though if the first value is the sentinel, it ignores the 2nd value. // w/o the 2nd value here, we might read uninitialized memory. // This happens when we are using gSentinel, which is pointing at // our sentinel value.
0
}; const SkRegionPriv::RunType* const gSentinel = &gEmptyScanline[2];
int a_top = *a_runs++; int a_bot = *a_runs++; int b_top = *b_runs++; int b_bot = *b_runs++;
a_runs += 1; // skip the intervalCount;
b_runs += 1; // skip the intervalCount;
// Now a_runs and b_runs to their intervals (or sentinel)
/* Given count RunTypes in a complex region, return the worst case number of logical intervals that represents (i.e. number of rects that would be returned from the iterator).
We could just return count/2, since there must be at least 2 values per interval, but we can first trim off the const overhead of the initial TOP value, plus the final BOTTOM + 2 sentinels.
*/ #if 0 // UNUSED staticint count_to_intervals(int count) {
SkASSERT(count >= 6); // a single rect is 6 values return (count - 4) >> 1;
} #endif
if (kReplace_Op == op) { return setRegionCheck(result, rgnbOrig);
}
// swith to using pointers, so we can swap them as needed const SkRegion* rgna = &rgnaOrig; const SkRegion* rgnb = &rgnbOrig; // after this point, do not refer to rgnaOrig or rgnbOrig!!!
// collaps difference and reverse-difference into just difference if (kReverseDifference_Op == op) { using std::swap;
swap(rgna, rgnb);
op = kDifference_Op;
}
staticbool validate_run_count(int ySpanCount, int intervalCount, int runCount) { // return 2 + 3 * ySpanCount + 2 * intervalCount; if (ySpanCount < 1 || intervalCount < 2) { returnfalse;
}
SkSafeMath safeMath; int sum = 2;
sum = safeMath.addInt(sum, ySpanCount);
sum = safeMath.addInt(sum, ySpanCount);
sum = safeMath.addInt(sum, ySpanCount);
sum = safeMath.addInt(sum, intervalCount);
sum = safeMath.addInt(sum, intervalCount); return safeMath && sum == runCount;
}
// Validate that a memory sequence is a valid region. // Try to check all possible errors. // never read beyond &runs[runCount-1]. staticbool validate_run(const int32_t* runs, int runCount, const SkIRect& givenBounds,
int32_t ySpanCount,
int32_t intervalCount) { // Region Layout: // Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel if (!validate_run_count(SkToInt(ySpanCount), SkToInt(intervalCount), runCount)) { returnfalse;
}
SkASSERT(runCount >= 7); // 7==SkRegion::kRectRegionRuns // quick safety check: if (runs[runCount - 1] != SkRegion_kRunTypeSentinel ||
runs[runCount - 2] != SkRegion_kRunTypeSentinel) { returnfalse;
} const int32_t* const end = runs + runCount;
SkIRect bounds = {0, 0, 0 ,0}; // calulated bounds
SkIRect rect = {0, 0, 0, 0}; // current rect
rect.fTop = *runs++; if (rect.fTop == SkRegion_kRunTypeSentinel) { returnfalse; // no rect can contain SkRegion_kRunTypeSentinel
} if (rect.fTop != givenBounds.fTop) { returnfalse; // Must not begin with empty span that does not contribute to bounds.
} do {
--ySpanCount; if (ySpanCount < 0) { returnfalse; // too many yspans
}
rect.fBottom = *runs++; if (rect.fBottom == SkRegion_kRunTypeSentinel) { returnfalse;
} if (rect.fBottom > givenBounds.fBottom) { returnfalse; // Must not end with empty span that does not contribute to bounds.
} if (rect.fBottom <= rect.fTop) { returnfalse; // y-intervals must be ordered; rects must be non-empty.
}
void SkRegion::Iterator::next() { if (fDone) { return;
}
if (fRuns == nullptr) { // rect case
fDone = true; return;
}
const RunType* runs = fRuns;
if (runs[0] < SkRegion_kRunTypeSentinel) { // valid X value
fRect.fLeft = runs[0];
fRect.fRight = runs[1];
runs += 2;
} else { // we're at the end of a line
runs += 1; if (runs[0] < SkRegion_kRunTypeSentinel) { // valid Y value int intervals = runs[1]; if (0 == intervals) { // empty line
fRect.fTop = runs[0];
runs += 3;
} else {
fRect.fTop = fRect.fBottom;
}
SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, int right) {
SkDEBUGCODE(SkRegionPriv::Validate(rgn));
const SkIRect& r = rgn.getBounds();
fDone = true; if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
right > r.fLeft && left < r.fRight) { if (rgn.isRect()) { if (left < r.fLeft) {
left = r.fLeft;
} if (right > r.fRight) {
right = r.fRight;
}
fLeft = left;
fRight = right;
fRuns = nullptr; // means we're a rect, not a rgn
fDone = false;
} else { const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
runs += 2; // skip Bottom and IntervalCount for (;;) { // runs[0..1] is to the right of the span, so we're done if (runs[0] >= right) { break;
} // runs[0..1] is to the left of the span, so continue if (runs[1] <= left) {
runs += 2; continue;
} // runs[0..1] intersects the span
fRuns = runs;
fLeft = left;
fRight = right;
fDone = false; break;
}
}
}
}
if (fRuns == nullptr) { // we're a rect
fDone = true; // ok, now we're done if (left) {
*left = fLeft;
} if (right) {
*right = fRight;
} returntrue; // this interval is legal
}
const SkRegion::RunType* runs = fRuns;
if (runs[0] >= fRight) {
fDone = true; returnfalse;
}
staticvoid visit_pairs(int pairCount, int y, const int32_t pairs[], const std::function<void(const SkIRect&)>& visitor) { for (int i = 0; i < pairCount; ++i) {
visitor({ pairs[0], y, pairs[1], y + 1 });
pairs += 2;
}
}
void SkRegionPriv::VisitSpans(const SkRegion& rgn, const std::function<void(const SkIRect&)>& visitor) { if (rgn.isEmpty()) { return;
} if (rgn.isRect()) {
visitor(rgn.getBounds());
} else { const int32_t* p = rgn.fRunHead->readonly_runs();
int32_t top = *p++;
int32_t bot = *p++; do { int pairCount = *p++; if (pairCount == 1) {
visitor({ p[0], top, p[1], bot });
p += 2;
} elseif (pairCount > 1) { // we have to loop repeated in Y, sending each interval in Y -> X order for (int y = top; y < bot; ++y) {
visit_pairs(pairCount, y, p, visitor);
}
p += pairCount * 2;
}
assert_sentinel(*p, true);
p += 1; // skip sentinel
// read next bottom or sentinel
top = bot;
bot = *p++;
} while (!SkRegionValueIsSentinel(bot));
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.42 Sekunden
(vorverarbeitet)
¤
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.