779d6950d7d1fd9bba56e95c572f620948636648
[platform/upstream/libSkiaSharp.git] / src / utils / debugger / SkDebugCanvas.cpp
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 "SkDrawFilter.h"
14 #include "SkDevice.h"
15 #include "SkXfermode.h"
16
17 SkDebugCanvas::SkDebugCanvas(int width, int height)
18         : INHERITED(width, height)
19         , fPicture(NULL)
20         , fFilter(false)
21         , fMegaVizMode(false)
22         , fOverdrawViz(false)
23         , fOverdrawFilter(NULL)
24         , fOverrideTexFiltering(false)
25         , fTexOverrideFilter(NULL) {
26     fUserMatrix.reset();
27
28     // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
29     // operations. This can lead to problems in the debugger which expects all
30     // the operations in the captured skp to appear in the debug canvas. To
31     // circumvent this we create a wide open clip here (an empty clip rect
32     // is not sufficient).
33     // Internally, the SkRect passed to clipRect is converted to an SkIRect and
34     // rounded out. The following code creates a nearly maximal rect that will
35     // not get collapsed by the coming conversions (Due to precision loss the
36     // inset has to be surprisingly large).
37     SkIRect largeIRect = SkIRect::MakeLargest();
38     largeIRect.inset(1024, 1024);
39     SkRect large = SkRect::Make(largeIRect);
40 #ifdef SK_DEBUG
41     SkASSERT(!large.roundOut().isEmpty());
42 #endif
43     // call the base class' version to avoid adding a draw command
44     this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
45 }
46
47 SkDebugCanvas::~SkDebugCanvas() {
48     fCommandVector.deleteAll();
49     SkSafeUnref(fOverdrawFilter);
50     SkSafeUnref(fTexOverrideFilter);
51 }
52
53 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
54     command->setOffset(this->getOpID());
55     fCommandVector.push(command);
56 }
57
58 void SkDebugCanvas::draw(SkCanvas* canvas) {
59     if (!fCommandVector.isEmpty()) {
60         this->drawTo(canvas, fCommandVector.count() - 1);
61     }
62 }
63
64 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
65     canvas->concat(fUserMatrix);
66 }
67
68 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
69     SkBitmap bitmap;
70     bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
71
72     SkCanvas canvas(bitmap);
73     canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
74     this->applyUserTransform(&canvas);
75
76     int layer = 0;
77     SkColor prev = bitmap.getColor(0,0);
78     for (int i = 0; i < index; i++) {
79         if (fCommandVector[i]->isVisible()) {
80             fCommandVector[i]->setUserMatrix(fUserMatrix);
81             fCommandVector[i]->execute(&canvas);
82         }
83         if (prev != bitmap.getColor(0,0)) {
84             layer = i;
85         }
86         prev = bitmap.getColor(0,0);
87     }
88     return layer;
89 }
90
91 class OverdrawXfermode : public SkXfermode {
92 public:
93     SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE {
94         // This table encodes the color progression of the overdraw visualization
95         static const SkPMColor gTable[] = {
96             SkPackARGB32(0x00, 0x00, 0x00, 0x00),
97             SkPackARGB32(0xFF, 128, 158, 255),
98             SkPackARGB32(0xFF, 170, 185, 212),
99             SkPackARGB32(0xFF, 213, 195, 170),
100             SkPackARGB32(0xFF, 255, 192, 127),
101             SkPackARGB32(0xFF, 255, 185, 85),
102             SkPackARGB32(0xFF, 255, 165, 42),
103             SkPackARGB32(0xFF, 255, 135, 0),
104             SkPackARGB32(0xFF, 255,  95, 0),
105             SkPackARGB32(0xFF, 255,  50, 0),
106             SkPackARGB32(0xFF, 255,  0, 0)
107         };
108  
109
110         int idx;
111         if (SkColorGetR(dst) < 64) { // 0
112             idx = 0;
113         } else if (SkColorGetG(dst) < 25) { // 10
114             idx = 9;  // cap at 9 for upcoming increment
115         } else if ((SkColorGetB(dst)+21)/42 > 0) { // 1-6
116             idx = 7 - (SkColorGetB(dst)+21)/42;
117         } else { // 7-9
118             idx = 10 - (SkColorGetG(dst)+22)/45;
119         }
120         ++idx;
121         SkASSERT(idx < (int)SK_ARRAY_COUNT(gTable));
122
123         return gTable[idx];
124     }
125
126     Factory getFactory() const SK_OVERRIDE { return NULL; }
127 #ifndef SK_IGNORE_TO_STRING
128     virtual void toString(SkString* str) const SK_OVERRIDE { str->set("OverdrawXfermode"); }
129 #endif
130 };
131
132 class SkOverdrawFilter : public SkDrawFilter {
133 public:
134     SkOverdrawFilter() {
135         fXferMode = SkNEW(OverdrawXfermode);
136     }
137
138     virtual ~SkOverdrawFilter() {
139         delete fXferMode;
140     }
141
142     bool filter(SkPaint* p, Type) SK_OVERRIDE {
143         p->setXfermode(fXferMode);
144         p->setAntiAlias(false);
145         return true;
146     }
147
148 protected:
149     SkXfermode* fXferMode;
150
151 private:
152     typedef SkDrawFilter INHERITED;
153 };
154
155 // SkTexOverrideFilter modifies every paint to use the specified
156 // texture filtering mode
157 class SkTexOverrideFilter : public SkDrawFilter {
158 public:
159     SkTexOverrideFilter() : fFilterQuality(kNone_SkFilterQuality) {
160     }
161
162     void setFilterQuality(SkFilterQuality filterQuality) {
163         fFilterQuality = filterQuality;
164     }
165
166     bool filter(SkPaint* p, Type) SK_OVERRIDE {
167         p->setFilterQuality(fFilterQuality);
168         return true;
169     }
170
171 protected:
172     SkFilterQuality fFilterQuality;
173
174 private:
175     typedef SkDrawFilter INHERITED;
176 };
177
178 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
179 public:
180     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
181
182     void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE {
183         SkPaint p;
184         p.setColor(SK_ColorRED);
185         p.setStyle(SkPaint::kStroke_Style);
186         p.setAntiAlias(doAA);
187         fCanvas->drawRect(r, p);
188     }
189     void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE {
190         SkPaint p;
191         p.setColor(SK_ColorGREEN);
192         p.setStyle(SkPaint::kStroke_Style);
193         p.setAntiAlias(doAA);
194         fCanvas->drawRRect(rr, p);
195     }
196     void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE {
197         SkPaint p;
198         p.setColor(SK_ColorBLUE);
199         p.setStyle(SkPaint::kStroke_Style);
200         p.setAntiAlias(doAA);
201         fCanvas->drawPath(path, p);
202     }
203
204 protected:
205     SkCanvas* fCanvas;
206
207 private:
208     typedef SkCanvas::ClipVisitor INHERITED;
209 };
210
211 // set up the saveLayer commands so that the active ones
212 // return true in their 'active' method
213 void SkDebugCanvas::markActiveCommands(int index) {
214     fActiveLayers.rewind();
215
216     for (int i = 0; i < fCommandVector.count(); ++i) {
217         fCommandVector[i]->setActive(false);
218     }
219
220     for (int i = 0; i < index; ++i) {
221         SkDrawCommand::Action result = fCommandVector[i]->action();
222         if (SkDrawCommand::kPushLayer_Action == result) {
223             fActiveLayers.push(fCommandVector[i]);
224         } else if (SkDrawCommand::kPopLayer_Action == result) {
225             fActiveLayers.pop();
226         }
227     }
228
229     for (int i = 0; i < fActiveLayers.count(); ++i) {
230         fActiveLayers[i]->setActive(true);
231     }
232
233 }
234
235 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
236     SkASSERT(!fCommandVector.isEmpty());
237     SkASSERT(index < fCommandVector.count());
238
239     int saveCount = canvas->save();
240
241     SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()),
242                                        SkIntToScalar(canvas->getBaseLayerSize().height()));
243
244     bool pathOpsMode = getAllowSimplifyClip();
245     canvas->setAllowSimplifyClip(pathOpsMode);
246     canvas->clear(SK_ColorTRANSPARENT);
247     canvas->resetMatrix();
248     if (!windowRect.isEmpty()) {
249         canvas->clipRect(windowRect, SkRegion::kReplace_Op);
250     }
251     this->applyUserTransform(canvas);
252
253     // The setting of the draw filter has to go here (rather than in
254     // SkRasterWidget) due to the canvas restores this class performs.
255     // Since the draw filter is stored in the layer stack if we
256     // call setDrawFilter on anything but the root layer odd things happen.
257     if (fOverdrawViz) {
258         if (NULL == fOverdrawFilter) {
259             fOverdrawFilter = new SkOverdrawFilter;
260         }
261
262         if (fOverdrawFilter != canvas->getDrawFilter()) {
263             canvas->setDrawFilter(fOverdrawFilter);
264         }
265     } else if (fOverrideTexFiltering) {
266         if (NULL == fTexOverrideFilter) {
267             fTexOverrideFilter = new SkTexOverrideFilter;
268         }
269
270         if (fTexOverrideFilter != canvas->getDrawFilter()) {
271             canvas->setDrawFilter(fTexOverrideFilter);
272         }
273     } else {
274         canvas->setDrawFilter(NULL);
275     }
276
277     if (fMegaVizMode) {
278         this->markActiveCommands(index);
279     }
280
281     for (int i = 0; i <= index; i++) {
282         if (i == index && fFilter) {
283             canvas->clear(0xAAFFFFFF);
284         }
285
286         if (fCommandVector[i]->isVisible()) {
287             if (fMegaVizMode && fCommandVector[i]->active()) {
288                 // "active" commands execute their visualization behaviors:
289                 //     All active saveLayers get replaced with saves so all draws go to the
290                 //     visible canvas.
291                 //     All active culls draw their cull box
292                 fCommandVector[i]->vizExecute(canvas);
293             } else {
294                 fCommandVector[i]->setUserMatrix(fUserMatrix);
295                 fCommandVector[i]->execute(canvas);
296             }
297         }
298     }
299
300     if (fMegaVizMode) {
301         canvas->save();
302         // nuke the CTM
303         canvas->resetMatrix();
304         // turn off clipping
305         if (!windowRect.isEmpty()) {
306             SkRect r = windowRect;
307             r.outset(SK_Scalar1, SK_Scalar1);
308             canvas->clipRect(r, SkRegion::kReplace_Op);
309         }
310         // visualize existing clips
311         SkDebugClipVisitor visitor(canvas);
312
313         canvas->replayClips(&visitor);
314
315         canvas->restore();
316     }
317     if (pathOpsMode) {
318         this->resetClipStackData();
319         const SkClipStack* clipStack = canvas->getClipStack();
320         SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
321         const SkClipStack::Element* element;
322         SkPath devPath;
323         while ((element = iter.next())) {
324             SkClipStack::Element::Type type = element->getType();
325             SkPath operand;
326             if (type != SkClipStack::Element::kEmpty_Type) {
327                element->asPath(&operand);
328             }
329             SkRegion::Op elementOp = element->getOp();
330             this->addClipStackData(devPath, operand, elementOp);
331             if (elementOp == SkRegion::kReplace_Op) {
332                 devPath = operand;
333             } else {
334                 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
335             }
336         }
337         this->lastClipStackData(devPath);
338     }
339     fMatrix = canvas->getTotalMatrix();
340     if (!canvas->getClipDeviceBounds(&fClip)) {
341         fClip.setEmpty();
342     }
343
344     canvas->restoreToCount(saveCount);
345 }
346
347 void SkDebugCanvas::deleteDrawCommandAt(int index) {
348     SkASSERT(index < fCommandVector.count());
349     delete fCommandVector[index];
350     fCommandVector.remove(index);
351 }
352
353 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
354     SkASSERT(index < fCommandVector.count());
355     return fCommandVector[index];
356 }
357
358 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
359     SkASSERT(index < fCommandVector.count());
360     delete fCommandVector[index];
361     fCommandVector[index] = command;
362 }
363
364 const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
365     SkASSERT(index < fCommandVector.count());
366     return fCommandVector[index]->Info();
367 }
368
369 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
370     SkASSERT(index < fCommandVector.count());
371     return fCommandVector[index]->isVisible();
372 }
373
374 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
375     return fCommandVector;
376 }
377
378 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
379     return fCommandVector;
380 }
381
382 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
383     if (NULL == fTexOverrideFilter) {
384         fTexOverrideFilter = new SkTexOverrideFilter;
385     }
386
387     fOverrideTexFiltering = overrideTexFiltering;
388     fTexOverrideFilter->setFilterQuality(quality);
389 }
390
391 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
392     this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
393 }
394
395 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
396     this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
397 }
398
399 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
400     this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
401 }
402
403 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
404     this->addDrawCommand(new SkClipRegionCommand(region, op));
405 }
406
407 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
408     this->addDrawCommand(new SkConcatCommand(matrix));
409     this->INHERITED::didConcat(matrix);
410 }
411
412 void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
413                                  SkScalar top, const SkPaint* paint) {
414     this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
415 }
416
417 void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
418                                      const SkPaint* paint, DrawBitmapRectFlags flags) {
419     this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
420 }
421
422 void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
423                                      const SkRect& dst, const SkPaint* paint) {
424     this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
425 }
426
427 void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
428                                 const SkPaint* paint) {
429     SkDebugf("SkDebugCanvas::onDrawImage unimplemented\n");
430 }
431
432 void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
433                                     const SkPaint* paint) {
434     SkDebugf("SkDebugCanvas::onDrawImageRect unimplemented\n");
435 }
436
437 void SkDebugCanvas::beginCommentGroup(const char* description) {
438     this->addDrawCommand(new SkBeginCommentGroupCommand(description));
439 }
440
441 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
442     this->addDrawCommand(new SkCommentCommand(kywd, value));
443 }
444
445 void SkDebugCanvas::endCommentGroup() {
446     this->addDrawCommand(new SkEndCommentGroupCommand());
447 }
448
449 void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
450     this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
451 }
452
453 void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
454     this->addDrawCommand(new SkDrawPaintCommand(paint));
455 }
456
457 void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
458     this->addDrawCommand(new SkDrawPathCommand(path, paint));
459 }
460
461 void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
462                                   const SkMatrix* matrix,
463                                   const SkPaint* paint) {
464     this->addDrawCommand(new SkDrawPictureCommand(picture, matrix, paint));
465 }
466
467 void 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
472 void 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
477 void 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
483 void 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
488 void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
489     this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
490 }
491
492 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
493                                  const SkPaint& paint) {
494     this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
495 }
496
497 void SkDebugCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
498     this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint));
499 }
500
501 void 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
506 void 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
512 void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
513                                    const SkPaint& paint) {
514     this->addDrawCommand(new SkDrawTextBlobCommand(blob, x, y, paint));
515 }
516
517 void 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
523 void 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
531 void SkDebugCanvas::willRestore() {
532     this->addDrawCommand(new SkRestoreCommand());
533     this->INHERITED::willRestore();
534 }
535
536 void SkDebugCanvas::willSave() {
537     this->addDrawCommand(new SkSaveCommand());
538     this->INHERITED::willSave();
539 }
540
541 SkCanvas::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
549 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
550     this->addDrawCommand(new SkSetMatrixCommand(matrix));
551     this->INHERITED::didSetMatrix(matrix);
552 }
553
554 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
555     SkASSERT(index < fCommandVector.count());
556     fCommandVector[index]->setVisible(toggle);
557 }
558
559 static const char* gFillTypeStrs[] = {
560     "kWinding_FillType",
561     "kEvenOdd_FillType",
562     "kInverseWinding_FillType",
563     "kInverseEvenOdd_FillType"
564 };
565
566 static const char* gOpStrs[] = {
567     "kDifference_PathOp",
568     "kIntersect_PathOp",
569     "kUnion_PathOp",
570     "kXor_PathOp",
571     "kReverseDifference_PathOp",
572 };
573
574 static const char kHTML4SpaceIndent[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
575
576 void 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
592 void 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
603 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
604     this->outputPointsCommon(pts, count);
605     fClipStackData.appendf(");<br>");
606 }
607
608 void 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
615 void 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
656 void 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
675 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
676     if (fCalledAddStackData) {
677         fClipStackData.appendf("<br>");
678         addPathData(devPath, "pathOut");
679         return true;
680     }
681     return false;
682 }