diff options
author | ethannicholas <ethannicholas@google.com> | 2016-02-09 12:44:06 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-09 12:44:06 -0800 |
commit | 3cb954245cecf262d740a83913681b9fe4b41555 (patch) | |
tree | c334c212efc44b713b4750e44361deaa624a24cc /tools/debugger/SkDebugCanvas.cpp | |
parent | a23659fcee922be39d64ac3abff2cf9a4f97921b (diff) |
moved debugger support files from src/utils/debugger to tools/debugger
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1678893003
Review URL: https://codereview.chromium.org/1678893003
Diffstat (limited to 'tools/debugger/SkDebugCanvas.cpp')
-rw-r--r-- | tools/debugger/SkDebugCanvas.cpp | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/tools/debugger/SkDebugCanvas.cpp b/tools/debugger/SkDebugCanvas.cpp new file mode 100644 index 0000000000..c2dd8f8a19 --- /dev/null +++ b/tools/debugger/SkDebugCanvas.cpp @@ -0,0 +1,624 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkClipStack.h" +#include "SkDebugCanvas.h" +#include "SkDrawCommand.h" +#include "SkDevice.h" +#include "SkPaintFilterCanvas.h" +#include "SkOverdrawMode.h" + +class DebugPaintFilterCanvas : public SkPaintFilterCanvas { +public: + DebugPaintFilterCanvas(int width, + int height, + bool overdrawViz, + bool overrideFilterQuality, + SkFilterQuality quality) + : INHERITED(width, height) + , fOverdrawXfermode(overdrawViz ? SkOverdrawMode::Create() : nullptr) + , fOverrideFilterQuality(overrideFilterQuality) + , fFilterQuality(quality) {} + +protected: + bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type) const override { + if (*paint) { + if (nullptr != fOverdrawXfermode.get()) { + paint->writable()->setAntiAlias(false); + paint->writable()->setXfermode(fOverdrawXfermode.get()); + } + + if (fOverrideFilterQuality) { + paint->writable()->setFilterQuality(fFilterQuality); + } + } + return true; + } + + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override { + // We need to replay the picture onto this canvas in order to filter its internal paints. + this->SkCanvas::onDrawPicture(picture, matrix, paint); + } + +private: + SkAutoTUnref<SkXfermode> fOverdrawXfermode; + + bool fOverrideFilterQuality; + SkFilterQuality fFilterQuality; + + typedef SkPaintFilterCanvas INHERITED; +}; + +SkDebugCanvas::SkDebugCanvas(int width, int height) + : INHERITED(width, height) + , fPicture(nullptr) + , fFilter(false) + , fMegaVizMode(false) + , fOverdrawViz(false) + , fOverrideFilterQuality(false) + , fFilterQuality(kNone_SkFilterQuality) { + fUserMatrix.reset(); + + // SkPicturePlayback uses the base-class' quickReject calls to cull clipped + // operations. This can lead to problems in the debugger which expects all + // the operations in the captured skp to appear in the debug canvas. To + // circumvent this we create a wide open clip here (an empty clip rect + // is not sufficient). + // Internally, the SkRect passed to clipRect is converted to an SkIRect and + // rounded out. The following code creates a nearly maximal rect that will + // not get collapsed by the coming conversions (Due to precision loss the + // inset has to be surprisingly large). + SkIRect largeIRect = SkIRect::MakeLargest(); + largeIRect.inset(1024, 1024); + SkRect large = SkRect::Make(largeIRect); +#ifdef SK_DEBUG + SkASSERT(!large.roundOut().isEmpty()); +#endif + // call the base class' version to avoid adding a draw command + this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle); +} + +SkDebugCanvas::~SkDebugCanvas() { + fCommandVector.deleteAll(); +} + +void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) { + fCommandVector.push(command); +} + +void SkDebugCanvas::draw(SkCanvas* canvas) { + if (!fCommandVector.isEmpty()) { + this->drawTo(canvas, fCommandVector.count() - 1); + } +} + +void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) { + canvas->concat(fUserMatrix); +} + +int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) { + SkBitmap bitmap; + bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1)); + + SkCanvas canvas(bitmap); + canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y)); + this->applyUserTransform(&canvas); + + int layer = 0; + SkColor prev = bitmap.getColor(0,0); + for (int i = 0; i < index; i++) { + if (fCommandVector[i]->isVisible()) { + fCommandVector[i]->setUserMatrix(fUserMatrix); + fCommandVector[i]->execute(&canvas); + } + if (prev != bitmap.getColor(0,0)) { + layer = i; + } + prev = bitmap.getColor(0,0); + } + return layer; +} + +class SkDebugClipVisitor : public SkCanvas::ClipVisitor { +public: + SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {} + + void clipRect(const SkRect& r, SkRegion::Op, bool doAA) override { + SkPaint p; + p.setColor(SK_ColorRED); + p.setStyle(SkPaint::kStroke_Style); + p.setAntiAlias(doAA); + fCanvas->drawRect(r, p); + } + void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) override { + SkPaint p; + p.setColor(SK_ColorGREEN); + p.setStyle(SkPaint::kStroke_Style); + p.setAntiAlias(doAA); + fCanvas->drawRRect(rr, p); + } + void clipPath(const SkPath& path, SkRegion::Op, bool doAA) override { + SkPaint p; + p.setColor(SK_ColorBLUE); + p.setStyle(SkPaint::kStroke_Style); + p.setAntiAlias(doAA); + fCanvas->drawPath(path, p); + } + +protected: + SkCanvas* fCanvas; + +private: + typedef SkCanvas::ClipVisitor INHERITED; +}; + +// set up the saveLayer commands so that the active ones +// return true in their 'active' method +void SkDebugCanvas::markActiveCommands(int index) { + fActiveLayers.rewind(); + + for (int i = 0; i < fCommandVector.count(); ++i) { + fCommandVector[i]->setActive(false); + } + + for (int i = 0; i < index; ++i) { + SkDrawCommand::Action result = fCommandVector[i]->action(); + if (SkDrawCommand::kPushLayer_Action == result) { + fActiveLayers.push(fCommandVector[i]); + } else if (SkDrawCommand::kPopLayer_Action == result) { + fActiveLayers.pop(); + } + } + + for (int i = 0; i < fActiveLayers.count(); ++i) { + fActiveLayers[i]->setActive(true); + } + +} + +void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) { + SkASSERT(!fCommandVector.isEmpty()); + SkASSERT(index < fCommandVector.count()); + + int saveCount = canvas->save(); + + SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()), + SkIntToScalar(canvas->getBaseLayerSize().height())); + + bool pathOpsMode = getAllowSimplifyClip(); + canvas->setAllowSimplifyClip(pathOpsMode); + canvas->clear(SK_ColorTRANSPARENT); + canvas->resetMatrix(); + if (!windowRect.isEmpty()) { + canvas->clipRect(windowRect, SkRegion::kReplace_Op); + } + this->applyUserTransform(canvas); + + if (fPaintFilterCanvas) { + fPaintFilterCanvas->addCanvas(canvas); + canvas = fPaintFilterCanvas.get(); + } + + if (fMegaVizMode) { + this->markActiveCommands(index); + } + + for (int i = 0; i <= index; i++) { + if (i == index && fFilter) { + canvas->clear(0xAAFFFFFF); + } + + if (fCommandVector[i]->isVisible()) { + if (fMegaVizMode && fCommandVector[i]->active()) { + // "active" commands execute their visualization behaviors: + // All active saveLayers get replaced with saves so all draws go to the + // visible canvas. + // All active culls draw their cull box + fCommandVector[i]->vizExecute(canvas); + } else { + fCommandVector[i]->setUserMatrix(fUserMatrix); + fCommandVector[i]->execute(canvas); + } + } + } + + if (fMegaVizMode) { + canvas->save(); + // nuke the CTM + canvas->resetMatrix(); + // turn off clipping + if (!windowRect.isEmpty()) { + SkRect r = windowRect; + r.outset(SK_Scalar1, SK_Scalar1); + canvas->clipRect(r, SkRegion::kReplace_Op); + } + // visualize existing clips + SkDebugClipVisitor visitor(canvas); + + canvas->replayClips(&visitor); + + 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(); + } + + canvas->restoreToCount(saveCount); + + if (fPaintFilterCanvas) { + fPaintFilterCanvas->removeAll(); + } +} + +void SkDebugCanvas::deleteDrawCommandAt(int index) { + SkASSERT(index < fCommandVector.count()); + delete fCommandVector[index]; + fCommandVector.remove(index); +} + +SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) { + SkASSERT(index < fCommandVector.count()); + return fCommandVector[index]; +} + +void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) { + SkASSERT(index < fCommandVector.count()); + delete fCommandVector[index]; + fCommandVector[index] = command; +} + +const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const { + SkASSERT(index < fCommandVector.count()); + return fCommandVector[index]->Info(); +} + +bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) { + SkASSERT(index < fCommandVector.count()); + return fCommandVector[index]->isVisible(); +} + +const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const { + return fCommandVector; +} + +SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() { + return fCommandVector; +} + +void SkDebugCanvas::updatePaintFilterCanvas() { + if (!fOverdrawViz && !fOverrideFilterQuality) { + fPaintFilterCanvas.reset(nullptr); + return; + } + + const SkImageInfo info = this->imageInfo(); + fPaintFilterCanvas.reset(new DebugPaintFilterCanvas(info.width(), info.height(), fOverdrawViz, + fOverrideFilterQuality, fFilterQuality)); +} + +void SkDebugCanvas::setOverdrawViz(bool overdrawViz) { + if (fOverdrawViz == overdrawViz) { + return; + } + + fOverdrawViz = overdrawViz; + this->updatePaintFilterCanvas(); +} + +void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) { + if (fOverrideFilterQuality == overrideTexFiltering && fFilterQuality == quality) { + return; + } + + fOverrideFilterQuality = overrideTexFiltering; + fFilterQuality = quality; + this->updatePaintFilterCanvas(); +} + +void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle)); +} + +void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle)); +} + +void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle)); +} + +void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) { + this->addDrawCommand(new SkClipRegionCommand(region, op)); +} + +void SkDebugCanvas::didConcat(const SkMatrix& matrix) { + this->addDrawCommand(new SkConcatCommand(matrix)); + this->INHERITED::didConcat(matrix); +} + +void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, + SkScalar top, const SkPaint* paint) { + this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint)); +} + +void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) { + this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, + (SrcRectConstraint)constraint)); +} + +void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { + this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint)); +} + +void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, + const SkPaint* paint) { + this->addDrawCommand(new SkDrawImageCommand(image, left, top, paint)); +} + +void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) { + this->addDrawCommand(new SkDrawImageRectCommand(image, src, dst, paint, constraint)); +} + +void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { + this->addDrawCommand(new SkDrawOvalCommand(oval, paint)); +} + +void SkDebugCanvas::onDrawPaint(const SkPaint& paint) { + this->addDrawCommand(new SkDrawPaintCommand(paint)); +} + +void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { + this->addDrawCommand(new SkDrawPathCommand(path, paint)); +} + +void SkDebugCanvas::onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) { + this->addDrawCommand(new SkBeginDrawPictureCommand(picture, matrix, paint)); + this->INHERITED::onDrawPicture(picture, matrix, paint); + this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint))); +} + +void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint) { + this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint)); +} + +void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], + const SkPaint& paint) { + this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint)); +} + +void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], + SkScalar constY, const SkPaint& paint) { + this->addDrawCommand( + new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint)); +} + +void SkDebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { + // NOTE(chudy): Messing up when renamed to DrawRect... Why? + addDrawCommand(new SkDrawRectCommand(rect, paint)); +} + +void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { + this->addDrawCommand(new SkDrawRRectCommand(rrect, paint)); +} + +void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, + const SkPaint& paint) { + this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint)); +} + +void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& paint) { + this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint)); +} + +void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, + const SkMatrix* matrix, const SkPaint& paint) { + this->addDrawCommand( + new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint)); +} + +void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + this->addDrawCommand(new SkDrawTextBlobCommand(blob, x, y, paint)); +} + +void SkDebugCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkXfermode* xmode, + const SkPaint& paint) { + this->addDrawCommand(new SkDrawPatchCommand(cubics, colors, texCoords, xmode, paint)); +} + +void SkDebugCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], + const SkPoint texs[], const SkColor colors[], + SkXfermode*, const uint16_t indices[], int indexCount, + const SkPaint& paint) { + this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices, + texs, colors, nullptr, indices, indexCount, paint)); +} + +void SkDebugCanvas::willRestore() { + this->addDrawCommand(new SkRestoreCommand()); + this->INHERITED::willRestore(); +} + +void SkDebugCanvas::willSave() { + this->addDrawCommand(new SkSaveCommand()); + this->INHERITED::willSave(); +} + +SkCanvas::SaveLayerStrategy SkDebugCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { + this->addDrawCommand(new SkSaveLayerCommand(rec)); + (void)this->INHERITED::getSaveLayerStrategy(rec); + // No need for a full layer. + return kNoLayer_SaveLayerStrategy; +} + +void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) { + this->addDrawCommand(new SkSetMatrixCommand(matrix)); + this->INHERITED::didSetMatrix(matrix); +} + +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; +} |