record yspancount and intervalcount in regions
Review URL: https://codereview.appspot.com/6132055 git-svn-id: http://skia.googlecode.com/svn/trunk@3808 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
ed4155d610
commit
af7e6943b7
@ -382,15 +382,18 @@ private:
|
||||
};
|
||||
|
||||
friend class android::Region; // needed for marshalling efficiently
|
||||
void allocateRuns(int count); // allocate space for count runs
|
||||
|
||||
struct RunHead;
|
||||
|
||||
// allocate space for count runs
|
||||
void allocateRuns(int count, int ySpanCount, int intervalCount);
|
||||
void allocateRuns(const RunHead& src);
|
||||
|
||||
SkIRect fBounds;
|
||||
RunHead* fRunHead;
|
||||
|
||||
void freeRuns();
|
||||
const RunType* getRuns(RunType tmpStorage[], int* count) const;
|
||||
const RunType* getRuns(RunType tmpStorage[], int* intervals) const;
|
||||
bool setRuns(RunType runs[], int count);
|
||||
|
||||
int count_runtype_values(int* itop, int* ibot) const;
|
||||
@ -399,7 +402,7 @@ private:
|
||||
RunType runs[kRectRegionRuns]);
|
||||
// returns true if runs are just a rect
|
||||
static bool ComputeRunBounds(const RunType runs[], int count,
|
||||
SkIRect* bounds);
|
||||
SkIRect*, int* ySpanCount, int* intervalCount);
|
||||
|
||||
/**
|
||||
* If the last arg is null, just return if the result is non-empty,
|
||||
|
@ -27,7 +27,8 @@ static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[]) {
|
||||
|
||||
// returns true if runs are just a rect
|
||||
bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count,
|
||||
SkIRect* bounds) {
|
||||
SkIRect* bounds, int* ySpanCountPtr,
|
||||
int* intervalCountPtr) {
|
||||
assert_sentinel(runs[0], false); // top
|
||||
|
||||
if (count == kRectRegionRuns) {
|
||||
@ -41,21 +42,36 @@ bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count,
|
||||
SkASSERT(runs[2] < runs[3]); // valid width
|
||||
|
||||
bounds->set(runs[2], runs[0], runs[3], runs[1]);
|
||||
*ySpanCountPtr = 1;
|
||||
*intervalCountPtr = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
int left = SK_MaxS32;
|
||||
int rite = SK_MinS32;
|
||||
int bot;
|
||||
int ySpanCount = 0;
|
||||
int intervalCount = 0;
|
||||
|
||||
bounds->fTop = *runs++;
|
||||
do {
|
||||
bot = *runs++;
|
||||
ySpanCount += 1;
|
||||
|
||||
if (*runs < SkRegion::kRunTypeSentinel) {
|
||||
if (left > *runs) {
|
||||
left = *runs;
|
||||
}
|
||||
|
||||
const RunType* prevRuns = runs;
|
||||
runs = skip_scanline(runs);
|
||||
// 2 run values == 1 pair, so hench the shift
|
||||
// runs - prevRuns will be odd, since it is #pairs + 1 for the sentinel
|
||||
// but our shift still gives us the right answer. More correct would
|
||||
// be (runs - prevRuns - 1) >> 1, but that extra math is not needed
|
||||
// since the shift truncates away the odd value.
|
||||
intervalCount += (runs - prevRuns) >> 1;
|
||||
|
||||
if (rite < runs[-2]) {
|
||||
rite = runs[-2];
|
||||
}
|
||||
@ -63,9 +79,12 @@ bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count,
|
||||
runs += 1; // skip X-sentinel
|
||||
}
|
||||
} while (runs[0] < SkRegion::kRunTypeSentinel);
|
||||
|
||||
bounds->fLeft = left;
|
||||
bounds->fRight = rite;
|
||||
bounds->fBottom = bot;
|
||||
*ySpanCountPtr = ySpanCount;
|
||||
*intervalCountPtr = intervalCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -102,8 +121,14 @@ void SkRegion::freeRuns() {
|
||||
}
|
||||
}
|
||||
|
||||
void SkRegion::allocateRuns(int count) {
|
||||
fRunHead = RunHead::Alloc(count);
|
||||
void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
|
||||
fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
|
||||
}
|
||||
|
||||
void SkRegion::allocateRuns(const RunHead& head) {
|
||||
fRunHead = RunHead::Alloc(head.fRunCount,
|
||||
head.getYSpanCount(),
|
||||
head.getIntervalCount());
|
||||
}
|
||||
|
||||
SkRegion& SkRegion::operator=(const SkRegion& src) {
|
||||
@ -206,6 +231,9 @@ int SkRegion::count_runtype_values(int* itop, int* ibot) const {
|
||||
maxT = 2;
|
||||
} else {
|
||||
SkASSERT(this->isComplex());
|
||||
|
||||
// compute the intervalCount, for consistency checking
|
||||
#ifdef SK_DEBUG
|
||||
// skip the top
|
||||
const RunType* runs = fRunHead->readonly_runs() + 1;
|
||||
maxT = 0;
|
||||
@ -219,6 +247,10 @@ int SkRegion::count_runtype_values(int* itop, int* ibot) const {
|
||||
}
|
||||
runs = next;
|
||||
} while (runs[0] < SkRegion::kRunTypeSentinel);
|
||||
|
||||
SkASSERT(fRunHead->getIntervalCount() * 2 == maxT);
|
||||
#endif
|
||||
maxT = fRunHead->getIntervalCount() * 2;
|
||||
}
|
||||
*itop = fBounds.fTop;
|
||||
*ibot = fBounds.fBottom;
|
||||
@ -271,7 +303,8 @@ bool SkRegion::setRuns(RunType runs[], int count) {
|
||||
|
||||
SkASSERT(count >= kRectRegionRuns);
|
||||
|
||||
if (ComputeRunBounds(runs, count, &fBounds)) {
|
||||
int ySpanCount, intervalCount;
|
||||
if (ComputeRunBounds(runs, count, &fBounds, &ySpanCount, &intervalCount)) {
|
||||
// SkDEBUGF(("setRuns: rect[%d %d %d %d]\n", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom));
|
||||
return this->setRect(fBounds);
|
||||
}
|
||||
@ -296,12 +329,16 @@ bool SkRegion::setRuns(RunType runs[], int count) {
|
||||
}
|
||||
#endif
|
||||
this->freeRuns();
|
||||
this->allocateRuns(count);
|
||||
this->allocateRuns(count, ySpanCount, intervalCount);
|
||||
}
|
||||
|
||||
// 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();
|
||||
// we may not have reallocated the runhead (because the total-size is the
|
||||
// the same) but we may still different yspans or xpairs, so update these.
|
||||
fRunHead->updateYSpanCount(ySpanCount);
|
||||
fRunHead->updateIntervalCount(intervalCount);
|
||||
memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
|
||||
|
||||
SkDEBUGCODE(this->validate();)
|
||||
@ -380,19 +417,19 @@ bool SkRegion::contains(const SkRegion& rgn) const {
|
||||
}
|
||||
|
||||
const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
|
||||
int* count) const {
|
||||
SkASSERT(tmpStorage && count);
|
||||
int* intervals) const {
|
||||
SkASSERT(tmpStorage && intervals);
|
||||
const RunType* runs = tmpStorage;
|
||||
|
||||
if (this->isEmpty()) {
|
||||
tmpStorage[0] = kRunTypeSentinel;
|
||||
*count = 1;
|
||||
*intervals = 0;
|
||||
} else if (this->isRect()) {
|
||||
BuildRectRuns(fBounds, tmpStorage);
|
||||
*count = kRectRegionRuns;
|
||||
*intervals = 1;
|
||||
} else {
|
||||
*count = fRunHead->fRunCount;
|
||||
runs = fRunHead->readonly_runs();
|
||||
*intervals = fRunHead->getIntervalCount();
|
||||
}
|
||||
return runs;
|
||||
}
|
||||
@ -478,7 +515,7 @@ void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
|
||||
dst->fRunHead = dst->fRunHead->ensureWritable();
|
||||
} else {
|
||||
SkRegion tmp;
|
||||
tmp.allocateRuns(fRunHead->fRunCount);
|
||||
tmp.allocateRuns(*fRunHead);
|
||||
tmp.fBounds = fBounds;
|
||||
dst->swap(tmp);
|
||||
}
|
||||
@ -867,12 +904,10 @@ static int intervals_to_count(int intervals) {
|
||||
return 1 + intervals * 4 + 1;
|
||||
}
|
||||
|
||||
/* Given the counts of RunTypes in two regions, return the worst-case number
|
||||
/* Given the intervalCounts of RunTypes in two regions, return the worst-case number
|
||||
of RunTypes need to store the result after a region-op.
|
||||
*/
|
||||
static int compute_worst_case_count(int a_count, int b_count) {
|
||||
int a_intervals = count_to_intervals(a_count);
|
||||
int b_intervals = count_to_intervals(b_count);
|
||||
static int compute_worst_case_count(int a_intervals, int b_intervals) {
|
||||
// Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
|
||||
int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
|
||||
// convert back to number of RunType values
|
||||
@ -967,15 +1002,16 @@ bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
|
||||
RunType tmpA[kRectRegionRuns];
|
||||
RunType tmpB[kRectRegionRuns];
|
||||
|
||||
int a_count, b_count;
|
||||
const RunType* a_runs = rgna->getRuns(tmpA, &a_count);
|
||||
const RunType* b_runs = rgnb->getRuns(tmpB, &b_count);
|
||||
int a_intervals, b_intervals;
|
||||
const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
|
||||
const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
|
||||
|
||||
int dstCount = compute_worst_case_count(a_count, b_count);
|
||||
SkAutoSTMalloc<64, RunType> array(dstCount);
|
||||
int dstCount = compute_worst_case_count(a_intervals, b_intervals);
|
||||
SkAutoSTMalloc<256, RunType> array(dstCount);
|
||||
|
||||
int count = operate(a_runs, b_runs, array.get(), op, NULL == result);
|
||||
SkASSERT(count <= dstCount);
|
||||
|
||||
if (result) {
|
||||
SkASSERT(count >= 0);
|
||||
return result->setRuns(array.get(), count);
|
||||
@ -999,6 +1035,7 @@ uint32_t SkRegion::flatten(void* storage) const {
|
||||
if (!this->isEmpty()) {
|
||||
size += sizeof(fBounds);
|
||||
if (this->isComplex()) {
|
||||
size += 2 * sizeof(int32_t); // ySpanCount + intervalCount
|
||||
size += fRunHead->fRunCount * sizeof(RunType);
|
||||
}
|
||||
}
|
||||
@ -1016,6 +1053,8 @@ uint32_t SkRegion::flatten(void* storage) const {
|
||||
buffer.write(&fBounds, sizeof(fBounds));
|
||||
|
||||
if (!isRect) {
|
||||
buffer.write32(fRunHead->getYSpanCount());
|
||||
buffer.write32(fRunHead->getIntervalCount());
|
||||
buffer.write(fRunHead->readonly_runs(),
|
||||
fRunHead->fRunCount * sizeof(RunType));
|
||||
}
|
||||
@ -1034,7 +1073,9 @@ uint32_t SkRegion::unflatten(const void* storage) {
|
||||
if (count == 0) {
|
||||
tmp.fRunHead = SkRegion_gRectRunHeadPtr;
|
||||
} else {
|
||||
tmp.allocateRuns(count);
|
||||
int32_t ySpanCount = buffer.readS32();
|
||||
int32_t intervalCount = buffer.readS32();
|
||||
tmp.allocateRuns(count, ySpanCount, intervalCount);
|
||||
buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
|
||||
}
|
||||
}
|
||||
@ -1093,9 +1134,15 @@ void SkRegion::validate() const {
|
||||
// check that our bounds match our runs
|
||||
{
|
||||
SkIRect bounds;
|
||||
bool isARect = ComputeRunBounds(run, stop - run, &bounds);
|
||||
int ySpanCount, intervalCount;
|
||||
bool isARect = ComputeRunBounds(run, stop - run, &bounds,
|
||||
&ySpanCount, &intervalCount);
|
||||
SkASSERT(!isARect);
|
||||
SkASSERT(bounds == fBounds);
|
||||
SkASSERT(ySpanCount > 0);
|
||||
SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
|
||||
SkASSERT(intervalCount > 1);
|
||||
SkASSERT(fRunHead->getIntervalCount() == intervalCount);
|
||||
}
|
||||
|
||||
SkASSERT(*run == fBounds.fTop);
|
||||
|
@ -22,15 +22,57 @@ struct SkRegion::RunHead {
|
||||
int32_t fRefCnt;
|
||||
int32_t fRunCount;
|
||||
|
||||
/**
|
||||
* Number of spans with different Y values. This does not count the initial
|
||||
* Top value, nor does it count the final Y-Sentinel value. In the logical
|
||||
* case of a rectangle, this would return 1, and an empty region would
|
||||
* return 0.
|
||||
*/
|
||||
int getYSpanCount() const {
|
||||
return fYSpanCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of intervals in the entire region. This equals the number of
|
||||
* rects that would be returned by the Iterator. In the logical case of
|
||||
* a rect, this would return 1, and an empty region would return 0.
|
||||
*/
|
||||
int getIntervalCount() const {
|
||||
return fIntervalCount;
|
||||
}
|
||||
|
||||
void updateYSpanCount(int n) {
|
||||
SkASSERT(n > 0);
|
||||
fYSpanCount = n;
|
||||
}
|
||||
|
||||
void updateIntervalCount(int n) {
|
||||
SkASSERT(n > 1);
|
||||
fIntervalCount = n;
|
||||
}
|
||||
|
||||
static RunHead* Alloc(int count) {
|
||||
//SkDEBUGCODE(sk_atomic_inc(&gRgnAllocCounter);)
|
||||
//SkDEBUGF(("************** gRgnAllocCounter::alloc %d\n", gRgnAllocCounter));
|
||||
|
||||
|
||||
SkASSERT(count >= SkRegion::kRectRegionRuns);
|
||||
|
||||
|
||||
RunHead* head = (RunHead*)sk_malloc_throw(sizeof(RunHead) + count * sizeof(RunType));
|
||||
head->fRefCnt = 1;
|
||||
head->fRunCount = count;
|
||||
// these must be filled in later, otherwise we will be invalid
|
||||
head->fYSpanCount = 0;
|
||||
head->fIntervalCount = 0;
|
||||
return head;
|
||||
}
|
||||
|
||||
static RunHead* Alloc(int count, int yspancount, int intervalCount) {
|
||||
SkASSERT(yspancount > 0);
|
||||
SkASSERT(intervalCount > 1);
|
||||
|
||||
RunHead* head = Alloc(count);
|
||||
head->fYSpanCount = yspancount;
|
||||
head->fIntervalCount = intervalCount;
|
||||
return head;
|
||||
}
|
||||
|
||||
@ -57,7 +99,7 @@ struct SkRegion::RunHead {
|
||||
// We need to alloc & copy the current region before we call
|
||||
// sk_atomic_dec because it could be freed in the meantime,
|
||||
// otherwise.
|
||||
writable = Alloc(fRunCount);
|
||||
writable = Alloc(fRunCount, fYSpanCount, fIntervalCount);
|
||||
memcpy(writable->writable_runs(), this->readonly_runs(),
|
||||
fRunCount * sizeof(RunType));
|
||||
|
||||
@ -70,6 +112,10 @@ struct SkRegion::RunHead {
|
||||
}
|
||||
return writable;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t fYSpanCount;
|
||||
int32_t fIntervalCount;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -302,10 +302,14 @@ bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
|
||||
this->setRect(fBounds);
|
||||
} else {
|
||||
SkRegion tmp;
|
||||
int ySpanCount, intervalCount;
|
||||
|
||||
tmp.fRunHead = RunHead::Alloc(count);
|
||||
builder.copyToRgn(tmp.fRunHead->writable_runs());
|
||||
ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds);
|
||||
ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds,
|
||||
&ySpanCount, &intervalCount);
|
||||
tmp.fRunHead->updateYSpanCount(ySpanCount);
|
||||
tmp.fRunHead->updateIntervalCount(intervalCount);
|
||||
this->swap(tmp);
|
||||
}
|
||||
SkDEBUGCODE(this->validate();)
|
||||
|
Loading…
Reference in New Issue
Block a user