1
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkColorPriv.h"
11#include "SkDebugCanvas.h"
12#include "SkDrawCommand.h"
13#include "SkDevice.h"
14#include "SkPaintFilterCanvas.h"
15#include "SkXfermode.h"
16
17namespace {
18
19class OverdrawXfermode : public SkXfermode {
20public:
21    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override {
22        // This table encodes the color progression of the overdraw visualization
23        static const SkPMColor gTable[] = {
24            SkPackARGB32(0x00, 0x00, 0x00, 0x00),
25            SkPackARGB32(0xFF, 128, 158, 255),
26            SkPackARGB32(0xFF, 170, 185, 212),
27            SkPackARGB32(0xFF, 213, 195, 170),
28            SkPackARGB32(0xFF, 255, 192, 127),
29            SkPackARGB32(0xFF, 255, 185, 85),
30            SkPackARGB32(0xFF, 255, 165, 42),
31            SkPackARGB32(0xFF, 255, 135, 0),
32            SkPackARGB32(0xFF, 255,  95, 0),
33            SkPackARGB32(0xFF, 255,  50, 0),
34            SkPackARGB32(0xFF, 255,  0, 0)
35        };
36
37
38        int idx;
39        if (SkColorGetR(dst) < 64) { // 0
40            idx = 0;
41        } else if (SkColorGetG(dst) < 25) { // 10
42            idx = 9;  // cap at 9 for upcoming increment
43        } else if ((SkColorGetB(dst)+21)/42 > 0) { // 1-6
44            idx = 7 - (SkColorGetB(dst)+21)/42;
45        } else { // 7-9
46            idx = 10 - (SkColorGetG(dst)+22)/45;
47        }
48        ++idx;
49        SkASSERT(idx < (int)SK_ARRAY_COUNT(gTable));
50
51        return gTable[idx];
52    }
53
54    Factory getFactory() const override { return NULL; }
55#ifndef SK_IGNORE_TO_STRING
56    void toString(SkString* str) const override { str->set("OverdrawXfermode"); }
57#endif
58};
59
60class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
61public:
62    DebugPaintFilterCanvas(int width, int height, bool overdrawViz, bool overrideFilterQuality,
63                           SkFilterQuality quality)
64        : INHERITED(width, height)
65        , fOverdrawXfermode(overdrawViz ? SkNEW(OverdrawXfermode) : NULL)
66        , fOverrideFilterQuality(overrideFilterQuality)
67        , fFilterQuality(quality) { }
68
69protected:
70    void onFilterPaint(SkPaint* paint, Type) const override {
71        if (NULL != fOverdrawXfermode.get()) {
72            paint->setAntiAlias(false);
73            paint->setXfermode(fOverdrawXfermode.get());
74        }
75
76        if (fOverrideFilterQuality) {
77            paint->setFilterQuality(fFilterQuality);
78        }
79    }
80
81    void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
82        // We need to replay the picture onto this canvas in order to filter its internal paints.
83        this->SkCanvas::onDrawPicture(picture, matrix, paint);
84    }
85
86private:
87    SkAutoTUnref<SkXfermode> fOverdrawXfermode;
88
89    bool fOverrideFilterQuality;
90    SkFilterQuality fFilterQuality;
91
92    typedef SkPaintFilterCanvas INHERITED;
93};
94
95}
96
97SkDebugCanvas::SkDebugCanvas(int width, int height)
98        : INHERITED(width, height)
99        , fPicture(NULL)
100        , fFilter(false)
101        , fMegaVizMode(false)
102        , fOverdrawViz(false)
103        , fOverrideFilterQuality(false)
104        , fFilterQuality(kNone_SkFilterQuality) {
105    fUserMatrix.reset();
106
107    // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
108    // operations. This can lead to problems in the debugger which expects all
109    // the operations in the captured skp to appear in the debug canvas. To
110    // circumvent this we create a wide open clip here (an empty clip rect
111    // is not sufficient).
112    // Internally, the SkRect passed to clipRect is converted to an SkIRect and
113    // rounded out. The following code creates a nearly maximal rect that will
114    // not get collapsed by the coming conversions (Due to precision loss the
115    // inset has to be surprisingly large).
116    SkIRect largeIRect = SkIRect::MakeLargest();
117    largeIRect.inset(1024, 1024);
118    SkRect large = SkRect::Make(largeIRect);
119#ifdef SK_DEBUG
120    SkASSERT(!large.roundOut().isEmpty());
121#endif
122    // call the base class' version to avoid adding a draw command
123    this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
124}
125
126SkDebugCanvas::~SkDebugCanvas() {
127    fCommandVector.deleteAll();
128}
129
130void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
131    fCommandVector.push(command);
132}
133
134void SkDebugCanvas::draw(SkCanvas* canvas) {
135    if (!fCommandVector.isEmpty()) {
136        this->drawTo(canvas, fCommandVector.count() - 1);
137    }
138}
139
140void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
141    canvas->concat(fUserMatrix);
142}
143
144int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
145    SkBitmap bitmap;
146    bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
147
148    SkCanvas canvas(bitmap);
149    canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
150    this->applyUserTransform(&canvas);
151
152    int layer = 0;
153    SkColor prev = bitmap.getColor(0,0);
154    for (int i = 0; i < index; i++) {
155        if (fCommandVector[i]->isVisible()) {
156            fCommandVector[i]->setUserMatrix(fUserMatrix);
157            fCommandVector[i]->execute(&canvas);
158        }
159        if (prev != bitmap.getColor(0,0)) {
160            layer = i;
161        }
162        prev = bitmap.getColor(0,0);
163    }
164    return layer;
165}
166
167class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
168public:
169    SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
170
171    void clipRect(const SkRect& r, SkRegion::Op, bool doAA) override {
172        SkPaint p;
173        p.setColor(SK_ColorRED);
174        p.setStyle(SkPaint::kStroke_Style);
175        p.setAntiAlias(doAA);
176        fCanvas->drawRect(r, p);
177    }
178    void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) override {
179        SkPaint p;
180        p.setColor(SK_ColorGREEN);
181        p.setStyle(SkPaint::kStroke_Style);
182        p.setAntiAlias(doAA);
183        fCanvas->drawRRect(rr, p);
184    }
185    void clipPath(const SkPath& path, SkRegion::Op, bool doAA) override {
186        SkPaint p;
187        p.setColor(SK_ColorBLUE);
188        p.setStyle(SkPaint::kStroke_Style);
189        p.setAntiAlias(doAA);
190        fCanvas->drawPath(path, p);
191    }
192
193protected:
194    SkCanvas* fCanvas;
195
196private:
197    typedef SkCanvas::ClipVisitor INHERITED;
198};
199
200// set up the saveLayer commands so that the active ones
201// return true in their 'active' method
202void SkDebugCanvas::markActiveCommands(int index) {
203    fActiveLayers.rewind();
204
205    for (int i = 0; i < fCommandVector.count(); ++i) {
206        fCommandVector[i]->setActive(false);
207    }
208
209    for (int i = 0; i < index; ++i) {
210        SkDrawCommand::Action result = fCommandVector[i]->action();
211        if (SkDrawCommand::kPushLayer_Action == result) {
212            fActiveLayers.push(fCommandVector[i]);
213        } else if (SkDrawCommand::kPopLayer_Action == result) {
214            fActiveLayers.pop();
215        }
216    }
217
218    for (int i = 0; i < fActiveLayers.count(); ++i) {
219        fActiveLayers[i]->setActive(true);
220    }
221
222}
223
224void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
225    SkASSERT(!fCommandVector.isEmpty());
226    SkASSERT(index < fCommandVector.count());
227
228    int saveCount = canvas->save();
229
230    SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()),
231                                       SkIntToScalar(canvas->getBaseLayerSize().height()));
232
233    bool pathOpsMode = getAllowSimplifyClip();
234    canvas->setAllowSimplifyClip(pathOpsMode);
235    canvas->clear(SK_ColorTRANSPARENT);
236    canvas->resetMatrix();
237    if (!windowRect.isEmpty()) {
238        canvas->clipRect(windowRect, SkRegion::kReplace_Op);
239    }
240    this->applyUserTransform(canvas);
241
242    if (fPaintFilterCanvas) {
243        fPaintFilterCanvas->addCanvas(canvas);
244        canvas = fPaintFilterCanvas.get();
245    }
246
247    if (fMegaVizMode) {
248        this->markActiveCommands(index);
249    }
250
251    for (int i = 0; i <= index; i++) {
252        if (i == index && fFilter) {
253            canvas->clear(0xAAFFFFFF);
254        }
255
256        if (fCommandVector[i]->isVisible()) {
257            if (fMegaVizMode && fCommandVector[i]->active()) {
258                // "active" commands execute their visualization behaviors:
259                //     All active saveLayers get replaced with saves so all draws go to the
260                //     visible canvas.
261                //     All active culls draw their cull box
262                fCommandVector[i]->vizExecute(canvas);
263            } else {
264                fCommandVector[i]->setUserMatrix(fUserMatrix);
265                fCommandVector[i]->execute(canvas);
266            }
267        }
268    }
269
270    if (fMegaVizMode) {
271        canvas->save();
272        // nuke the CTM
273        canvas->resetMatrix();
274        // turn off clipping
275        if (!windowRect.isEmpty()) {
276            SkRect r = windowRect;
277            r.outset(SK_Scalar1, SK_Scalar1);
278            canvas->clipRect(r, SkRegion::kReplace_Op);
279        }
280        // visualize existing clips
281        SkDebugClipVisitor visitor(canvas);
282
283        canvas->replayClips(&visitor);
284
285        canvas->restore();
286    }
287    if (pathOpsMode) {
288        this->resetClipStackData();
289        const SkClipStack* clipStack = canvas->getClipStack();
290        SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
291        const SkClipStack::Element* element;
292        SkPath devPath;
293        while ((element = iter.next())) {
294            SkClipStack::Element::Type type = element->getType();
295            SkPath operand;
296            if (type != SkClipStack::Element::kEmpty_Type) {
297               element->asPath(&operand);
298            }
299            SkRegion::Op elementOp = element->getOp();
300            this->addClipStackData(devPath, operand, elementOp);
301            if (elementOp == SkRegion::kReplace_Op) {
302                devPath = operand;
303            } else {
304                Op(devPath, operand, (SkPathOp) elementOp, &devPath);
305            }
306        }
307        this->lastClipStackData(devPath);
308    }
309    fMatrix = canvas->getTotalMatrix();
310    if (!canvas->getClipDeviceBounds(&fClip)) {
311        fClip.setEmpty();
312    }
313
314    canvas->restoreToCount(saveCount);
315
316    if (fPaintFilterCanvas) {
317        fPaintFilterCanvas->removeAll();
318    }
319}
320
321void SkDebugCanvas::deleteDrawCommandAt(int index) {
322    SkASSERT(index < fCommandVector.count());
323    delete fCommandVector[index];
324    fCommandVector.remove(index);
325}
326
327SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
328    SkASSERT(index < fCommandVector.count());
329    return fCommandVector[index];
330}
331
332void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
333    SkASSERT(index < fCommandVector.count());
334    delete fCommandVector[index];
335    fCommandVector[index] = command;
336}
337
338const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
339    SkASSERT(index < fCommandVector.count());
340    return fCommandVector[index]->Info();
341}
342
343bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
344    SkASSERT(index < fCommandVector.count());
345    return fCommandVector[index]->isVisible();
346}
347
348const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
349    return fCommandVector;
350}
351
352SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
353    return fCommandVector;
354}
355
356void SkDebugCanvas::updatePaintFilterCanvas() {
357    if (!fOverdrawViz && !fOverrideFilterQuality) {
358        fPaintFilterCanvas.reset(NULL);
359        return;
360    }
361
362    const SkImageInfo info = this->imageInfo();
363    fPaintFilterCanvas.reset(SkNEW_ARGS(DebugPaintFilterCanvas, (info.width(),
364                                                                 info.height(),
365                                                                 fOverdrawViz,
366                                                                 fOverrideFilterQuality,
367                                                                 fFilterQuality)));
368}
369
370void SkDebugCanvas::setOverdrawViz(bool overdrawViz) {
371    if (fOverdrawViz == overdrawViz) {
372        return;
373    }
374
375    fOverdrawViz = overdrawViz;
376    this->updatePaintFilterCanvas();
377}
378
379void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
380    if (fOverrideFilterQuality == overrideTexFiltering && fFilterQuality == quality) {
381        return;
382    }
383
384    fOverrideFilterQuality = overrideTexFiltering;
385    fFilterQuality = quality;
386    this->updatePaintFilterCanvas();
387}
388
389void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
390    this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
391}
392
393void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
394    this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
395}
396
397void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
398    this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
399}
400
401void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
402    this->addDrawCommand(new SkClipRegionCommand(region, op));
403}
404
405void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
406    this->addDrawCommand(new SkConcatCommand(matrix));
407    this->INHERITED::didConcat(matrix);
408}
409
410void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
411                                 SkScalar top, const SkPaint* paint) {
412    this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
413}
414
415void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
416                                     const SkPaint* paint, DrawBitmapRectFlags flags) {
417    this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
418}
419
420void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
421                                     const SkRect& dst, const SkPaint* paint) {
422    this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
423}
424
425void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
426                                const SkPaint* paint) {
427    SkDebugf("SkDebugCanvas::onDrawImage unimplemented\n");
428}
429
430void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
431                                    const SkPaint* paint) {
432    SkDebugf("SkDebugCanvas::onDrawImageRect unimplemented\n");
433}
434
435void SkDebugCanvas::beginCommentGroup(const char* description) {
436    this->addDrawCommand(new SkBeginCommentGroupCommand(description));
437}
438
439void SkDebugCanvas::addComment(const char* kywd, const char* value) {
440    this->addDrawCommand(new SkCommentCommand(kywd, value));
441}
442
443void SkDebugCanvas::endCommentGroup() {
444    this->addDrawCommand(new SkEndCommentGroupCommand());
445}
446
447void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
448    this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
449}
450
451void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
452    this->addDrawCommand(new SkDrawPaintCommand(paint));
453}
454
455void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
456    this->addDrawCommand(new SkDrawPathCommand(path, paint));
457}
458
459void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
460                                  const SkMatrix* matrix,
461                                  const SkPaint* paint) {
462    this->addDrawCommand(new SkBeginDrawPictureCommand(picture, matrix, paint));
463    this->INHERITED::onDrawPicture(picture, matrix, paint);
464    this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
465}
466
467void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count,
468                                 const SkPoint pts[], const SkPaint& paint) {
469    this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
470}
471
472void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
473                                  const SkPaint& paint) {
474    this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint));
475}
476
477void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
478                                   SkScalar constY, const SkPaint& paint) {
479    this->addDrawCommand(
480        new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint));
481}
482
483void SkDebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
484    // NOTE(chudy): Messing up when renamed to DrawRect... Why?
485    addDrawCommand(new SkDrawRectCommand(rect, paint));
486}
487
488void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
489    this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
490}
491
492void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
493                                 const SkPaint& paint) {
494    this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
495}
496
497void SkDebugCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
498    this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint));
499}
500
501void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
502                               const SkPaint& paint) {
503    this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint));
504}
505
506void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
507                                     const SkMatrix* matrix, const SkPaint& paint) {
508    this->addDrawCommand(
509        new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint));
510}
511
512void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
513                                   const SkPaint& paint) {
514    this->addDrawCommand(new SkDrawTextBlobCommand(blob, x, y, paint));
515}
516
517void SkDebugCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
518                                const SkPoint texCoords[4], SkXfermode* xmode,
519                                const SkPaint& paint) {
520    this->addDrawCommand(new SkDrawPatchCommand(cubics, colors, texCoords, xmode, paint));
521}
522
523void SkDebugCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
524                                   const SkPoint texs[], const SkColor colors[],
525                                   SkXfermode*, const uint16_t indices[], int indexCount,
526                                   const SkPaint& paint) {
527    this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices,
528                         texs, colors, NULL, indices, indexCount, paint));
529}
530
531void SkDebugCanvas::willRestore() {
532    this->addDrawCommand(new SkRestoreCommand());
533    this->INHERITED::willRestore();
534}
535
536void SkDebugCanvas::willSave() {
537    this->addDrawCommand(new SkSaveCommand());
538    this->INHERITED::willSave();
539}
540
541SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint,
542                                                         SaveFlags flags) {
543    this->addDrawCommand(new SkSaveLayerCommand(bounds, paint, flags));
544    this->INHERITED::willSaveLayer(bounds, paint, flags);
545    // No need for a full layer.
546    return kNoLayer_SaveLayerStrategy;
547}
548
549void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
550    this->addDrawCommand(new SkSetMatrixCommand(matrix));
551    this->INHERITED::didSetMatrix(matrix);
552}
553
554void SkDebugCanvas::toggleCommand(int index, bool toggle) {
555    SkASSERT(index < fCommandVector.count());
556    fCommandVector[index]->setVisible(toggle);
557}
558
559static const char* gFillTypeStrs[] = {
560    "kWinding_FillType",
561    "kEvenOdd_FillType",
562    "kInverseWinding_FillType",
563    "kInverseEvenOdd_FillType"
564};
565
566static const char* gOpStrs[] = {
567    "kDifference_PathOp",
568    "kIntersect_PathOp",
569    "kUnion_PathOp",
570    "kXor_PathOp",
571    "kReverseDifference_PathOp",
572};
573
574static const char kHTML4SpaceIndent[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
575
576void SkDebugCanvas::outputScalar(SkScalar num) {
577    if (num == (int) num) {
578        fClipStackData.appendf("%d", (int) num);
579    } else {
580        SkString str;
581        str.printf("%1.9g", num);
582        int width = (int) str.size();
583        const char* cStr = str.c_str();
584        while (cStr[width - 1] == '0') {
585            --width;
586        }
587        str.resize(width);
588        fClipStackData.appendf("%sf", str.c_str());
589    }
590}
591
592void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) {
593    for (int index = 0; index < count; ++index) {
594        this->outputScalar(pts[index].fX);
595        fClipStackData.appendf(", ");
596        this->outputScalar(pts[index].fY);
597        if (index + 1 < count) {
598            fClipStackData.appendf(", ");
599        }
600    }
601}
602
603void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
604    this->outputPointsCommon(pts, count);
605    fClipStackData.appendf(");<br>");
606}
607
608void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) {
609    this->outputPointsCommon(pts, 2);
610    fClipStackData.appendf(", ");
611    this->outputScalar(weight);
612    fClipStackData.appendf(");<br>");
613}
614
615void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) {
616    SkPath::RawIter iter(path);
617    SkPath::FillType fillType = path.getFillType();
618    fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName);
619    fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName,
620            gFillTypeStrs[fillType]);
621    iter.setPath(path);
622    uint8_t verb;
623    SkPoint pts[4];
624    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
625        switch (verb) {
626            case SkPath::kMove_Verb:
627                fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
628                this->outputPoints(&pts[0], 1);
629                continue;
630            case SkPath::kLine_Verb:
631                fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
632                this->outputPoints(&pts[1], 1);
633                break;
634            case SkPath::kQuad_Verb:
635                fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
636                this->outputPoints(&pts[1], 2);
637                break;
638            case SkPath::kConic_Verb:
639                fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
640                this->outputConicPoints(&pts[1], iter.conicWeight());
641                break;
642            case SkPath::kCubic_Verb:
643                fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
644                this->outputPoints(&pts[1], 3);
645                break;
646            case SkPath::kClose_Verb:
647                fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
648                break;
649            default:
650                SkDEBUGFAIL("bad verb");
651                return;
652        }
653    }
654}
655
656void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand,
657                                     SkRegion::Op elementOp) {
658    if (elementOp == SkRegion::kReplace_Op) {
659        if (!lastClipStackData(devPath)) {
660            fSaveDevPath = operand;
661        }
662        fCalledAddStackData = false;
663    } else {
664        fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter,"
665            " const char* filename) {<br>");
666        addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path");
667        addPathData(operand, "pathB");
668        fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>",
669            kHTML4SpaceIndent, gOpStrs[elementOp]);
670        fClipStackData.appendf("}<br>");
671        fCalledAddStackData = true;
672    }
673}
674
675bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
676    if (fCalledAddStackData) {
677        fClipStackData.appendf("<br>");
678        addPathData(devPath, "pathOut");
679        return true;
680    }
681    return false;
682}
683