Bug 945079 - Add a way to simplify regions based on the change in volume. r=mattwoodrow

This commit is contained in:
Jeff Muizelaar 2014-05-06 16:39:34 -04:00
parent d1be6ba0f3
commit abac769686
3 changed files with 402 additions and 0 deletions

View File

@ -97,6 +97,290 @@ void nsRegion::SimplifyOutward (uint32_t aMaxRects)
}
}
// compute the covered area difference between two rows.
// by iterating over both rows simultaneously and adding up
// the additional increase in area caused by extending each
// of the rectangles to the combined height of both rows
static uint32_t ComputeMergedAreaIncrease(pixman_box32_t *topRects,
pixman_box32_t *topRectsEnd,
pixman_box32_t *bottomRects,
pixman_box32_t *bottomRectsEnd)
{
uint32_t totalArea = 0;
struct pt {
int32_t x, y;
};
pt *i = (pt*)topRects;
pt *end_i = (pt*)topRectsEnd;
pt *j = (pt*)bottomRects;
pt *end_j = (pt*)bottomRectsEnd;
bool top = false;
bool bottom = false;
int cur_x = i->x;
bool top_next = top;
bool bottom_next = bottom;
//XXX: we could probably simplify this condition and perhaps move it into the loop below
if (j->x < cur_x) {
cur_x = j->x;
j++;
bottom_next = !bottom;
} else if (j->x == cur_x) {
i++;
top_next = !top;
bottom_next = !bottom;
j++;
} else {
top_next = !top;
i++;
}
int topRectsHeight = topRects->y2 - topRects->y1;
int bottomRectsHeight = bottomRects->y2 - bottomRects->y1;
int inbetweenHeight = bottomRects->y1 - topRects->y2;
int width = cur_x;
// top and bottom are the in-status to the left of cur_x
do {
if (top && !bottom) {
totalArea += (inbetweenHeight+bottomRectsHeight)*width;
} else if (bottom && !top) {
totalArea += (inbetweenHeight+topRectsHeight)*width;
} else if (bottom && top) {
totalArea += (inbetweenHeight)*width;
}
top = top_next;
bottom = bottom_next;
// find the next edge
if (i->x < j->x) {
top_next = !top;
width = i->x - cur_x;
cur_x = i->x;
i++;
} else if (j->x < i->x) {
bottom_next = !bottom;
width = j->x - cur_x;
cur_x = j->x;
j++;
} else { // i->x == j->x
top_next = !top;
bottom_next = !bottom;
width = i->x - cur_x;
cur_x = i->x;
i++;
j++;
}
} while (i < end_i && j < end_j);
// handle any remaining rects
while (i < end_i) {
width = i->x - cur_x;
cur_x = i->x;
i++;
if (top)
totalArea += (inbetweenHeight+bottomRectsHeight)*width;
top = !top;
}
while (j < end_j) {
width = j->x - cur_x;
cur_x = j->x;
j++;
if (bottom)
totalArea += (inbetweenHeight+topRectsHeight)*width;
bottom = !bottom;
}
return totalArea;
}
static pixman_box32_t *
CopyRow(pixman_box32_t *dest_it, pixman_box32_t *src_start, pixman_box32_t *src_end)
{
// XXX: std::copy
pixman_box32_t *src_it = src_start;
while (src_it < src_end) {
*dest_it++ = *src_it++;
}
return dest_it;
}
static pixman_box32_t *
MergeRects(pixman_box32_t *topRects, pixman_box32_t *topRectsEnd,
pixman_box32_t *bottomRects, pixman_box32_t *bottomRectsEnd,
pixman_box32_t *tmpRect)
{
struct pt {
int32_t x, y;
};
pixman_box32_t *rect;
// merge the two spans of rects
pt *i = (pt*)topRects;
pt *end_i = (pt*)topRectsEnd;
pt *j = (pt*)bottomRects;
pt *end_j = (pt*)bottomRectsEnd;
bool top;
bool bottom;
int cur_x = i->x;
int32_t y1 = topRects->y1;
int32_t y2 = bottomRects->y2;
if (j->x < cur_x) {
top = false;
bottom = true;
cur_x = j->x;
j++;
} else if (j->x == cur_x) {
top = true;
bottom = true;
i++;
j++;
} else {
top = true;
bottom = false;
i++;
}
rect = tmpRect;
bool started = false;
do {
if (started && !top && !bottom) {
rect->x2 = cur_x;
rect->y2 = y2;
rect++;
started = false;
} else if (!started) {
rect->x1 = cur_x;
rect->y1 = y1;
started = true;
}
if (i >= end_i || j >= end_j)
break;
if (i->x < j->x) {
top = !top;
cur_x = i->x;
i++;
} else if (j->x < i->x) {
bottom = !bottom;
cur_x = j->x;
j++;
} else { // i->x == j->x
top = !top;
bottom = !bottom;
cur_x = i->x;
i++;
j++;
}
} while (true);
// handle any remaining rects
while (i < end_i) {
top = !top;
cur_x = i->x;
i++;
if (!top) {
rect->x2 = cur_x;
rect->y2 = y2;
rect++;
} else {
rect->x1 = cur_x;
rect->y1 = y1;
}
}
while (j < end_j) {
bottom = !bottom;
cur_x = j->x;
j++;
if (!bottom) {
rect->x2 = cur_x;
rect->y2 = y2;
rect++;
} else {
rect->x1 = cur_x;
rect->y1 = y1;
}
}
return rect;
}
void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold)
{
pixman_box32_t *boxes;
int n;
boxes = pixman_region32_rectangles(&mImpl, &n);
pixman_box32_t *end = boxes + n;
pixman_box32_t *topRectsEnd = boxes+1;
pixman_box32_t *topRects = boxes;
// we need some temporary storage for merging both rows of rectangles
nsAutoTArray<pixman_box32_t, 10> tmpStorage;
tmpStorage.SetCapacity(n);
pixman_box32_t *tmpRect = tmpStorage.Elements();
pixman_box32_t *destRect = boxes;
pixman_box32_t *rect = tmpRect;
// find the end of the first span of rectangles
while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
topRectsEnd++;
}
// if we only have one row we are done
if (topRectsEnd == end)
return;
pixman_box32_t *bottomRects = topRectsEnd;
pixman_box32_t *bottomRectsEnd = bottomRects+1;
do {
// find the end of the bottom span of rectangles
while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
bottomRectsEnd++;
}
uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd,
bottomRects, bottomRectsEnd);
if (totalArea <= aThreshold) {
// merge the rects into tmpRect
rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect);
// copy the merged rects back into the destination
topRectsEnd = CopyRow(destRect, tmpRect, rect);
topRects = destRect;
bottomRects = bottomRectsEnd;
destRect = topRects;
} else {
// copy the unmerged rects
destRect = CopyRow(destRect, topRects, topRectsEnd);
topRects = bottomRects;
topRectsEnd = bottomRectsEnd;
bottomRects = bottomRectsEnd;
if (bottomRectsEnd == end) {
// copy the last row when we are done
topRectsEnd = CopyRow(destRect, topRects, topRectsEnd);
}
}
} while (bottomRectsEnd != end);
uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n);
// pixman has a special representation for
// regions of 1 rectangle. So just use the
// bounds in that case
if (reducedCount > 1) {
// reach into pixman and lower the number
// of rects stored in data.
this->mImpl.data->numRects = reducedCount;
} else {
*this = GetBounds();
}
}
void nsRegion::SimplifyInward (uint32_t aMaxRects)
{
NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");

View File

@ -242,6 +242,13 @@ public:
* the same as for the current region.
*/
void SimplifyOutward (uint32_t aMaxRects);
/**
* Simplify the region by adding at most aThreshold area between spans of
* rects. The simplified region will be a superset of the original region.
* The simplified region's bounding box will be the same as for the current
* region.
*/
void SimplifyOutwardByArea(uint32_t aThreshold);
/**
* Make sure the region has at most aMaxRects by removing area from
* it if necessary. The simplified region will be a subset of the
@ -539,6 +546,10 @@ public:
{
mImpl.SimplifyOutward (aMaxRects);
}
void SimplifyOutwardByArea (uint32_t aThreshold)
{
mImpl.SimplifyOutwardByArea (aThreshold);
}
/**
* Make sure the region has at most aMaxRects by removing area from
* it if necessary. The simplified region will be a subset of the

View File

@ -171,3 +171,110 @@ TEST(Gfx, RegionScaleToInside) {
}
}
TEST(Gfx, RegionSimplify) {
{ // ensure simplify works on a single rect
nsRegion r(nsRect(0,100,200,100));
r.SimplifyOutwardByArea(100*100);
nsRegion result(nsRect(0,100,200,100));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not the same";
}
{ // the rectangles will be merged
nsRegion r(nsRect(0,100,200,100));
r.Or(r, nsRect(0,200,300,200));
r.SimplifyOutwardByArea(100*100);
nsRegion result(nsRect(0,100,300,300));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not merged";
}
{ // two rectangle on the first span
// one on the second
nsRegion r(nsRect(0,100,200,100));
r.Or(r, nsRect(0,200,300,200));
r.Or(r, nsRect(250,100,50,100));
EXPECT_TRUE(r.GetNumRects() == 3) <<
"wrong number of rects";
r.SimplifyOutwardByArea(100*100);
nsRegion result(nsRect(0,100,300,300));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not merged";
}
{ // the rectangles will be merged
nsRegion r(nsRect(0,100,200,100));
r.Or(r, nsRect(0,200,300,200));
r.Or(r, nsRect(250,100,50,100));
r.Sub(r, nsRect(200,200,40,200));
EXPECT_TRUE(r.GetNumRects() == 4) <<
"wrong number of rects";
r.SimplifyOutwardByArea(100*100);
nsRegion result(nsRect(0,100,300,300));
result.Sub(result, nsRect(200,100,40,300));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not merged";
}
{ // three spans of rectangles
nsRegion r(nsRect(0,100,200,100));
r.Or(r, nsRect(0,200,300,200));
r.Or(r, nsRect(250,100,50,50));
r.Sub(r, nsRect(200,200,40,200));
r.SimplifyOutwardByArea(100*100);
nsRegion result(nsRect(0,100,300,300));
result.Sub(result, nsRect(200,100,40,300));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not merged";
}
{ // three spans of rectangles and an unmerged rectangle
nsRegion r(nsRect(0,100,200,100));
r.Or(r, nsRect(0,200,300,200));
r.Or(r, nsRect(250,100,50,50));
r.Sub(r, nsRect(200,200,40,200));
r.Or(r, nsRect(250,900,150,50));
r.SimplifyOutwardByArea(100*100);
nsRegion result(nsRect(0,100,300,300));
result.Sub(result, nsRect(200,100,40,300));
result.Or(result, nsRect(250,900,150,50));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not merged";
}
{ // unmerged regions
nsRegion r(nsRect(0,100,200,100));
r.Or(r, nsRect(0,200,300,200));
r.SimplifyOutwardByArea(100);
nsRegion result(nsRect(0,100,200,100));
result.Or(result, nsRect(0,200,300,200));
EXPECT_TRUE(r.IsEqual(result)) <<
"regions not merged";
}
}