Compact the clipstack for kReplace_Op'd geometry
When adding a clip rect or clip path to the stack with the kReplace_Op operation, remove all previous elements within the same save frame (elements with fSaveCount equal to the current fSaveCount of the stack). This prevents unbounded growth of the clipstack for long-lived instances that gets reused. Addresses https://code.google.com/p/skia/issues/detail?id=748 R=robertphillips@google.com Author: fs@opera.com Review URL: https://chromiumcodereview.appspot.com/16160020 git-svn-id: http://skia.googlecode.com/svn/trunk@9502 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
7fb5373fb7
commit
6fbe54c663
@ -452,6 +452,11 @@ private:
|
||||
|
||||
mutable SkTDArray<ClipCallbackData> fCallbackData;
|
||||
|
||||
/**
|
||||
* Restore the stack back to the specified save count.
|
||||
*/
|
||||
void restoreTo(int saveCount);
|
||||
|
||||
/**
|
||||
* Invoke all the purge callbacks passing in element's generation ID.
|
||||
*/
|
||||
|
@ -443,9 +443,13 @@ void SkClipStack::save() {
|
||||
|
||||
void SkClipStack::restore() {
|
||||
fSaveCount -= 1;
|
||||
restoreTo(fSaveCount);
|
||||
}
|
||||
|
||||
void SkClipStack::restoreTo(int saveCount) {
|
||||
while (!fDeque.empty()) {
|
||||
Element* element = (Element*)fDeque.back();
|
||||
if (element->fSaveCount <= fSaveCount) {
|
||||
if (element->fSaveCount <= saveCount) {
|
||||
break;
|
||||
}
|
||||
this->purgeClip(element);
|
||||
@ -528,32 +532,37 @@ void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
|
||||
SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
|
||||
Element* element = (Element*) iter.prev();
|
||||
|
||||
if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
|
||||
switch (element->fType) {
|
||||
case Element::kEmpty_Type:
|
||||
element->checkEmpty();
|
||||
return;
|
||||
case Element::kRect_Type:
|
||||
if (element->rectRectIntersectAllowed(rect, doAA)) {
|
||||
this->purgeClip(element);
|
||||
if (!element->fRect.intersect(rect)) {
|
||||
if (NULL != element) {
|
||||
if (element->canBeIntersectedInPlace(fSaveCount, op)) {
|
||||
switch (element->fType) {
|
||||
case Element::kEmpty_Type:
|
||||
element->checkEmpty();
|
||||
return;
|
||||
case Element::kRect_Type:
|
||||
if (element->rectRectIntersectAllowed(rect, doAA)) {
|
||||
this->purgeClip(element);
|
||||
if (!element->fRect.intersect(rect)) {
|
||||
element->setEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
element->fDoAA = doAA;
|
||||
Element* prev = (Element*) iter.prev();
|
||||
element->updateBoundAndGenID(prev);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Element::kPath_Type:
|
||||
if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
|
||||
this->purgeClip(element);
|
||||
element->setEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
element->fDoAA = doAA;
|
||||
Element* prev = (Element*) iter.prev();
|
||||
element->updateBoundAndGenID(prev);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Element::kPath_Type:
|
||||
if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
|
||||
this->purgeClip(element);
|
||||
element->setEmpty();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
} else if (SkRegion::kReplace_Op == op) {
|
||||
this->restoreTo(fSaveCount - 1);
|
||||
element = (Element*) fDeque.back();
|
||||
}
|
||||
}
|
||||
new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
|
||||
@ -571,26 +580,31 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
|
||||
}
|
||||
|
||||
Element* element = (Element*)fDeque.back();
|
||||
if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
|
||||
const SkRect& pathBounds = path.getBounds();
|
||||
switch (element->fType) {
|
||||
case Element::kEmpty_Type:
|
||||
element->checkEmpty();
|
||||
return;
|
||||
case Element::kRect_Type:
|
||||
if (!SkRect::Intersects(element->fRect, pathBounds)) {
|
||||
this->purgeClip(element);
|
||||
element->setEmpty();
|
||||
if (NULL != element) {
|
||||
if (element->canBeIntersectedInPlace(fSaveCount, op)) {
|
||||
const SkRect& pathBounds = path.getBounds();
|
||||
switch (element->fType) {
|
||||
case Element::kEmpty_Type:
|
||||
element->checkEmpty();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Element::kPath_Type:
|
||||
if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
|
||||
this->purgeClip(element);
|
||||
element->setEmpty();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Element::kRect_Type:
|
||||
if (!SkRect::Intersects(element->fRect, pathBounds)) {
|
||||
this->purgeClip(element);
|
||||
element->setEmpty();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Element::kPath_Type:
|
||||
if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
|
||||
this->purgeClip(element);
|
||||
element->setEmpty();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (SkRegion::kReplace_Op == op) {
|
||||
this->restoreTo(fSaveCount - 1);
|
||||
element = (Element*) fDeque.back();
|
||||
}
|
||||
}
|
||||
new (fDeque.push_back()) Element(fSaveCount, path, op, doAA);
|
||||
|
@ -392,6 +392,107 @@ static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, bounds == rect);
|
||||
}
|
||||
|
||||
static void test_rect_replace(skiatest::Reporter* reporter) {
|
||||
SkRect rect = SkRect::MakeWH(100, 100);
|
||||
SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
|
||||
|
||||
SkRect bound;
|
||||
SkClipStack::BoundsType type;
|
||||
bool isIntersectionOfRects;
|
||||
|
||||
// Adding a new rect with the replace operator should not increase
|
||||
// the stack depth. BW replacing BW.
|
||||
{
|
||||
SkClipStack stack;
|
||||
REPORTER_ASSERT(reporter, 0 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
}
|
||||
|
||||
// Adding a new rect with the replace operator should not increase
|
||||
// the stack depth. AA replacing AA.
|
||||
{
|
||||
SkClipStack stack;
|
||||
REPORTER_ASSERT(reporter, 0 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
}
|
||||
|
||||
// Adding a new rect with the replace operator should not increase
|
||||
// the stack depth. BW replacing AA replacing BW.
|
||||
{
|
||||
SkClipStack stack;
|
||||
REPORTER_ASSERT(reporter, 0 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
}
|
||||
|
||||
// Make sure replace clip rects don't collapse too much.
|
||||
{
|
||||
SkClipStack stack;
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
|
||||
stack.save();
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 2 == count(stack));
|
||||
stack.getBounds(&bound, &type, &isIntersectionOfRects);
|
||||
REPORTER_ASSERT(reporter, bound == rect);
|
||||
stack.restore();
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
|
||||
stack.save();
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 2 == count(stack));
|
||||
stack.restore();
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
|
||||
stack.save();
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 2 == count(stack));
|
||||
stack.restore();
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
}
|
||||
}
|
||||
|
||||
// Simplified path-based version of test_rect_replace.
|
||||
static void test_path_replace(skiatest::Reporter* reporter) {
|
||||
SkRect rect = SkRect::MakeWH(100, 100);
|
||||
SkPath path;
|
||||
path.addCircle(50, 50, 50);
|
||||
|
||||
// Replace operation doesn't grow the stack.
|
||||
{
|
||||
SkClipStack stack;
|
||||
REPORTER_ASSERT(reporter, 0 == count(stack));
|
||||
stack.clipDevPath(path, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
stack.clipDevPath(path, SkRegion::kReplace_Op, false);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
}
|
||||
|
||||
// Replacing rect with path.
|
||||
{
|
||||
SkClipStack stack;
|
||||
stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
stack.clipDevPath(path, SkRegion::kReplace_Op, true);
|
||||
REPORTER_ASSERT(reporter, 1 == count(stack));
|
||||
}
|
||||
}
|
||||
|
||||
// Test out SkClipStack's merging of rect clips. In particular exercise
|
||||
// merging of aa vs. bw rects.
|
||||
static void test_rect_merging(skiatest::Reporter* reporter) {
|
||||
@ -950,7 +1051,9 @@ static void TestClipStack(skiatest::Reporter* reporter) {
|
||||
test_bounds(reporter, false); // once with paths
|
||||
test_isWideOpen(reporter);
|
||||
test_rect_merging(reporter);
|
||||
test_rect_replace(reporter);
|
||||
test_rect_inverse_fill(reporter);
|
||||
test_path_replace(reporter);
|
||||
test_quickContains(reporter);
|
||||
#if SK_SUPPORT_GPU
|
||||
test_reduced_clip_stack(reporter);
|
||||
|
Loading…
Reference in New Issue
Block a user