SkDebugCanvas.cpp revision 4bf98e7e802edf43effec93bea22fecb031f65f1
17a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow/*
27a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow * Copyright 2012 Google Inc.
37a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow *
47a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow * Use of this source code is governed by a BSD-style license that can be
57a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow * found in the LICENSE file.
67a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow */
77a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
87a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "SkCanvasPriv.h"
97a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "SkClipStack.h"
107a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "SkDebugCanvas.h"
117a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "SkDrawCommand.h"
127a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "SkPaintFilterCanvas.h"
137a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "SkTextBlob.h"
1484acb1ec3f7d5e0f37d7176697c2fa876c413407Eric Fiselier#include "SkClipOpPriv.h"
157a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
167a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#if SK_SUPPORT_GPU
177a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "GrAuditTrail.h"
187a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "GrContext.h"
197a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#include "GrRenderTargetContext.h"
207a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#endif
217a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
227a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#define SKDEBUGCANVAS_VERSION                     1
237a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#define SKDEBUGCANVAS_ATTRIBUTE_VERSION           "version"
247a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS          "commands"
257a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL        "auditTrail"
267a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
277a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowclass DebugPaintFilterCanvas : public SkPaintFilterCanvas {
28cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clowpublic:
297a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    DebugPaintFilterCanvas(SkCanvas* canvas,
307a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow                           bool overdrawViz,
317a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow                           bool overrideFilterQuality,
327a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow                           SkFilterQuality quality)
337a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        : INHERITED(canvas)
347a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fOverdrawViz(overdrawViz)
357a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fOverrideFilterQuality(overrideFilterQuality)
367a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fFilterQuality(quality) {}
373cc263dda1223f031756b4b5a6f3b82c784cf52eEric Fiselier
387a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowprotected:
397a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type) const override {
40cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        if (*paint) {
417a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow            if (fOverdrawViz) {
42d812865ec051fe38a4241da6a6719ec10072efd3Marshall Clow                paint->writable()->setColor(SK_ColorRED);
43d812865ec051fe38a4241da6a6719ec10072efd3Marshall Clow                paint->writable()->setAlpha(0x08);
44d812865ec051fe38a4241da6a6719ec10072efd3Marshall Clow                paint->writable()->setBlendMode(SkBlendMode::kSrcOver);
45d812865ec051fe38a4241da6a6719ec10072efd3Marshall Clow            }
46d812865ec051fe38a4241da6a6719ec10072efd3Marshall Clow
4784acb1ec3f7d5e0f37d7176697c2fa876c413407Eric Fiselier            if (fOverrideFilterQuality) {
487a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow                paint->writable()->setFilterQuality(fFilterQuality);
49cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow            }
507a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        }
517a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        return true;
527a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
53cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
547a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    void onDrawPicture(const SkPicture* picture,
55cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow                       const SkMatrix* matrix,
567a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow                       const SkPaint* paint) override {
577a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        // We need to replay the picture onto this canvas in order to filter its internal paints.
58cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        this->SkCanvas::onDrawPicture(picture, matrix, paint);
597a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
607a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
617a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    void onDrawShadowedPicture(const SkPicture* picture,
62cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow                               const SkMatrix* matrix,
637a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow                               const SkPaint* paint,
64cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow                               const SkShadowParams& params) {
657a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#ifdef SK_EXPERIMENTAL_SHADOWING
667a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        this->SkCanvas::onDrawShadowedPicture(picture, matrix, paint, params);
67cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow#else
687a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        this->SkCanvas::onDrawPicture(picture, matrix, paint);
697a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#endif
707a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
71cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
727a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowprivate:
73cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    bool fOverdrawViz;
747a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    bool fOverrideFilterQuality;
757a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkFilterQuality fFilterQuality;
76cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
777a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    typedef SkPaintFilterCanvas INHERITED;
787a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow};
797a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
80cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall ClowSkDebugCanvas::SkDebugCanvas(int width, int height)
817a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        : INHERITED(width, height)
82cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        , fPicture(nullptr)
837a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fFilter(false)
847a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fMegaVizMode(false)
85cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        , fOverdrawViz(false)
867a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fOverrideFilterQuality(false)
877a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fFilterQuality(kNone_SkFilterQuality)
887a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        , fClipVizColor(SK_ColorTRANSPARENT)
89cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        , fDrawGpuOpBounds(false) {
907a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    fUserMatrix.reset();
91cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
927a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
937a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    // operations. This can lead to problems in the debugger which expects all
94cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    // the operations in the captured skp to appear in the debug canvas. To
957a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    // circumvent this we create a wide open clip here (an empty clip rect
967a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    // is not sufficient).
977a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    // Internally, the SkRect passed to clipRect is converted to an SkIRect and
98cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    // rounded out. The following code creates a nearly maximal rect that will
997a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    // not get collapsed by the coming conversions (Due to precision loss the
100cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    // inset has to be surprisingly large).
1017a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkIRect largeIRect = SkIRect::MakeLargest();
1027a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    largeIRect.inset(1024, 1024);
103cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    SkRect large = SkRect::Make(largeIRect);
1047a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#ifdef SK_DEBUG
1057a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkASSERT(!large.roundOut().isEmpty());
1067a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow#endif
107cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    // call the base class' version to avoid adding a draw command
1087a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    this->INHERITED::onClipRect(large, kReplace_SkClipOp, kHard_ClipEdgeStyle);
109cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow}
1107a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
1117a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall ClowSkDebugCanvas::~SkDebugCanvas() {
112cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    fCommandVector.deleteAll();
1137a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow}
1147a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
1157a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowvoid SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
116cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    fCommandVector.push(command);
1177a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow}
118cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1197a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowvoid SkDebugCanvas::draw(SkCanvas* canvas) {
12084acb1ec3f7d5e0f37d7176697c2fa876c413407Eric Fiselier    if (!fCommandVector.isEmpty()) {
1217a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        this->drawTo(canvas, fCommandVector.count() - 1);
1227a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
123cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow}
1247a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
1257a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowvoid SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
126cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    canvas->concat(fUserMatrix);
1277a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow}
128cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1297a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowint SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
1307a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkBitmap bitmap;
1317a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
132cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1337a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkCanvas canvas(bitmap);
1347a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
135cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    this->applyUserTransform(&canvas);
1367a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
137cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    int layer = 0;
1387a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkColor prev = bitmap.getColor(0,0);
1397a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    for (int i = 0; i < index; i++) {
1407a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        if (fCommandVector[i]->isVisible()) {
141cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow            fCommandVector[i]->setUserMatrix(fUserMatrix);
1427a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow            fCommandVector[i]->execute(&canvas);
1437a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        }
144cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        if (prev != bitmap.getColor(0,0)) {
1457a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow            layer = i;
146cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        }
1477a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        prev = bitmap.getColor(0,0);
1487a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
1497a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    return layer;
150cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow}
1517a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
1527a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowclass SkDebugClipVisitor : public SkCanvas::ClipVisitor {
153cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clowpublic:
1547a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
155cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1567a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    void clipRect(const SkRect& r, SkClipOp, bool doAA) override {
1577a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        SkPaint p;
1587a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setColor(SK_ColorRED);
159cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        p.setStyle(SkPaint::kStroke_Style);
1607a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setAntiAlias(doAA);
1617a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        fCanvas->drawRect(r, p);
162cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    }
1637a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    void clipRRect(const SkRRect& rr, SkClipOp, bool doAA) override {
164cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        SkPaint p;
1657a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setColor(SK_ColorGREEN);
1667a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setStyle(SkPaint::kStroke_Style);
1677a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setAntiAlias(doAA);
168cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        fCanvas->drawRRect(rr, p);
1697a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
1707a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    void clipPath(const SkPath& path, SkClipOp, bool doAA) override {
171cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        SkPaint p;
1727a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setColor(SK_ColorBLUE);
173cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        p.setStyle(SkPaint::kStroke_Style);
1747a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        p.setAntiAlias(doAA);
1757a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow        fCanvas->drawPath(path, p);
1767a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
177cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1787a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowprotected:
1797a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    SkCanvas* fCanvas;
180cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1817a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowprivate:
182cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow    typedef SkCanvas::ClipVisitor INHERITED;
1837a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow};
1847a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
1857a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow// set up the saveLayer commands so that the active ones
186cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow// return true in their 'active' method
1877a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clowvoid SkDebugCanvas::markActiveCommands(int index) {
1887a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    fActiveLayers.rewind();
189cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow
1907a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    for (int i = 0; i < fCommandVector.count(); ++i) {
191cd86b70d14974ff8a37a83ff4484f1ea04075391Marshall Clow        fCommandVector[i]->setActive(false);
1927a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow    }
1937a7960ff7f88e70e97cf8469d82286aec7eec5feMarshall Clow
194    for (int i = 0; i < index; ++i) {
195        SkDrawCommand::Action result = fCommandVector[i]->action();
196        if (SkDrawCommand::kPushLayer_Action == result) {
197            fActiveLayers.push(fCommandVector[i]);
198        } else if (SkDrawCommand::kPopLayer_Action == result) {
199            fActiveLayers.pop();
200        }
201    }
202
203    for (int i = 0; i < fActiveLayers.count(); ++i) {
204        fActiveLayers[i]->setActive(true);
205    }
206
207}
208
209void SkDebugCanvas::drawTo(SkCanvas* originalCanvas, int index, int m) {
210    SkASSERT(!fCommandVector.isEmpty());
211    SkASSERT(index < fCommandVector.count());
212
213    int saveCount = originalCanvas->save();
214
215    SkRect windowRect = SkRect::MakeWH(SkIntToScalar(originalCanvas->getBaseLayerSize().width()),
216                                       SkIntToScalar(originalCanvas->getBaseLayerSize().height()));
217
218    bool pathOpsMode = getAllowSimplifyClip();
219    originalCanvas->setAllowSimplifyClip(pathOpsMode);
220    originalCanvas->clear(SK_ColorWHITE);
221    originalCanvas->resetMatrix();
222    if (!windowRect.isEmpty()) {
223        originalCanvas->clipRect(windowRect, kReplace_SkClipOp);
224    }
225    this->applyUserTransform(originalCanvas);
226
227    DebugPaintFilterCanvas filterCanvas(originalCanvas, fOverdrawViz, fOverrideFilterQuality,
228                                        fFilterQuality);
229
230    if (fMegaVizMode) {
231        this->markActiveCommands(index);
232    }
233
234#if SK_SUPPORT_GPU
235    // If we have a GPU backend we can also visualize the op information
236    GrAuditTrail* at = nullptr;
237    if (fDrawGpuOpBounds || m != -1) {
238        // The audit trail must be obtained from the original canvas.
239        at = this->getAuditTrail(originalCanvas);
240    }
241#endif
242
243    for (int i = 0; i <= index; i++) {
244        if (i == index && fFilter) {
245            filterCanvas.clear(0xAAFFFFFF);
246        }
247
248#if SK_SUPPORT_GPU
249        // We need to flush any pending operations, or they might combine with commands below.
250        // Previous operations were not registered with the audit trail when they were
251        // created, so if we allow them to combine, the audit trail will fail to find them.
252        filterCanvas.flush();
253
254        GrAuditTrail::AutoCollectOps* acb = nullptr;
255        if (at) {
256            acb = new GrAuditTrail::AutoCollectOps(at, i);
257        }
258#endif
259
260        if (fCommandVector[i]->isVisible()) {
261            if (fMegaVizMode && fCommandVector[i]->active()) {
262                // "active" commands execute their visualization behaviors:
263                //     All active saveLayers get replaced with saves so all draws go to the
264                //     visible canvas.
265                //     All active culls draw their cull box
266                fCommandVector[i]->vizExecute(&filterCanvas);
267            } else {
268                fCommandVector[i]->setUserMatrix(fUserMatrix);
269                fCommandVector[i]->execute(&filterCanvas);
270            }
271        }
272#if SK_SUPPORT_GPU
273        if (at && acb) {
274            delete acb;
275        }
276#endif
277    }
278
279    if (SkColorGetA(fClipVizColor) != 0) {
280        filterCanvas.save();
281        #define LARGE_COORD 1000000000
282        filterCanvas.clipRect(
283                SkRect::MakeLTRB(-LARGE_COORD, -LARGE_COORD, LARGE_COORD, LARGE_COORD),
284                kReverseDifference_SkClipOp);
285        SkPaint clipPaint;
286        clipPaint.setColor(fClipVizColor);
287        filterCanvas.drawPaint(clipPaint);
288        filterCanvas.restore();
289    }
290
291    if (fMegaVizMode) {
292        filterCanvas.save();
293        // nuke the CTM
294        filterCanvas.resetMatrix();
295        // turn off clipping
296        if (!windowRect.isEmpty()) {
297            SkRect r = windowRect;
298            r.outset(SK_Scalar1, SK_Scalar1);
299            filterCanvas.clipRect(r, kReplace_SkClipOp);
300        }
301        // visualize existing clips
302        SkDebugClipVisitor visitor(&filterCanvas);
303
304        filterCanvas.replayClips(&visitor);
305
306        filterCanvas.restore();
307    }
308    if (pathOpsMode) {
309        this->resetClipStackData();
310        const SkClipStack* clipStack = filterCanvas.getClipStack();
311        SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
312        const SkClipStack::Element* element;
313        SkPath devPath;
314        while ((element = iter.next())) {
315            SkClipStack::Element::Type type = element->getType();
316            SkPath operand;
317            if (type != SkClipStack::Element::kEmpty_Type) {
318               element->asPath(&operand);
319            }
320            SkClipOp elementOp = element->getOp();
321            this->addClipStackData(devPath, operand, elementOp);
322            if (elementOp == kReplace_SkClipOp) {
323                devPath = operand;
324            } else {
325                Op(devPath, operand, (SkPathOp) elementOp, &devPath);
326            }
327        }
328        this->lastClipStackData(devPath);
329    }
330    fMatrix = filterCanvas.getTotalMatrix();
331    if (!filterCanvas.getClipDeviceBounds(&fClip)) {
332        fClip.setEmpty();
333    }
334
335    filterCanvas.restoreToCount(saveCount);
336
337#if SK_SUPPORT_GPU
338    // draw any ops if required and issue a full reset onto GrAuditTrail
339    if (at) {
340        // just in case there is global reordering, we flush the canvas before querying
341        // GrAuditTrail
342        GrAuditTrail::AutoEnable ae(at);
343        filterCanvas.flush();
344
345        // we pick three colorblind-safe colors, 75% alpha
346        static const SkColor kTotalBounds = SkColorSetARGB(0xC0, 0x6A, 0x3D, 0x9A);
347        static const SkColor kCommandOpBounds = SkColorSetARGB(0xC0, 0xE3, 0x1A, 0x1C);
348        static const SkColor kOtherOpBounds = SkColorSetARGB(0xC0, 0xFF, 0x7F, 0x00);
349
350        // get the render target of the top device (from the original canvas) so we can ignore ops
351        // drawn offscreen
352        GrRenderTargetContext* rtc =
353                originalCanvas->internal_private_accessTopLayerRenderTargetContext();
354        GrGpuResource::UniqueID rtID = rtc->accessRenderTarget()->uniqueID();
355
356        // get the bounding boxes to draw
357        SkTArray<GrAuditTrail::OpInfo> childrenBounds;
358        if (m == -1) {
359            at->getBoundsByClientID(&childrenBounds, index);
360        } else {
361            // the client wants us to draw the mth op
362            at->getBoundsByOpListID(&childrenBounds.push_back(), m);
363        }
364        SkPaint paint;
365        paint.setStyle(SkPaint::kStroke_Style);
366        paint.setStrokeWidth(1);
367        for (int i = 0; i < childrenBounds.count(); i++) {
368            if (childrenBounds[i].fRenderTargetUniqueID != rtID) {
369                // offscreen draw, ignore for now
370                continue;
371            }
372            paint.setColor(kTotalBounds);
373            filterCanvas.drawRect(childrenBounds[i].fBounds, paint);
374            for (int j = 0; j < childrenBounds[i].fOps.count(); j++) {
375                const GrAuditTrail::OpInfo::Op& op = childrenBounds[i].fOps[j];
376                if (op.fClientID != index) {
377                    paint.setColor(kOtherOpBounds);
378                } else {
379                    paint.setColor(kCommandOpBounds);
380                }
381                filterCanvas.drawRect(op.fBounds, paint);
382            }
383        }
384    }
385#endif
386    this->cleanupAuditTrail(originalCanvas);
387}
388
389void SkDebugCanvas::deleteDrawCommandAt(int index) {
390    SkASSERT(index < fCommandVector.count());
391    delete fCommandVector[index];
392    fCommandVector.remove(index);
393}
394
395SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
396    SkASSERT(index < fCommandVector.count());
397    return fCommandVector[index];
398}
399
400void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
401    SkASSERT(index < fCommandVector.count());
402    delete fCommandVector[index];
403    fCommandVector[index] = command;
404}
405
406const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
407    SkASSERT(index < fCommandVector.count());
408    return fCommandVector[index]->Info();
409}
410
411bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
412    SkASSERT(index < fCommandVector.count());
413    return fCommandVector[index]->isVisible();
414}
415
416const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
417    return fCommandVector;
418}
419
420SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
421    return fCommandVector;
422}
423
424GrAuditTrail* SkDebugCanvas::getAuditTrail(SkCanvas* canvas) {
425    GrAuditTrail* at = nullptr;
426#if SK_SUPPORT_GPU
427    GrContext* ctx = canvas->getGrContext();
428    if (ctx) {
429        at = ctx->getAuditTrail();
430    }
431#endif
432    return at;
433}
434
435void SkDebugCanvas::drawAndCollectOps(int n, SkCanvas* canvas) {
436#if SK_SUPPORT_GPU
437    GrAuditTrail* at = this->getAuditTrail(canvas);
438    if (at) {
439        // loop over all of the commands and draw them, this is to collect reordering
440        // information
441        for (int i = 0; i < this->getSize() && i <= n; i++) {
442            GrAuditTrail::AutoCollectOps enable(at, i);
443            fCommandVector[i]->execute(canvas);
444        }
445
446        // in case there is some kind of global reordering
447        {
448            GrAuditTrail::AutoEnable ae(at);
449            canvas->flush();
450        }
451    }
452#endif
453}
454
455void SkDebugCanvas::cleanupAuditTrail(SkCanvas* canvas) {
456    GrAuditTrail* at = this->getAuditTrail(canvas);
457    if (at) {
458#if SK_SUPPORT_GPU
459        GrAuditTrail::AutoEnable ae(at);
460        at->fullReset();
461#endif
462    }
463}
464
465Json::Value SkDebugCanvas::toJSON(UrlDataManager& urlDataManager, int n, SkCanvas* canvas) {
466    this->drawAndCollectOps(n, canvas);
467
468    // now collect json
469#if SK_SUPPORT_GPU
470    GrAuditTrail* at = this->getAuditTrail(canvas);
471#endif
472    Json::Value result = Json::Value(Json::objectValue);
473    result[SKDEBUGCANVAS_ATTRIBUTE_VERSION] = Json::Value(SKDEBUGCANVAS_VERSION);
474    Json::Value commands = Json::Value(Json::arrayValue);
475    for (int i = 0; i < this->getSize() && i <= n; i++) {
476        commands[i] = this->getDrawCommandAt(i)->toJSON(urlDataManager);
477#if SK_SUPPORT_GPU
478        if (at) {
479            // TODO if this is inefficient we could add a method to GrAuditTrail which takes
480            // a Json::Value and is only compiled in this file
481            Json::Value parsedFromString;
482            Json::Reader reader;
483            SkAssertResult(reader.parse(at->toJson(i).c_str(), parsedFromString));
484
485            commands[i][SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL] = parsedFromString;
486        }
487#endif
488    }
489    this->cleanupAuditTrail(canvas);
490    result[SKDEBUGCANVAS_ATTRIBUTE_COMMANDS] = commands;
491    return result;
492}
493
494Json::Value SkDebugCanvas::toJSONOpList(int n, SkCanvas* canvas) {
495    this->drawAndCollectOps(n, canvas);
496
497    Json::Value parsedFromString;
498#if SK_SUPPORT_GPU
499    GrAuditTrail* at = this->getAuditTrail(canvas);
500    if (at) {
501        GrAuditTrail::AutoManageOpList enable(at);
502        Json::Reader reader;
503        SkAssertResult(reader.parse(at->toJson().c_str(), parsedFromString));
504    }
505#endif
506    this->cleanupAuditTrail(canvas);
507    return parsedFromString;
508}
509
510void SkDebugCanvas::setOverdrawViz(bool overdrawViz) {
511    fOverdrawViz = overdrawViz;
512}
513
514void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
515    fOverrideFilterQuality = overrideTexFiltering;
516    fFilterQuality = quality;
517}
518
519void SkDebugCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
520    this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
521}
522
523void SkDebugCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
524    this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
525}
526
527void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
528    this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
529}
530
531void SkDebugCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
532    this->addDrawCommand(new SkClipRegionCommand(region, op));
533}
534
535void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
536    this->addDrawCommand(new SkConcatCommand(matrix));
537    this->INHERITED::didConcat(matrix);
538}
539
540void SkDebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
541    this->addDrawCommand(new SkDrawAnnotationCommand(rect, key, sk_ref_sp(value)));
542}
543
544void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
545                                 SkScalar top, const SkPaint* paint) {
546    this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
547}
548
549void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
550                                     const SkPaint* paint, SrcRectConstraint constraint) {
551    this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint,
552                                                     (SrcRectConstraint)constraint));
553}
554
555void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
556                                     const SkRect& dst, const SkPaint* paint) {
557    this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
558}
559
560void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
561                                const SkPaint* paint) {
562    this->addDrawCommand(new SkDrawImageCommand(image, left, top, paint));
563}
564
565void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
566                                    const SkPaint* paint, SrcRectConstraint constraint) {
567    this->addDrawCommand(new SkDrawImageRectCommand(image, src, dst, paint, constraint));
568}
569
570void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
571    this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
572}
573
574void SkDebugCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
575                               bool useCenter, const SkPaint& paint) {
576    this->addDrawCommand(new SkDrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint));
577}
578
579void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
580    this->addDrawCommand(new SkDrawPaintCommand(paint));
581}
582
583void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
584    this->addDrawCommand(new SkDrawPathCommand(path, paint));
585}
586
587void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
588                                  const SkMatrix* matrix,
589                                  const SkPaint* paint) {
590    this->addDrawCommand(new SkBeginDrawPictureCommand(picture, matrix, paint));
591    SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
592    picture->playback(this);
593    this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
594}
595
596void SkDebugCanvas::onDrawShadowedPicture(const SkPicture* picture,
597                                          const SkMatrix* matrix,
598                                          const SkPaint* paint,
599                                          const SkShadowParams& params) {
600    this->addDrawCommand(new SkBeginDrawShadowedPictureCommand(picture, matrix, paint, params));
601    SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
602    picture->playback(this);
603    this->addDrawCommand(new SkEndDrawShadowedPictureCommand(SkToBool(matrix) || SkToBool(paint)));
604}
605
606void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count,
607                                 const SkPoint pts[], const SkPaint& paint) {
608    this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
609}
610
611void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
612                                  const SkPaint& paint) {
613    this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint));
614}
615
616void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
617                                   SkScalar constY, const SkPaint& paint) {
618    this->addDrawCommand(
619        new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint));
620}
621
622void SkDebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
623    // NOTE(chudy): Messing up when renamed to DrawRect... Why?
624    addDrawCommand(new SkDrawRectCommand(rect, paint));
625}
626
627void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
628    this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
629}
630
631void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
632                                 const SkPaint& paint) {
633    this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
634}
635
636void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
637                               const SkPaint& paint) {
638    this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint));
639}
640
641void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
642                                     const SkMatrix* matrix, const SkPaint& paint) {
643    this->addDrawCommand(
644        new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint));
645}
646
647void SkDebugCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
648                                      const SkRect* cull, const SkPaint& paint) {
649    this->addDrawCommand(new SkDrawTextRSXformCommand(text, byteLength, xform, cull, paint));
650}
651
652void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
653                                   const SkPaint& paint) {
654    this->addDrawCommand(new SkDrawTextBlobCommand(sk_ref_sp(const_cast<SkTextBlob*>(blob)),
655                                                   x, y, paint));
656}
657
658void SkDebugCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
659                                const SkPoint texCoords[4], SkBlendMode bmode,
660                                const SkPaint& paint) {
661    this->addDrawCommand(new SkDrawPatchCommand(cubics, colors, texCoords, bmode, paint));
662}
663
664void SkDebugCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
665                                   const SkPoint texs[], const SkColor colors[],
666                                   SkBlendMode bmode, const uint16_t indices[], int indexCount,
667                                   const SkPaint& paint) {
668    this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices,
669                         texs, colors, bmode, indices, indexCount, paint));
670}
671
672void SkDebugCanvas::willRestore() {
673    this->addDrawCommand(new SkRestoreCommand());
674    this->INHERITED::willRestore();
675}
676
677void SkDebugCanvas::willSave() {
678    this->addDrawCommand(new SkSaveCommand());
679    this->INHERITED::willSave();
680}
681
682SkCanvas::SaveLayerStrategy SkDebugCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
683    this->addDrawCommand(new SkSaveLayerCommand(rec));
684    (void)this->INHERITED::getSaveLayerStrategy(rec);
685    // No need for a full layer.
686    return kNoLayer_SaveLayerStrategy;
687}
688
689void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
690    this->addDrawCommand(new SkSetMatrixCommand(matrix));
691    this->INHERITED::didSetMatrix(matrix);
692}
693
694void SkDebugCanvas::didTranslateZ(SkScalar z) {
695#ifdef SK_EXPERIMENTAL_SHADOWING
696    this->addDrawCommand(new SkTranslateZCommand(z));
697    this->INHERITED::didTranslateZ(z);
698#endif
699}
700
701void SkDebugCanvas::toggleCommand(int index, bool toggle) {
702    SkASSERT(index < fCommandVector.count());
703    fCommandVector[index]->setVisible(toggle);
704}
705
706static const char* gFillTypeStrs[] = {
707    "kWinding_FillType",
708    "kEvenOdd_FillType",
709    "kInverseWinding_FillType",
710    "kInverseEvenOdd_FillType"
711};
712
713static const char* gOpStrs[] = {
714    "kDifference_PathOp",
715    "kIntersect_PathOp",
716    "kUnion_PathOp",
717    "kXor_PathOp",
718    "kReverseDifference_PathOp",
719};
720
721static const char kHTML4SpaceIndent[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
722
723void SkDebugCanvas::outputScalar(SkScalar num) {
724    if (num == (int) num) {
725        fClipStackData.appendf("%d", (int) num);
726    } else {
727        SkString str;
728        str.printf("%1.9g", num);
729        int width = (int) str.size();
730        const char* cStr = str.c_str();
731        while (cStr[width - 1] == '0') {
732            --width;
733        }
734        str.resize(width);
735        fClipStackData.appendf("%sf", str.c_str());
736    }
737}
738
739void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) {
740    for (int index = 0; index < count; ++index) {
741        this->outputScalar(pts[index].fX);
742        fClipStackData.appendf(", ");
743        this->outputScalar(pts[index].fY);
744        if (index + 1 < count) {
745            fClipStackData.appendf(", ");
746        }
747    }
748}
749
750void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
751    this->outputPointsCommon(pts, count);
752    fClipStackData.appendf(");<br>");
753}
754
755void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) {
756    this->outputPointsCommon(pts, 2);
757    fClipStackData.appendf(", ");
758    this->outputScalar(weight);
759    fClipStackData.appendf(");<br>");
760}
761
762void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) {
763    SkPath::RawIter iter(path);
764    SkPath::FillType fillType = path.getFillType();
765    fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName);
766    fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName,
767            gFillTypeStrs[fillType]);
768    iter.setPath(path);
769    uint8_t verb;
770    SkPoint pts[4];
771    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
772        switch (verb) {
773            case SkPath::kMove_Verb:
774                fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
775                this->outputPoints(&pts[0], 1);
776                continue;
777            case SkPath::kLine_Verb:
778                fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
779                this->outputPoints(&pts[1], 1);
780                break;
781            case SkPath::kQuad_Verb:
782                fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
783                this->outputPoints(&pts[1], 2);
784                break;
785            case SkPath::kConic_Verb:
786                fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
787                this->outputConicPoints(&pts[1], iter.conicWeight());
788                break;
789            case SkPath::kCubic_Verb:
790                fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
791                this->outputPoints(&pts[1], 3);
792                break;
793            case SkPath::kClose_Verb:
794                fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
795                break;
796            default:
797                SkDEBUGFAIL("bad verb");
798                return;
799        }
800    }
801}
802
803void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand,
804                                     SkClipOp elementOp) {
805    if (elementOp == kReplace_SkClipOp) {
806        if (!lastClipStackData(devPath)) {
807            fSaveDevPath = operand;
808        }
809        fCalledAddStackData = false;
810    } else {
811        fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter,"
812            " const char* filename) {<br>");
813        addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path");
814        addPathData(operand, "pathB");
815        fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>",
816            kHTML4SpaceIndent, gOpStrs[static_cast<int>(elementOp)]);
817        fClipStackData.appendf("}<br>");
818        fCalledAddStackData = true;
819    }
820}
821
822bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
823    if (fCalledAddStackData) {
824        fClipStackData.appendf("<br>");
825        addPathData(devPath, "pathOut");
826        return true;
827    }
828    return false;
829}
830