• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SkDebugCanvas(int windowWidth,int windowHeight)17 SkDebugCanvas::SkDebugCanvas(int windowWidth, int windowHeight)
18         : INHERITED(windowWidth, windowHeight)
19         , fPicture(NULL)
20         , fWindowSize(SkISize::Make(windowWidth, windowHeight))
21         , fFilter(false)
22         , fMegaVizMode(false)
23         , fIndex(0)
24         , fOverdrawViz(false)
25         , fOverdrawFilter(NULL)
26         , fOverrideTexFiltering(false)
27         , fTexOverrideFilter(NULL)
28         , fOutstandingSaveCount(0) {
29     fUserMatrix.reset();
30 
31     // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
32     // operations. This can lead to problems in the debugger which expects all
33     // the operations in the captured skp to appear in the debug canvas. To
34     // circumvent this we create a wide open clip here (an empty clip rect
35     // is not sufficient).
36     // Internally, the SkRect passed to clipRect is converted to an SkIRect and
37     // rounded out. The following code creates a nearly maximal rect that will
38     // not get collapsed by the coming conversions (Due to precision loss the
39     // inset has to be surprisingly large).
40     SkIRect largeIRect = SkIRect::MakeLargest();
41     largeIRect.inset(1024, 1024);
42     SkRect large = SkRect::Make(largeIRect);
43 #ifdef SK_DEBUG
44     large.roundOut(&largeIRect);
45     SkASSERT(!largeIRect.isEmpty());
46 #endif
47     // call the base class' version to avoid adding a draw command
48     this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
49 }
50 
~SkDebugCanvas()51 SkDebugCanvas::~SkDebugCanvas() {
52     fCommandVector.deleteAll();
53     SkSafeUnref(fOverdrawFilter);
54     SkSafeUnref(fTexOverrideFilter);
55 }
56 
addDrawCommand(SkDrawCommand * command)57 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
58     command->setOffset(this->getOpID());
59     fCommandVector.push(command);
60 }
61 
draw(SkCanvas * canvas)62 void SkDebugCanvas::draw(SkCanvas* canvas) {
63     if (!fCommandVector.isEmpty()) {
64         this->drawTo(canvas, fCommandVector.count() - 1);
65     }
66 }
67 
applyUserTransform(SkCanvas * canvas)68 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
69     canvas->concat(fUserMatrix);
70 }
71 
getCommandAtPoint(int x,int y,int index)72 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
73     SkBitmap bitmap;
74     bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
75 
76     SkCanvas canvas(bitmap);
77     canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
78     this->applyUserTransform(&canvas);
79 
80     int layer = 0;
81     SkColor prev = bitmap.getColor(0,0);
82     for (int i = 0; i < index; i++) {
83         if (fCommandVector[i]->isVisible()) {
84             fCommandVector[i]->execute(&canvas);
85         }
86         if (prev != bitmap.getColor(0,0)) {
87             layer = i;
88         }
89         prev = bitmap.getColor(0,0);
90     }
91     return layer;
92 }
93 
94 class OverdrawXfermode : public SkXfermode {
95 public:
xferColor(SkPMColor src,SkPMColor dst) const96     virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE {
97         // This table encodes the color progression of the overdraw visualization
98         static const SkPMColor gTable[] = {
99             SkPackARGB32(0x00, 0x00, 0x00, 0x00),
100             SkPackARGB32(0xFF, 128, 158, 255),
101             SkPackARGB32(0xFF, 170, 185, 212),
102             SkPackARGB32(0xFF, 213, 195, 170),
103             SkPackARGB32(0xFF, 255, 192, 127),
104             SkPackARGB32(0xFF, 255, 185, 85),
105             SkPackARGB32(0xFF, 255, 165, 42),
106             SkPackARGB32(0xFF, 255, 135, 0),
107             SkPackARGB32(0xFF, 255,  95, 0),
108             SkPackARGB32(0xFF, 255,  50, 0),
109             SkPackARGB32(0xFF, 255,  0, 0)
110         };
111 
112         for (size_t i = 0; i < SK_ARRAY_COUNT(gTable)-1; ++i) {
113             if (gTable[i] == dst) {
114                 return gTable[i+1];
115             }
116         }
117 
118         return gTable[SK_ARRAY_COUNT(gTable)-1];
119     }
120 
getFactory() const121     virtual Factory getFactory() const SK_OVERRIDE { return NULL; }
122 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const123     virtual void toString(SkString* str) const { str->set("OverdrawXfermode"); }
124 #endif
125 };
126 
127 class SkOverdrawFilter : public SkDrawFilter {
128 public:
SkOverdrawFilter()129     SkOverdrawFilter() {
130         fXferMode = SkNEW(OverdrawXfermode);
131     }
132 
~SkOverdrawFilter()133     virtual ~SkOverdrawFilter() {
134         delete fXferMode;
135     }
136 
filter(SkPaint * p,Type)137     virtual bool filter(SkPaint* p, Type) SK_OVERRIDE {
138         p->setXfermode(fXferMode);
139         return true;
140     }
141 
142 protected:
143     SkXfermode* fXferMode;
144 
145 private:
146     typedef SkDrawFilter INHERITED;
147 };
148 
149 // SkTexOverrideFilter modifies every paint to use the specified
150 // texture filtering mode
151 class SkTexOverrideFilter : public SkDrawFilter {
152 public:
SkTexOverrideFilter()153     SkTexOverrideFilter() : fFilterLevel(SkPaint::kNone_FilterLevel) {
154     }
155 
setFilterLevel(SkPaint::FilterLevel filterLevel)156     void setFilterLevel(SkPaint::FilterLevel filterLevel) {
157         fFilterLevel = filterLevel;
158     }
159 
filter(SkPaint * p,Type)160     virtual bool filter(SkPaint* p, Type) SK_OVERRIDE {
161         p->setFilterLevel(fFilterLevel);
162         return true;
163     }
164 
165 protected:
166     SkPaint::FilterLevel fFilterLevel;
167 
168 private:
169     typedef SkDrawFilter INHERITED;
170 };
171 
172 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
173 public:
SkDebugClipVisitor(SkCanvas * canvas)174     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
175 
clipRect(const SkRect & r,SkRegion::Op,bool doAA)176     virtual void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE {
177         SkPaint p;
178         p.setColor(SK_ColorRED);
179         p.setStyle(SkPaint::kStroke_Style);
180         p.setAntiAlias(doAA);
181         fCanvas->drawRect(r, p);
182     }
clipRRect(const SkRRect & rr,SkRegion::Op,bool doAA)183     virtual void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE {
184         SkPaint p;
185         p.setColor(SK_ColorGREEN);
186         p.setStyle(SkPaint::kStroke_Style);
187         p.setAntiAlias(doAA);
188         fCanvas->drawRRect(rr, p);
189     }
clipPath(const SkPath & path,SkRegion::Op,bool doAA)190     virtual void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE {
191         SkPaint p;
192         p.setColor(SK_ColorBLUE);
193         p.setStyle(SkPaint::kStroke_Style);
194         p.setAntiAlias(doAA);
195         fCanvas->drawPath(path, p);
196     }
197 
198 protected:
199     SkCanvas* fCanvas;
200 
201 private:
202     typedef SkCanvas::ClipVisitor INHERITED;
203 };
204 
205 // set up the saveLayer commands so that the active ones
206 // return true in their 'active' method
markActiveCommands(int index)207 void SkDebugCanvas::markActiveCommands(int index) {
208     fActiveLayers.rewind();
209     fActiveCulls.rewind();
210 
211     for (int i = 0; i < fCommandVector.count(); ++i) {
212         fCommandVector[i]->setActive(false);
213     }
214 
215     for (int i = 0; i < index; ++i) {
216         SkDrawCommand::Action result = fCommandVector[i]->action();
217         if (SkDrawCommand::kPushLayer_Action == result) {
218             fActiveLayers.push(fCommandVector[i]);
219         } else if (SkDrawCommand::kPopLayer_Action == result) {
220             fActiveLayers.pop();
221         } else if (SkDrawCommand::kPushCull_Action == result) {
222             fActiveCulls.push(fCommandVector[i]);
223         } else if (SkDrawCommand::kPopCull_Action == result) {
224             fActiveCulls.pop();
225         }
226     }
227 
228     for (int i = 0; i < fActiveLayers.count(); ++i) {
229         fActiveLayers[i]->setActive(true);
230     }
231 
232     for (int i = 0; i < fActiveCulls.count(); ++i) {
233         fActiveCulls[i]->setActive(true);
234     }
235 }
236 
drawTo(SkCanvas * canvas,int index)237 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
238     SkASSERT(!fCommandVector.isEmpty());
239     SkASSERT(index < fCommandVector.count());
240     int i = 0;
241 
242     bool pathOpsMode = getAllowSimplifyClip();
243     canvas->setAllowSimplifyClip(pathOpsMode);
244     // This only works assuming the canvas and device are the same ones that
245     // were previously drawn into because they need to preserve all saves
246     // and restores.
247     // The visibility filter also requires a full re-draw - otherwise we can
248     // end up drawing the filter repeatedly.
249     if (fIndex < index && !fFilter && !fMegaVizMode && !pathOpsMode) {
250         i = fIndex + 1;
251     } else {
252         for (int j = 0; j < fOutstandingSaveCount; j++) {
253             canvas->restore();
254         }
255         canvas->clear(SK_ColorTRANSPARENT);
256         canvas->resetMatrix();
257         SkRect rect = SkRect::MakeWH(SkIntToScalar(fWindowSize.fWidth),
258                                      SkIntToScalar(fWindowSize.fHeight));
259         canvas->clipRect(rect, SkRegion::kReplace_Op);
260         this->applyUserTransform(canvas);
261         fOutstandingSaveCount = 0;
262     }
263 
264     // The setting of the draw filter has to go here (rather than in
265     // SkRasterWidget) due to the canvas restores this class performs.
266     // Since the draw filter is stored in the layer stack if we
267     // call setDrawFilter on anything but the root layer odd things happen.
268     if (fOverdrawViz) {
269         if (NULL == fOverdrawFilter) {
270             fOverdrawFilter = new SkOverdrawFilter;
271         }
272 
273         if (fOverdrawFilter != canvas->getDrawFilter()) {
274             canvas->setDrawFilter(fOverdrawFilter);
275         }
276     } else if (fOverrideTexFiltering) {
277         if (NULL == fTexOverrideFilter) {
278             fTexOverrideFilter = new SkTexOverrideFilter;
279         }
280 
281         if (fTexOverrideFilter != canvas->getDrawFilter()) {
282             canvas->setDrawFilter(fTexOverrideFilter);
283         }
284     } else {
285         canvas->setDrawFilter(NULL);
286     }
287 
288     if (fMegaVizMode) {
289         this->markActiveCommands(index);
290     }
291 
292     for (; i <= index; i++) {
293         if (i == index && fFilter) {
294             canvas->clear(0xAAFFFFFF);
295         }
296 
297         if (fCommandVector[i]->isVisible()) {
298             if (fMegaVizMode && fCommandVector[i]->active()) {
299                 // "active" commands execute their visualization behaviors:
300                 //     All active saveLayers get replaced with saves so all draws go to the
301                 //     visible canvas.
302                 //     All active culls draw their cull box
303                 fCommandVector[i]->vizExecute(canvas);
304             } else {
305                 fCommandVector[i]->execute(canvas);
306             }
307 
308             fCommandVector[i]->trackSaveState(&fOutstandingSaveCount);
309         }
310     }
311 
312     if (fMegaVizMode) {
313         SkRect r = SkRect::MakeWH(SkIntToScalar(fWindowSize.fWidth),
314                                   SkIntToScalar(fWindowSize.fHeight));
315         r.outset(SK_Scalar1, SK_Scalar1);
316 
317         canvas->save();
318         // nuke the CTM
319         canvas->resetMatrix();
320         // turn off clipping
321         canvas->clipRect(r, SkRegion::kReplace_Op);
322 
323         // visualize existing clips
324         SkDebugClipVisitor visitor(canvas);
325 
326         canvas->replayClips(&visitor);
327 
328         canvas->restore();
329     }
330     if (pathOpsMode) {
331         this->resetClipStackData();
332         const SkClipStack* clipStack = canvas->getClipStack();
333         SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
334         const SkClipStack::Element* element;
335         SkPath devPath;
336         while ((element = iter.next())) {
337             SkClipStack::Element::Type type = element->getType();
338             SkPath operand;
339             if (type != SkClipStack::Element::kEmpty_Type) {
340                element->asPath(&operand);
341             }
342             SkRegion::Op elementOp = element->getOp();
343             this->addClipStackData(devPath, operand, elementOp);
344             if (elementOp == SkRegion::kReplace_Op) {
345                 devPath = operand;
346             } else {
347                 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
348             }
349         }
350         this->lastClipStackData(devPath);
351     }
352     fMatrix = canvas->getTotalMatrix();
353     if (!canvas->getClipDeviceBounds(&fClip)) {
354         fClip.setEmpty();
355     }
356     fIndex = index;
357 }
358 
deleteDrawCommandAt(int index)359 void SkDebugCanvas::deleteDrawCommandAt(int index) {
360     SkASSERT(index < fCommandVector.count());
361     delete fCommandVector[index];
362     fCommandVector.remove(index);
363 }
364 
getDrawCommandAt(int index)365 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
366     SkASSERT(index < fCommandVector.count());
367     return fCommandVector[index];
368 }
369 
setDrawCommandAt(int index,SkDrawCommand * command)370 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
371     SkASSERT(index < fCommandVector.count());
372     delete fCommandVector[index];
373     fCommandVector[index] = command;
374 }
375 
getCommandInfo(int index)376 SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) {
377     SkASSERT(index < fCommandVector.count());
378     return fCommandVector[index]->Info();
379 }
380 
getDrawCommandVisibilityAt(int index)381 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
382     SkASSERT(index < fCommandVector.count());
383     return fCommandVector[index]->isVisible();
384 }
385 
getDrawCommands() const386 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
387     return fCommandVector;
388 }
389 
getDrawCommands()390 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
391     return fCommandVector;
392 }
393 
394 // TODO(chudy): Free command string memory.
getDrawCommandsAsStrings() const395 SkTArray<SkString>* SkDebugCanvas::getDrawCommandsAsStrings() const {
396     SkTArray<SkString>* commandString = new SkTArray<SkString>(fCommandVector.count());
397     if (!fCommandVector.isEmpty()) {
398         for (int i = 0; i < fCommandVector.count(); i ++) {
399             commandString->push_back() = fCommandVector[i]->toString();
400         }
401     }
402     return commandString;
403 }
404 
getDrawCommandOffsets() const405 SkTDArray<size_t>* SkDebugCanvas::getDrawCommandOffsets() const {
406     SkTDArray<size_t>* commandOffsets = new SkTDArray<size_t>;
407     if (!fCommandVector.isEmpty()) {
408         for (int i = 0; i < fCommandVector.count(); i ++) {
409             *commandOffsets->push() = fCommandVector[i]->offset();
410         }
411     }
412     return commandOffsets;
413 }
414 
overrideTexFiltering(bool overrideTexFiltering,SkPaint::FilterLevel level)415 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkPaint::FilterLevel level) {
416     if (NULL == fTexOverrideFilter) {
417         fTexOverrideFilter = new SkTexOverrideFilter;
418     }
419 
420     fOverrideTexFiltering = overrideTexFiltering;
421     fTexOverrideFilter->setFilterLevel(level);
422 }
423 
clear(SkColor color)424 void SkDebugCanvas::clear(SkColor color) {
425     this->addDrawCommand(new SkClearCommand(color));
426 }
427 
onClipPath(const SkPath & path,SkRegion::Op op,ClipEdgeStyle edgeStyle)428 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
429     this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
430 }
431 
onClipRect(const SkRect & rect,SkRegion::Op op,ClipEdgeStyle edgeStyle)432 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
433     this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
434 }
435 
onClipRRect(const SkRRect & rrect,SkRegion::Op op,ClipEdgeStyle edgeStyle)436 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
437     this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
438 }
439 
onClipRegion(const SkRegion & region,SkRegion::Op op)440 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
441     this->addDrawCommand(new SkClipRegionCommand(region, op));
442 }
443 
didConcat(const SkMatrix & matrix)444 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
445     switch (matrix.getType()) {
446         case SkMatrix::kTranslate_Mask:
447             this->addDrawCommand(new SkTranslateCommand(matrix.getTranslateX(),
448                                                         matrix.getTranslateY()));
449             break;
450         case SkMatrix::kScale_Mask:
451             this->addDrawCommand(new SkScaleCommand(matrix.getScaleX(),
452                                                     matrix.getScaleY()));
453             break;
454         default:
455             this->addDrawCommand(new SkConcatCommand(matrix));
456             break;
457     }
458 
459     this->INHERITED::didConcat(matrix);
460 }
461 
drawBitmap(const SkBitmap & bitmap,SkScalar left,SkScalar top,const SkPaint * paint=NULL)462 void SkDebugCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
463                                SkScalar top, const SkPaint* paint = NULL) {
464     this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
465 }
466 
drawBitmapRectToRect(const SkBitmap & bitmap,const SkRect * src,const SkRect & dst,const SkPaint * paint,SkCanvas::DrawBitmapRectFlags flags)467 void SkDebugCanvas::drawBitmapRectToRect(const SkBitmap& bitmap,
468                                          const SkRect* src, const SkRect& dst,
469                                          const SkPaint* paint,
470                                          SkCanvas::DrawBitmapRectFlags flags) {
471     this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
472 }
473 
drawBitmapMatrix(const SkBitmap & bitmap,const SkMatrix & matrix,const SkPaint * paint)474 void SkDebugCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
475                                      const SkMatrix& matrix, const SkPaint* paint) {
476     this->addDrawCommand(new SkDrawBitmapMatrixCommand(bitmap, matrix, paint));
477 }
478 
drawBitmapNine(const SkBitmap & bitmap,const SkIRect & center,const SkRect & dst,const SkPaint * paint)479 void SkDebugCanvas::drawBitmapNine(const SkBitmap& bitmap,
480         const SkIRect& center, const SkRect& dst, const SkPaint* paint) {
481     this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
482 }
483 
drawData(const void * data,size_t length)484 void SkDebugCanvas::drawData(const void* data, size_t length) {
485     this->addDrawCommand(new SkDrawDataCommand(data, length));
486 }
487 
beginCommentGroup(const char * description)488 void SkDebugCanvas::beginCommentGroup(const char* description) {
489     this->addDrawCommand(new SkBeginCommentGroupCommand(description));
490 }
491 
addComment(const char * kywd,const char * value)492 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
493     this->addDrawCommand(new SkCommentCommand(kywd, value));
494 }
495 
endCommentGroup()496 void SkDebugCanvas::endCommentGroup() {
497     this->addDrawCommand(new SkEndCommentGroupCommand());
498 }
499 
drawOval(const SkRect & oval,const SkPaint & paint)500 void SkDebugCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
501     this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
502 }
503 
drawPaint(const SkPaint & paint)504 void SkDebugCanvas::drawPaint(const SkPaint& paint) {
505     this->addDrawCommand(new SkDrawPaintCommand(paint));
506 }
507 
drawPath(const SkPath & path,const SkPaint & paint)508 void SkDebugCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
509     this->addDrawCommand(new SkDrawPathCommand(path, paint));
510 }
511 
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)512 void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
513                                   const SkMatrix* matrix,
514                                   const SkPaint* paint) {
515     this->addDrawCommand(new SkDrawPictureCommand(picture, matrix, paint));
516 }
517 
drawPoints(PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)518 void SkDebugCanvas::drawPoints(PointMode mode, size_t count,
519                                const SkPoint pts[], const SkPaint& paint) {
520     this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
521 }
522 
onDrawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)523 void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
524                                   const SkPaint& paint) {
525     this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint));
526 }
527 
onDrawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)528 void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
529                                    SkScalar constY, const SkPaint& paint) {
530     this->addDrawCommand(
531         new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint));
532 }
533 
drawRect(const SkRect & rect,const SkPaint & paint)534 void SkDebugCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
535     // NOTE(chudy): Messing up when renamed to DrawRect... Why?
536     addDrawCommand(new SkDrawRectCommand(rect, paint));
537 }
538 
drawRRect(const SkRRect & rrect,const SkPaint & paint)539 void SkDebugCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
540     this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
541 }
542 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)543 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
544                                  const SkPaint& paint) {
545     this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
546 }
547 
drawSprite(const SkBitmap & bitmap,int left,int top,const SkPaint * paint=NULL)548 void SkDebugCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
549                                const SkPaint* paint = NULL) {
550     this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint));
551 }
552 
onDrawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)553 void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
554                                const SkPaint& paint) {
555     this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint));
556 }
557 
onDrawTextOnPath(const void * text,size_t byteLength,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)558 void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
559                                      const SkMatrix* matrix, const SkPaint& paint) {
560     this->addDrawCommand(
561         new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint));
562 }
563 
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)564 void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
565                                    const SkPaint& paint) {
566     this->addDrawCommand(new SkDrawTextBlobCommand(blob, x, y, paint));
567 }
568 
drawVertices(VertexMode vmode,int vertexCount,const SkPoint vertices[],const SkPoint texs[],const SkColor colors[],SkXfermode *,const uint16_t indices[],int indexCount,const SkPaint & paint)569 void SkDebugCanvas::drawVertices(VertexMode vmode, int vertexCount,
570         const SkPoint vertices[], const SkPoint texs[], const SkColor colors[],
571         SkXfermode*, const uint16_t indices[], int indexCount,
572         const SkPaint& paint) {
573     this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices,
574                          texs, colors, NULL, indices, indexCount, paint));
575 }
576 
onPushCull(const SkRect & cullRect)577 void SkDebugCanvas::onPushCull(const SkRect& cullRect) {
578     this->addDrawCommand(new SkPushCullCommand(cullRect));
579 }
580 
onPopCull()581 void SkDebugCanvas::onPopCull() {
582     this->addDrawCommand(new SkPopCullCommand());
583 }
584 
willRestore()585 void SkDebugCanvas::willRestore() {
586     this->addDrawCommand(new SkRestoreCommand());
587     this->INHERITED::willRestore();
588 }
589 
willSave()590 void SkDebugCanvas::willSave() {
591     this->addDrawCommand(new SkSaveCommand());
592     this->INHERITED::willSave();
593 }
594 
willSaveLayer(const SkRect * bounds,const SkPaint * paint,SaveFlags flags)595 SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint,
596                                                          SaveFlags flags) {
597     this->addDrawCommand(new SkSaveLayerCommand(bounds, paint, flags));
598     this->INHERITED::willSaveLayer(bounds, paint, flags);
599     // No need for a full layer.
600     return kNoLayer_SaveLayerStrategy;
601 }
602 
didSetMatrix(const SkMatrix & matrix)603 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
604     this->addDrawCommand(new SkSetMatrixCommand(matrix));
605     this->INHERITED::didSetMatrix(matrix);
606 }
607 
toggleCommand(int index,bool toggle)608 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
609     SkASSERT(index < fCommandVector.count());
610     fCommandVector[index]->setVisible(toggle);
611 }
612 
613 static const char* gFillTypeStrs[] = {
614     "kWinding_FillType",
615     "kEvenOdd_FillType",
616     "kInverseWinding_FillType",
617     "kInverseEvenOdd_FillType"
618 };
619 
620 static const char* gOpStrs[] = {
621     "kDifference_PathOp",
622     "kIntersect_PathOp",
623     "kUnion_PathOp",
624     "kXor_PathOp",
625     "kReverseDifference_PathOp",
626 };
627 
628 static const char kHTML4SpaceIndent[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
629 
outputScalar(SkScalar num)630 void SkDebugCanvas::outputScalar(SkScalar num) {
631     if (num == (int) num) {
632         fClipStackData.appendf("%d", (int) num);
633     } else {
634         SkString str;
635         str.printf("%1.9g", num);
636         int width = (int) str.size();
637         const char* cStr = str.c_str();
638         while (cStr[width - 1] == '0') {
639             --width;
640         }
641         str.resize(width);
642         fClipStackData.appendf("%sf", str.c_str());
643     }
644 }
645 
outputPointsCommon(const SkPoint * pts,int count)646 void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) {
647     for (int index = 0; index < count; ++index) {
648         this->outputScalar(pts[index].fX);
649         fClipStackData.appendf(", ");
650         this->outputScalar(pts[index].fY);
651         if (index + 1 < count) {
652             fClipStackData.appendf(", ");
653         }
654     }
655 }
656 
outputPoints(const SkPoint * pts,int count)657 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
658     this->outputPointsCommon(pts, count);
659     fClipStackData.appendf(");<br>");
660 }
661 
outputConicPoints(const SkPoint * pts,SkScalar weight)662 void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) {
663     this->outputPointsCommon(pts, 2);
664     fClipStackData.appendf(", ");
665     this->outputScalar(weight);
666     fClipStackData.appendf(");<br>");
667 }
668 
addPathData(const SkPath & path,const char * pathName)669 void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) {
670     SkPath::RawIter iter(path);
671     SkPath::FillType fillType = path.getFillType();
672     fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName);
673     fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName,
674             gFillTypeStrs[fillType]);
675     iter.setPath(path);
676     uint8_t verb;
677     SkPoint pts[4];
678     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
679         switch (verb) {
680             case SkPath::kMove_Verb:
681                 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
682                 this->outputPoints(&pts[0], 1);
683                 continue;
684             case SkPath::kLine_Verb:
685                 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
686                 this->outputPoints(&pts[1], 1);
687                 break;
688             case SkPath::kQuad_Verb:
689                 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
690                 this->outputPoints(&pts[1], 2);
691                 break;
692             case SkPath::kConic_Verb:
693                 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
694                 this->outputConicPoints(&pts[1], iter.conicWeight());
695                 break;
696             case SkPath::kCubic_Verb:
697                 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
698                 this->outputPoints(&pts[1], 3);
699                 break;
700             case SkPath::kClose_Verb:
701                 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
702                 break;
703             default:
704                 SkDEBUGFAIL("bad verb");
705                 return;
706         }
707     }
708 }
709 
addClipStackData(const SkPath & devPath,const SkPath & operand,SkRegion::Op elementOp)710 void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand,
711                                      SkRegion::Op elementOp) {
712     if (elementOp == SkRegion::kReplace_Op) {
713         if (!lastClipStackData(devPath)) {
714             fSaveDevPath = operand;
715         }
716         fCalledAddStackData = false;
717     } else {
718         fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter,"
719             " const char* filename) {<br>");
720         addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path");
721         addPathData(operand, "pathB");
722         fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>",
723             kHTML4SpaceIndent, gOpStrs[elementOp]);
724         fClipStackData.appendf("}<br>");
725         fCalledAddStackData = true;
726     }
727 }
728 
lastClipStackData(const SkPath & devPath)729 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
730     if (fCalledAddStackData) {
731         fClipStackData.appendf("<br>");
732         addPathData(devPath, "pathOut");
733         return true;
734     }
735     return false;
736 }
737