From 2a67e123a3e559774a16a58cbe5106bc0fb86740 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Mon, 19 May 2014 13:53:10 +0000 Subject: [PATCH] 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 --- debugger/QT/SkDebuggerGUI.cpp | 14 ++++ debugger/QT/SkDebuggerGUI.h | 10 +++ debugger/QT/SkInspectorWidget.cpp | 1 + debugger/QT/SkInspectorWidget.h | 1 + debugger/QT/SkSettingsWidget.cpp | 6 ++ debugger/QT/SkSettingsWidget.h | 6 ++ debugger/SkDebugger.cpp | 8 ++ debugger/SkDebugger.h | 8 ++ include/core/SkCanvas.h | 1 + src/core/SkCanvas.cpp | 7 +- src/utils/debugger/SkDebugCanvas.cpp | 151 ++++++++++++++++++++++++++++++++++- src/utils/debugger/SkDebugCanvas.h | 19 +++++ 12 files changed, 227 insertions(+), 5 deletions(-) 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 @@ SkDebuggerGUI::SkDebuggerGUI(QWidget *parent) : 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 @@ void SkDebuggerGUI::actionProfile() { } setupOverviewText(picture->typeTimes(), picture->totTime(), kNumRepeats); + setupClipStackText(); } void SkDebuggerGUI::actionCancel() { @@ -535,6 +537,11 @@ void SkDebuggerGUI::actionMegaVizWidget(bool isToggled) { 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 @@ void SkDebuggerGUI::registerListClick(QListWidgetItem *item) { fInspectorWidget.setText(info, SkInspectorWidget::kDetail_TabType); fInspectorWidget.setDisabled(false); } + setupClipStackText(); } } @@ -1043,6 +1051,12 @@ void SkDebuggerGUI::setupOverviewText(const SkTDArray* typeTimes, 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* 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 @@ private slots: 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 @@ private: void setupOverviewText(const SkTDArray* 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 @@ SkInspectorWidget::SkInspectorWidget() : QWidget() 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 @@ public: 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 @@ SkSettingsWidget::SkSettingsWidget() : QWidget() 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 @@ SkSettingsWidget::SkSettingsWidget() : QWidget() 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 @@ public: return &fMegaVizCheckBox; } + QCheckBox* getPathOpsCheckBox() { + return &fPathOpsCheckBox; + } + private slots: void updateCommand(int newCommand); void updateHit(int newHit); @@ -122,6 +126,8 @@ private: 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 @@ SkPicture* SkDebugger::copyPicture() { 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 @@ SkPicture* SkDebugger::copyPicture() { fDebugCanvas->setMegaVizMode(vizMode); fDebugCanvas->setOverdrawViz(overDraw); fDebugCanvas->setOutstandingSaveCount(saveCount); + fDebugCanvas->setAllowSimplifyClip(pathOps); return recorder.endRecording(); } @@ -148,3 +151,8 @@ void SkDebugger::getOverviewText(const SkTDArray* typeTimes, 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 @@ public: } } + void setPathOps(bool pathOps) { + if (NULL != fDebugCanvas) { + fDebugCanvas->setAllowSimplifyClip(pathOps); + } + } + void setMegaViz(bool megaViz) { if (NULL != fDebugCanvas) { fDebugCanvas->setMegaVizMode(megaViz); @@ -126,6 +132,8 @@ public: void getOverviewText(const SkTDArray* 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 @@ private: 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 @@ void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edg 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 @@ 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(");
"); +} + +void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) { + this->outputPointsCommon(pts, 2); + fClipStackData.appendf(", "); + this->outputScalar(weight); + fClipStackData.appendf(");
"); +} + +void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) { + SkPath::RawIter iter(path); + SkPath::FillType fillType = path.getFillType(); + fClipStackData.appendf("%sSkPath %s;
", kHTML4SpaceIndent, pathName); + fClipStackData.appendf("%s%s.setFillType(SkPath::%s);
", 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();
", 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("
static void test(skiatest::Reporter* reporter," + " const char* filename) {
"); + addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path"); + addPathData(operand, "pathB"); + fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);
", + kHTML4SpaceIndent, gOpStrs[elementOp]); + fClipStackData.appendf("}
"); + fCalledAddStackData = true; + } +} + +bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) { + if (fCalledAddStackData) { + fClipStackData.appendf("
"); + 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 @@ public: 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 @@ public: fUserMatrix = matrix; } + SkString clipStackData() const { return fClipStackData; } + //////////////////////////////////////////////////////////////////////////////// // Inherited from SkCanvas //////////////////////////////////////////////////////////////////////////////// @@ -266,6 +271,10 @@ private: SkMatrix fMatrix; SkIRect fClip; + SkString fClipStackData; + bool fCalledAddStackData; + SkPath fSaveDevPath; + bool fOverdrawViz; SkDrawFilter* fOverdrawFilter; @@ -311,6 +320,16 @@ private: 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; }; -- 2.7.4