| Index: src/utils/debugger/SkDebugCanvas.cpp
 | 
| diff --git a/src/utils/debugger/SkDebugCanvas.cpp b/src/utils/debugger/SkDebugCanvas.cpp
 | 
| index 14fbf8888a6d149f7d6e9b304ad403daefbbe708..89a388e11003d0958131367356d91444a3f9a506 100644
 | 
| --- a/src/utils/debugger/SkDebugCanvas.cpp
 | 
| +++ b/src/utils/debugger/SkDebugCanvas.cpp
 | 
| @@ -240,12 +240,14 @@ void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
 | 
|      SkASSERT(index < fCommandVector.count());
 | 
|      int i = 0;
 | 
|  
 | 
| +    bool pathOpsMode = getAllowSimplifyClip();
 | 
| +    canvas->setAllowSimplifyClip(pathOpsMode);
 | 
|      // This only works assuming the canvas and device are the same ones that
 | 
|      // were previously drawn into because they need to preserve all saves
 | 
|      // and restores.
 | 
|      // The visibility filter also requires a full re-draw - otherwise we can
 | 
|      // end up drawing the filter repeatedly.
 | 
| -    if (fIndex < index && !fFilter && !fMegaVizMode) {
 | 
| +    if (fIndex < index && !fFilter && !fMegaVizMode && !pathOpsMode) {
 | 
|          i = fIndex + 1;
 | 
|      } else {
 | 
|          for (int j = 0; j < fOutstandingSaveCount; j++) {
 | 
| @@ -335,6 +337,28 @@ void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
 | 
|  
 | 
|          canvas->restore();
 | 
|      }
 | 
| +    if (pathOpsMode) {
 | 
| +        this->resetClipStackData();
 | 
| +        const SkClipStack* clipStack = canvas->getClipStack();
 | 
| +        SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
 | 
| +        const SkClipStack::Element* element;
 | 
| +        SkPath devPath;
 | 
| +        while ((element = iter.next())) {
 | 
| +            SkClipStack::Element::Type type = element->getType();
 | 
| +            SkPath operand;
 | 
| +            if (type != SkClipStack::Element::kEmpty_Type) {
 | 
| +               element->asPath(&operand);
 | 
| +            }
 | 
| +            SkRegion::Op elementOp = element->getOp();
 | 
| +            this->addClipStackData(devPath, operand, elementOp);
 | 
| +            if (elementOp == SkRegion::kReplace_Op) {
 | 
| +                devPath = operand;
 | 
| +            } else {
 | 
| +                Op(devPath, operand, (SkPathOp) elementOp, &devPath);
 | 
| +            }
 | 
| +        }
 | 
| +        this->lastClipStackData(devPath);
 | 
| +    }
 | 
|      fMatrix = canvas->getTotalMatrix();
 | 
|      if (!canvas->getClipDeviceBounds(&fClip)) {
 | 
|          fClip.setEmpty();
 | 
| @@ -588,3 +612,128 @@ void SkDebugCanvas::toggleCommand(int index, bool toggle) {
 | 
|      SkASSERT(index < fCommandVector.count());
 | 
|      fCommandVector[index]->setVisible(toggle);
 | 
|  }
 | 
| +
 | 
| +static const char* gFillTypeStrs[] = {
 | 
| +    "kWinding_FillType",
 | 
| +    "kEvenOdd_FillType",
 | 
| +    "kInverseWinding_FillType",
 | 
| +    "kInverseEvenOdd_FillType"
 | 
| +};
 | 
| +
 | 
| +static const char* gOpStrs[] = {
 | 
| +    "kDifference_PathOp",
 | 
| +    "kIntersect_PathOp",
 | 
| +    "kUnion_PathOp",
 | 
| +    "kXor_PathOp",
 | 
| +    "kReverseDifference_PathOp",
 | 
| +};
 | 
| +
 | 
| +static const char kHTML4SpaceIndent[] = "    ";
 | 
| +
 | 
| +void SkDebugCanvas::outputScalar(SkScalar num) {
 | 
| +    if (num == (int) num) {
 | 
| +        fClipStackData.appendf("%d", (int) num);
 | 
| +    } else {
 | 
| +        SkString str;
 | 
| +        str.printf("%1.9g", num);
 | 
| +        int width = (int) str.size();
 | 
| +        const char* cStr = str.c_str();
 | 
| +        while (cStr[width - 1] == '0') {
 | 
| +            --width;
 | 
| +        }
 | 
| +        str.resize(width);
 | 
| +        fClipStackData.appendf("%sf", str.c_str());
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) {
 | 
| +    for (int index = 0; index < count; ++index) {
 | 
| +        this->outputScalar(pts[index].fX);
 | 
| +        fClipStackData.appendf(", ");
 | 
| +        this->outputScalar(pts[index].fY);
 | 
| +        if (index + 1 < count) {
 | 
| +            fClipStackData.appendf(", ");
 | 
| +        }
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
 | 
| +    this->outputPointsCommon(pts, count);
 | 
| +    fClipStackData.appendf(");<br>");
 | 
| +}
 | 
| +
 | 
| +void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) {
 | 
| +    this->outputPointsCommon(pts, 2);
 | 
| +    fClipStackData.appendf(", ");
 | 
| +    this->outputScalar(weight);
 | 
| +    fClipStackData.appendf(");<br>");
 | 
| +}
 | 
| +
 | 
| +void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) {
 | 
| +    SkPath::RawIter iter(path);
 | 
| +    SkPath::FillType fillType = path.getFillType();
 | 
| +    fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName);
 | 
| +    fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName,
 | 
| +            gFillTypeStrs[fillType]);
 | 
| +    iter.setPath(path);
 | 
| +    uint8_t verb;
 | 
| +    SkPoint pts[4];
 | 
| +    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
 | 
| +        switch (verb) {
 | 
| +            case SkPath::kMove_Verb:
 | 
| +                fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
 | 
| +                this->outputPoints(&pts[0], 1);
 | 
| +                continue;
 | 
| +            case SkPath::kLine_Verb:
 | 
| +                fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
 | 
| +                this->outputPoints(&pts[1], 1);
 | 
| +                break;
 | 
| +            case SkPath::kQuad_Verb:
 | 
| +                fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
 | 
| +                this->outputPoints(&pts[1], 2);
 | 
| +                break;
 | 
| +            case SkPath::kConic_Verb:
 | 
| +                fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
 | 
| +                this->outputConicPoints(&pts[1], iter.conicWeight());
 | 
| +                break;
 | 
| +            case SkPath::kCubic_Verb:
 | 
| +                fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
 | 
| +                this->outputPoints(&pts[1], 3);
 | 
| +                break;
 | 
| +            case SkPath::kClose_Verb:
 | 
| +                fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
 | 
| +                break;
 | 
| +            default:
 | 
| +                SkDEBUGFAIL("bad verb");
 | 
| +                return;
 | 
| +        }
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand,
 | 
| +                                     SkRegion::Op elementOp) {
 | 
| +    if (elementOp == SkRegion::kReplace_Op) {
 | 
| +        if (!lastClipStackData(devPath)) {
 | 
| +            fSaveDevPath = operand;
 | 
| +        }
 | 
| +        fCalledAddStackData = false;
 | 
| +    } else {
 | 
| +        fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter,"
 | 
| +            " const char* filename) {<br>");
 | 
| +        addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path");
 | 
| +        addPathData(operand, "pathB");
 | 
| +        fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>",
 | 
| +            kHTML4SpaceIndent, gOpStrs[elementOp]);
 | 
| +        fClipStackData.appendf("}<br>");
 | 
| +        fCalledAddStackData = true;
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
 | 
| +    if (fCalledAddStackData) {
 | 
| +        fClipStackData.appendf("<br>");
 | 
| +        addPathData(devPath, "pathOut");
 | 
| +        return true;
 | 
| +    }
 | 
| +    return false;
 | 
| +}
 | 
| 
 |