| Index: src/core/SkClipStack.cpp
 | 
| diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
 | 
| index c66a219b629bbe7c467eafa946e2487fe067afbe..17eb6f931d31f04d344c27ecc0228fe7a100ce0a 100644
 | 
| --- a/src/core/SkClipStack.cpp
 | 
| +++ b/src/core/SkClipStack.cpp
 | 
| @@ -16,17 +16,89 @@
 | 
|  static const int32_t kFirstUnreservedGenID = 3;
 | 
|  int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
 | 
|  
 | 
| +bool SkClipStack::Element::operator== (const Element& element) const {
 | 
| +    if (this == &element) {
 | 
| +        return true;
 | 
| +    }
 | 
| +    if (fOp != element.fOp ||
 | 
| +        fType != element.fType ||
 | 
| +        fDoAA != element.fDoAA ||
 | 
| +        fSaveCount != element.fSaveCount) {
 | 
| +        return false;
 | 
| +    }
 | 
| +    switch (fType) {
 | 
| +        case kPath_Type:
 | 
| +            return fPath == element.fPath;
 | 
| +        case kRRect_Type:
 | 
| +            return fRRect == element.fRRect;
 | 
| +        case kRect_Type:
 | 
| +            return fRect == element.fRect;
 | 
| +        case kEmpty_Type:
 | 
| +            return true;
 | 
| +        default:
 | 
| +            SkDEBUGFAIL("Unexpected type.");
 | 
| +            return false;
 | 
| +    }
 | 
| +}
 | 
| +
 | 
|  void SkClipStack::Element::invertShapeFillType() {
 | 
|      switch (fType) {
 | 
|          case kRect_Type:
 | 
|              fPath.reset();
 | 
|              fPath.addRect(fRect);
 | 
| -            fPath.setFillType(SkPath::kInverseWinding_FillType);
 | 
| +            fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
 | 
| +            fType = kPath_Type;
 | 
| +            break;
 | 
| +        case kRRect_Type:
 | 
| +            fPath.reset();
 | 
| +            fPath.addRRect(fRRect);
 | 
| +            fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
 | 
|              fType = kPath_Type;
 | 
|              break;
 | 
|          case kPath_Type:
 | 
|              fPath.toggleInverseFillType();
 | 
| +            break;
 | 
|          case kEmpty_Type:
 | 
| +            // Should this set to an empty, inverse filled path?
 | 
| +            break;
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkRegion::Op op,
 | 
| +                                    bool doAA) {
 | 
| +    if (!path.isInverseFillType()) {
 | 
| +        if (SkPath::kNone_PathAsRect != path.asRect()) {
 | 
| +            this->initRect(saveCount, path.getBounds(), op, doAA);
 | 
| +            return;
 | 
| +        }
 | 
| +        SkRect ovalRect;
 | 
| +        if (path.isOval(&ovalRect)) {
 | 
| +            SkRRect rrect;
 | 
| +            rrect.setOval(ovalRect);
 | 
| +            this->initRRect(saveCount, rrect, op, doAA);
 | 
| +            return;
 | 
| +        }
 | 
| +    }
 | 
| +    fPath = path;
 | 
| +    fType = kPath_Type;
 | 
| +    this->initCommon(saveCount, op, doAA);
 | 
| +}
 | 
| +
 | 
| +void SkClipStack::Element::asPath(SkPath* path) const {
 | 
| +    switch (fType) {
 | 
| +        case kEmpty_Type:
 | 
| +            path->reset();
 | 
| +            break;
 | 
| +        case kRect_Type:
 | 
| +            path->reset();
 | 
| +            path->addRect(fRect);
 | 
| +            break;
 | 
| +        case kRRect_Type:
 | 
| +            path->reset();
 | 
| +            path->addRRect(fRRect);
 | 
| +            break;
 | 
| +        case kPath_Type:
 | 
| +            *path = fPath;
 | 
|              break;
 | 
|      }
 | 
|  }
 | 
| @@ -100,10 +172,10 @@ void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect
 | 
|              // is erased, so the only pixels that can remain set
 | 
|              // occur w/in the intersection of the two finite bounds
 | 
|              if (!fFiniteBound.intersect(prevFinite)) {
 | 
| -                fFiniteBound.setEmpty();
 | 
| -                fGenID = kEmptyGenID;
 | 
| +                this->setEmpty();
 | 
| +            } else {
 | 
| +                fFiniteBoundType = kNormal_BoundsType;
 | 
|              }
 | 
| -            fFiniteBoundType = kNormal_BoundsType;
 | 
|              break;
 | 
|          case kPrev_Cur_FillCombo:
 | 
|              // The most conservative result bound is that of the
 | 
| @@ -205,8 +277,7 @@ void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRe
 | 
|              break;
 | 
|          case kPrev_Cur_FillCombo:
 | 
|              if (!fFiniteBound.intersect(prevFinite)) {
 | 
| -                fFiniteBound.setEmpty();
 | 
| -                fGenID = kEmptyGenID;
 | 
| +                this->setEmpty();
 | 
|              }
 | 
|              break;
 | 
|          default:
 | 
| @@ -228,10 +299,10 @@ void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& p
 | 
|              break;
 | 
|          case kInvPrev_Cur_FillCombo:
 | 
|              if (!fFiniteBound.intersect(prevFinite)) {
 | 
| -                fFiniteBound.setEmpty();
 | 
| -                fGenID = kEmptyGenID;
 | 
| +                this->setEmpty();
 | 
| +            } else {
 | 
| +                fFiniteBoundType = kNormal_BoundsType;
 | 
|              }
 | 
| -            fFiniteBoundType = kNormal_BoundsType;
 | 
|              break;
 | 
|          case kPrev_InvCur_FillCombo:
 | 
|              fFiniteBound.join(prevFinite);
 | 
| @@ -258,27 +329,34 @@ void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
 | 
|      // First, optimistically update the current Element's bound information
 | 
|      // with the current clip's bound
 | 
|      fIsIntersectionOfRects = false;
 | 
| -    if (kRect_Type == fType) {
 | 
| -        fFiniteBound = fRect;
 | 
| -        fFiniteBoundType = kNormal_BoundsType;
 | 
| -
 | 
| -        if (SkRegion::kReplace_Op == fOp ||
 | 
| -            (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
 | 
| -            (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
 | 
| -                prior->rectRectIntersectAllowed(fRect, fDoAA))) {
 | 
| -            fIsIntersectionOfRects = true;
 | 
| -        }
 | 
| -
 | 
| -    } else {
 | 
| -        SkASSERT(kPath_Type == fType);
 | 
| -
 | 
| -        fFiniteBound = fPath.getBounds();
 | 
| +    switch (fType) {
 | 
| +        case kRect_Type:
 | 
| +            fFiniteBound = fRect;
 | 
| +            fFiniteBoundType = kNormal_BoundsType;
 | 
|  
 | 
| -        if (fPath.isInverseFillType()) {
 | 
| -            fFiniteBoundType = kInsideOut_BoundsType;
 | 
| -        } else {
 | 
| +            if (SkRegion::kReplace_Op == fOp ||
 | 
| +                (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
 | 
| +                (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
 | 
| +                    prior->rectRectIntersectAllowed(fRect, fDoAA))) {
 | 
| +                fIsIntersectionOfRects = true;
 | 
| +            }
 | 
| +            break;
 | 
| +        case kRRect_Type:
 | 
| +            fFiniteBound = fRRect.getBounds();
 | 
|              fFiniteBoundType = kNormal_BoundsType;
 | 
| -        }
 | 
| +            break;
 | 
| +        case kPath_Type:
 | 
| +            fFiniteBound = fPath.getBounds();
 | 
| +
 | 
| +            if (fPath.isInverseFillType()) {
 | 
| +                fFiniteBoundType = kInsideOut_BoundsType;
 | 
| +            } else {
 | 
| +                fFiniteBoundType = kNormal_BoundsType;
 | 
| +            }
 | 
| +            break;
 | 
| +        case kEmpty_Type:
 | 
| +            SkDEBUGFAIL("We shouldn't get here with an empty element.");
 | 
| +            break;
 | 
|      }
 | 
|  
 | 
|      if (!fDoAA) {
 | 
| @@ -344,7 +422,7 @@ void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
 | 
|              // so nothing to do
 | 
|              break;
 | 
|          default:
 | 
| -            SkDebugf("SkRegion::Op error/n");
 | 
| +            SkDebugf("SkRegion::Op error\n");
 | 
|              SkASSERT(0);
 | 
|              break;
 | 
|      }
 | 
| @@ -528,97 +606,69 @@ bool SkClipStack::quickContains(const SkRect& rect) const {
 | 
|      return true;
 | 
|  }
 | 
|  
 | 
| -void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
 | 
| -
 | 
| +void SkClipStack::pushElement(const Element& element) {
 | 
|      // Use reverse iterator instead of back because Rect path may need previous
 | 
|      SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
 | 
| -    Element* element = (Element*) iter.prev();
 | 
| +    Element* prior = (Element*) iter.prev();
 | 
|  
 | 
| -    if (NULL != element) {
 | 
| -        if (element->canBeIntersectedInPlace(fSaveCount, op)) {
 | 
| -            switch (element->fType) {
 | 
| +    if (NULL != prior) {
 | 
| +        if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
 | 
| +            switch (prior->fType) {
 | 
|                  case Element::kEmpty_Type:
 | 
| -                    element->checkEmpty();
 | 
| +                    SkDEBUGCODE(prior->checkEmpty();)
 | 
|                      return;
 | 
|                  case Element::kRect_Type:
 | 
| -                    if (element->rectRectIntersectAllowed(rect, doAA)) {
 | 
| -                        if (!element->fRect.intersect(rect)) {
 | 
| -                            element->setEmpty();
 | 
| +                    if (Element::kRect_Type == element.getType()) {
 | 
| +                        if (prior->rectRectIntersectAllowed(element.getRect(), element.isAA())) {
 | 
| +                            if (!prior->fRect.intersect(element.getRect())) {
 | 
| +                                prior->setEmpty();
 | 
| +                                return;
 | 
| +                            }
 | 
| +
 | 
| +                            prior->fDoAA = element.isAA();
 | 
| +                            Element* priorPrior = (Element*) iter.prev();
 | 
| +                            prior->updateBoundAndGenID(priorPrior);
 | 
|                              return;
 | 
|                          }
 | 
| -
 | 
| -                        element->fDoAA = doAA;
 | 
| -                        Element* prev = (Element*) iter.prev();
 | 
| -                        element->updateBoundAndGenID(prev);
 | 
| -                        return;
 | 
| +                        break;
 | 
|                      }
 | 
| -                    break;
 | 
| -                case Element::kPath_Type:
 | 
| -                    if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
 | 
| -                        element->setEmpty();
 | 
| +                    // fallthrough
 | 
| +                default:
 | 
| +                    if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
 | 
| +                        prior->setEmpty();
 | 
|                          return;
 | 
|                      }
 | 
|                      break;
 | 
|              }
 | 
| -        } else if (SkRegion::kReplace_Op == op) {
 | 
| +        } else if (SkRegion::kReplace_Op == element.getOp()) {
 | 
|              this->restoreTo(fSaveCount - 1);
 | 
| -            element = (Element*) fDeque.back();
 | 
| +            prior = (Element*) fDeque.back();
 | 
|          }
 | 
|      }
 | 
| -    new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
 | 
| -    ((Element*) fDeque.back())->updateBoundAndGenID(element);
 | 
| +    Element* newElement = SkNEW_PLACEMENT_ARGS(fDeque.push_back(), Element, (element));
 | 
| +    newElement->updateBoundAndGenID(prior);
 | 
|  }
 | 
|  
 | 
| -void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
 | 
| -    SkRect alt;
 | 
| -    if (path.isRect(&alt) && !path.isInverseFillType()) {
 | 
| -        return this->clipDevRect(alt, op, doAA);
 | 
| -    }
 | 
| +void SkClipStack::clipDevRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
 | 
| +    Element element(fSaveCount, rrect, op, doAA);
 | 
| +    this->pushElement(element);
 | 
| +}
 | 
|  
 | 
| -    Element* element = (Element*)fDeque.back();
 | 
| -    if (NULL != element) {
 | 
| -        if (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)) {
 | 
| -                        element->setEmpty();
 | 
| -                        return;
 | 
| -                    }
 | 
| -                    break;
 | 
| -                case Element::kPath_Type:
 | 
| -                    if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
 | 
| -                        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);
 | 
| -    ((Element*) fDeque.back())->updateBoundAndGenID(element);
 | 
| +void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
 | 
| +    Element element(fSaveCount, rect, op, doAA);
 | 
| +    this->pushElement(element);
 | 
|  }
 | 
|  
 | 
| -void SkClipStack::clipEmpty() {
 | 
| +void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
 | 
| +    Element element(fSaveCount, path, op, doAA);
 | 
| +    this->pushElement(element);
 | 
| +}
 | 
|  
 | 
| +void SkClipStack::clipEmpty() {
 | 
|      Element* element = (Element*) fDeque.back();
 | 
|  
 | 
|      if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
 | 
| -        switch (element->fType) {
 | 
| -            case Element::kEmpty_Type:
 | 
| -                element->checkEmpty();
 | 
| -                return;
 | 
| -            case Element::kRect_Type:
 | 
| -            case Element::kPath_Type:
 | 
| -                element->setEmpty();
 | 
| -                return;
 | 
| -        }
 | 
| +        element->setEmpty();
 | 
|      }
 | 
|      new (fDeque.push_back()) Element(fSaveCount);
 | 
|  
 | 
| 
 |