This adds a checkbox to the debugger to allow seeing the effect pathops has on the clip. A new tab shows the C code that the pathops generate.

Once in place, this CL found a bug in the pathops code where it was not handling empty clip stack elements correctly. The Cl also has the change to SkCanvas to fix this bug.

R=robertphillips@google.com, reed@google.com

Author: caryclark@google.com

Review URL: https://codereview.chromium.org/282283002

git-svn-id: http://skia.googlecode.com/svn/trunk@14774 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp
index 01d165a..0f816ee 100644
--- a/debugger/QT/SkDebuggerGUI.cpp
+++ b/debugger/QT/SkDebuggerGUI.cpp
@@ -101,6 +101,7 @@
     connect(fSettingsWidget.getRasterCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionRasterWidget(bool)));
     connect(fSettingsWidget.getOverdrawVizCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionOverdrawVizWidget(bool)));
     connect(fSettingsWidget.getMegaVizCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionMegaVizWidget(bool)));
+    connect(fSettingsWidget.getPathOpsCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionPathOpsWidget(bool)));
     connect(&fActionPause, SIGNAL(toggled(bool)), this, SLOT(pauseDrawing(bool)));
     connect(&fActionCreateBreakpoint, SIGNAL(activated()), this, SLOT(toggleBreakpoint()));
     connect(&fActionShowDeletes, SIGNAL(triggered()), this, SLOT(showDeletes()));
@@ -415,6 +416,7 @@
     }
 
     setupOverviewText(picture->typeTimes(), picture->totTime(), kNumRepeats);
+    setupClipStackText();
 }
 
 void SkDebuggerGUI::actionCancel() {
@@ -535,6 +537,11 @@
     fCanvasWidget.update();
 }
 
+void SkDebuggerGUI::actionPathOpsWidget(bool isToggled) {
+    fDebugger.setPathOps(isToggled);
+    fCanvasWidget.update();
+}
+
 void SkDebuggerGUI::actionTextureFilter() {
     SkPaint::FilterLevel level;
     bool enabled = fSettingsWidget.getFilterOverride(&level);
@@ -662,6 +669,7 @@
                 fInspectorWidget.setText(info, SkInspectorWidget::kDetail_TabType);
                 fInspectorWidget.setDisabled(false);
             }
+            setupClipStackText();
         }
 
     }
@@ -1043,6 +1051,12 @@
     fInspectorWidget.setText(overview.c_str(), SkInspectorWidget::kOverview_TabType);
 }
 
+void SkDebuggerGUI::setupClipStackText() {
+    SkString clipStack;
+    fDebugger.getClipStackText(&clipStack);
+    fInspectorWidget.setText(clipStack.c_str(), SkInspectorWidget::kClipStack_TabType);
+}
+
 void SkDebuggerGUI::setupComboBox(SkTArray<SkString>* command) {
     fFilter.clear();
     fFilter.addItem("--Filter By Available Commands--");
diff --git a/debugger/QT/SkDebuggerGUI.h b/debugger/QT/SkDebuggerGUI.h
index 41c5e24..a137ee7 100644
--- a/debugger/QT/SkDebuggerGUI.h
+++ b/debugger/QT/SkDebuggerGUI.h
@@ -152,6 +152,11 @@
     void actionMegaVizWidget(bool isToggled);
 
     /**
+        Toggles using path ops to simplify the clip stack
+     */
+    void actionPathOpsWidget(bool );
+
+    /**
         Applies the new texture filter override
      */
     void actionTextureFilter();
@@ -344,6 +349,11 @@
     void setupOverviewText(const SkTDArray<double>* typeTimes, double totTime, int numRuns);
 
     /**
+        Fills in the clip stack pane with text
+     */
+    void setupClipStackText();
+
+    /**
         Render the supplied picture several times tracking the time consumed
         by each command.
      */
diff --git a/debugger/QT/SkInspectorWidget.cpp b/debugger/QT/SkInspectorWidget.cpp
index 6cf1212..6bcac1d 100644
--- a/debugger/QT/SkInspectorWidget.cpp
+++ b/debugger/QT/SkInspectorWidget.cpp
@@ -23,6 +23,7 @@
     QString tabNames[kTotalTabCount];
     tabNames[kOverview_TabType] = "Overview";
     tabNames[kDetail_TabType] = "Details";
+    tabNames[kClipStack_TabType] = "Clip Stack";
 
     for (int i = 0; i < kTotalTabCount; i++) {
         fTabTexts[i].setReadOnly(true);
diff --git a/debugger/QT/SkInspectorWidget.h b/debugger/QT/SkInspectorWidget.h
index 1b96235..96a6da3 100644
--- a/debugger/QT/SkInspectorWidget.h
+++ b/debugger/QT/SkInspectorWidget.h
@@ -31,6 +31,7 @@
     enum TabType {
         kOverview_TabType,
         kDetail_TabType,
+        kClipStack_TabType,
         kTotalTabCount,
     };
 
diff --git a/debugger/QT/SkSettingsWidget.cpp b/debugger/QT/SkSettingsWidget.cpp
index 29026cf..dc9dc6e 100644
--- a/debugger/QT/SkSettingsWidget.cpp
+++ b/debugger/QT/SkSettingsWidget.cpp
@@ -56,6 +56,10 @@
     fRasterLabel.setMinimumWidth(178);
     fRasterLabel.setMaximumWidth(178);
 
+    fPathOpsLabel.setText("PathOps: ");
+    fPathOpsLabel.setMinimumWidth(178);
+    fPathOpsLabel.setMaximumWidth(178);
+
     fRasterCheckBox.setChecked(true);
 
     fOverdrawVizLabel.setText("     Overdraw Viz: ");
@@ -112,6 +116,8 @@
 
     fRasterLayout.addWidget(&fRasterLabel);
     fRasterLayout.addWidget(&fRasterCheckBox);
+    fRasterLayout.addWidget(&fPathOpsLabel);
+    fRasterLayout.addWidget(&fPathOpsCheckBox);
 
     fVizLayout.addWidget(&fOverdrawVizLabel);
     fVizLayout.addWidget(&fOverdrawVizCheckBox);
diff --git a/debugger/QT/SkSettingsWidget.h b/debugger/QT/SkSettingsWidget.h
index 660da77..f35df94 100644
--- a/debugger/QT/SkSettingsWidget.h
+++ b/debugger/QT/SkSettingsWidget.h
@@ -74,6 +74,10 @@
         return &fMegaVizCheckBox;
     }
 
+    QCheckBox* getPathOpsCheckBox() {
+        return &fPathOpsCheckBox;
+    }
+
 private slots:
     void updateCommand(int newCommand);
     void updateHit(int newHit);
@@ -122,6 +126,8 @@
     QCheckBox fOverdrawVizCheckBox;
     QLabel fMegaVizLabel;
     QCheckBox fMegaVizCheckBox;
+    QLabel fPathOpsLabel;
+    QCheckBox fPathOpsCheckBox;
 
 #if SK_SUPPORT_GPU
     QHBoxLayout fGLLayout;
diff --git a/debugger/SkDebugger.cpp b/debugger/SkDebugger.cpp
index f4730d6..394c0ad 100644
--- a/debugger/SkDebugger.cpp
+++ b/debugger/SkDebugger.cpp
@@ -49,6 +49,8 @@
     fDebugCanvas->setMegaVizMode(false);
     bool overDraw = fDebugCanvas->getOverdrawViz();
     fDebugCanvas->setOverdrawViz(false);
+    bool pathOps = fDebugCanvas->getAllowSimplifyClip();
+    fDebugCanvas->setAllowSimplifyClip(false);
     int saveCount = fDebugCanvas->getOutstandingSaveCount();
     fDebugCanvas->setOutstandingSaveCount(0);
 
@@ -62,6 +64,7 @@
     fDebugCanvas->setMegaVizMode(vizMode);
     fDebugCanvas->setOverdrawViz(overDraw);
     fDebugCanvas->setOutstandingSaveCount(saveCount);
+    fDebugCanvas->setAllowSimplifyClip(pathOps);
 
     return recorder.endRecording();
 }
@@ -148,3 +151,8 @@
     overview->appendS32(pictureHeight());
     overview->append("px");
 }
+
+void SkDebugger::getClipStackText(SkString* clipStack) {
+    clipStack->set(fDebugCanvas->clipStackData());
+}
+
diff --git a/debugger/SkDebugger.h b/debugger/SkDebugger.h
index 94300d8..ffb2953 100644
--- a/debugger/SkDebugger.h
+++ b/debugger/SkDebugger.h
@@ -111,6 +111,12 @@
         }
     }
 
+    void setPathOps(bool pathOps) {
+        if (NULL != fDebugCanvas) {
+            fDebugCanvas->setAllowSimplifyClip(pathOps);
+        }
+    }
+
     void setMegaViz(bool megaViz) {
         if (NULL != fDebugCanvas) {
             fDebugCanvas->setMegaVizMode(megaViz);
@@ -126,6 +132,8 @@
     void getOverviewText(const SkTDArray<double>* typeTimes, double totTime,
                          SkString* overview, int numRuns);
 
+    void getClipStackText(SkString* clipStack);
+
 private:
     SkDebugCanvas* fDebugCanvas;
     SkPicture* fPicture;
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index fa19176..81dcfe1 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1319,6 +1319,7 @@
     friend class SkDrawIter;        // needs setupDrawForLayerDevice()
     friend class AutoDrawLooper;
     friend class SkLua;             // needs top layer size and offset
+    friend class SkDebugCanvas;     // needs experimental fAllowSimplifyClip
     friend class SkDeferredDevice;  // needs getTopDevice()
 
     SkBaseDevice* createLayerDevice(const SkImageInfo&);
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 57cf850..6500212 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1544,11 +1544,10 @@
         const SkClipStack::Element* element;
         while ((element = iter.next())) {
             SkClipStack::Element::Type type = element->getType();
-            if (type == SkClipStack::Element::kEmpty_Type) {
-                continue;
-            }
             SkPath operand;
-            element->asPath(&operand);
+            if (type != SkClipStack::Element::kEmpty_Type) {
+                element->asPath(&operand);
+            }
             SkRegion::Op elementOp = element->getOp();
             if (elementOp == SkRegion::kReplace_Op) {
                 devPath = operand;
diff --git a/src/utils/debugger/SkDebugCanvas.cpp b/src/utils/debugger/SkDebugCanvas.cpp
index 14fbf88..89a388e 100644
--- a/src/utils/debugger/SkDebugCanvas.cpp
+++ b/src/utils/debugger/SkDebugCanvas.cpp
@@ -240,12 +240,14 @@
     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 @@
 
         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 @@
     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[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
+
+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;
+}
diff --git a/src/utils/debugger/SkDebugCanvas.h b/src/utils/debugger/SkDebugCanvas.h
index d4fded7..be1b860 100644
--- a/src/utils/debugger/SkDebugCanvas.h
+++ b/src/utils/debugger/SkDebugCanvas.h
@@ -12,6 +12,7 @@
 
 #include "SkCanvas.h"
 #include "SkDrawCommand.h"
+#include "SkPathOps.h"
 #include "SkPicture.h"
 #include "SkTArray.h"
 #include "SkString.h"
@@ -37,6 +38,8 @@
     void setOutstandingSaveCount(int saveCount) { fOutstandingSaveCount = saveCount; }
     int getOutstandingSaveCount() const { return fOutstandingSaveCount; }
 
+    bool getAllowSimplifyClip() const { return fAllowSimplifyClip; }
+
     void setPicture(SkPicture* picture) { fPicture = picture; }
 
     /**
@@ -151,6 +154,8 @@
         fUserMatrix = matrix;
     }
 
+    SkString clipStackData() const { return fClipStackData; }
+
 ////////////////////////////////////////////////////////////////////////////////
 // Inherited from SkCanvas
 ////////////////////////////////////////////////////////////////////////////////
@@ -266,6 +271,10 @@
     SkMatrix fMatrix;
     SkIRect fClip;
 
+    SkString fClipStackData;
+    bool fCalledAddStackData;
+    SkPath fSaveDevPath;
+
     bool fOverdrawViz;
     SkDrawFilter* fOverdrawFilter;
 
@@ -311,6 +320,16 @@
         return 0;
     }
 
+    void resetClipStackData() { fClipStackData.reset(); fCalledAddStackData = false; }
+
+    void addClipStackData(const SkPath& devPath, const SkPath& operand, SkRegion::Op elementOp);
+    void addPathData(const SkPath& path, const char* pathName);
+    bool lastClipStackData(const SkPath& devPath);
+    void outputConicPoints(const SkPoint* pts, SkScalar weight);
+    void outputPoints(const SkPoint* pts, int count);
+    void outputPointsCommon(const SkPoint* pts, int count);
+    void outputScalar(SkScalar num);
+   
     typedef SkCanvas INHERITED;
 };