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 
17 namespace {
18 
19 class OverdrawXfermode : public SkXfermode {
20 public:
xferColor(SkPMColor src,SkPMColor dst) const21     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 
getFactory() const54     Factory getFactory() const override { return NULL; }
55 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const56     void toString(SkString* str) const override { str->set("OverdrawXfermode"); }
57 #endif
58 };
59 
60 class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
61 public:
DebugPaintFilterCanvas(int width,int height,bool overdrawViz,bool overrideFilterQuality,SkFilterQuality quality)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 
69 protected:
onFilterPaint(SkPaint * paint,Type) const70     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 
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)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 
86 private:
87     SkAutoTUnref<SkXfermode> fOverdrawXfermode;
88 
89     bool fOverrideFilterQuality;
90     SkFilterQuality fFilterQuality;
91 
92     typedef SkPaintFilterCanvas INHERITED;
93 };
94 
95 }
96 
SkDebugCanvas(int width,int height)97 SkDebugCanvas::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 
~SkDebugCanvas()126 SkDebugCanvas::~SkDebugCanvas() {
127     fCommandVector.deleteAll();
128 }
129 
addDrawCommand(SkDrawCommand * command)130 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
131     fCommandVector.push(command);
132 }
133 
draw(SkCanvas * canvas)134 void SkDebugCanvas::draw(SkCanvas* canvas) {
135     if (!fCommandVector.isEmpty()) {
136         this->drawTo(canvas, fCommandVector.count() - 1);
137     }
138 }
139 
applyUserTransform(SkCanvas * canvas)140 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
141     canvas->concat(fUserMatrix);
142 }
143 
getCommandAtPoint(int x,int y,int index)144 int 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 
167 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
168 public:
SkDebugClipVisitor(SkCanvas * canvas)169     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
170 
clipRect(const SkRect & r,SkRegion::Op,bool doAA)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     }
clipRRect(const SkRRect & rr,SkRegion::Op,bool doAA)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     }
clipPath(const SkPath & path,SkRegion::Op,bool doAA)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 
193 protected:
194     SkCanvas* fCanvas;
195 
196 private:
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
markActiveCommands(int index)202 void 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 
drawTo(SkCanvas * canvas,int index)224 void 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 
deleteDrawCommandAt(int index)321 void SkDebugCanvas::deleteDrawCommandAt(int index) {
322     SkASSERT(index < fCommandVector.count());
323     delete fCommandVector[index];
324     fCommandVector.remove(index);
325 }
326 
getDrawCommandAt(int index)327 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
328     SkASSERT(index < fCommandVector.count());
329     return fCommandVector[index];
330 }
331 
setDrawCommandAt(int index,SkDrawCommand * command)332 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
333     SkASSERT(index < fCommandVector.count());
334     delete fCommandVector[index];
335     fCommandVector[index] = command;
336 }
337 
getCommandInfo(int index) const338 const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
339     SkASSERT(index < fCommandVector.count());
340     return fCommandVector[index]->Info();
341 }
342 
getDrawCommandVisibilityAt(int index)343 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
344     SkASSERT(index < fCommandVector.count());
345     return fCommandVector[index]->isVisible();
346 }
347 
getDrawCommands() const348 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
349     return fCommandVector;
350 }
351 
getDrawCommands()352 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
353     return fCommandVector;
354 }
355 
updatePaintFilterCanvas()356 void 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 
setOverdrawViz(bool overdrawViz)370 void SkDebugCanvas::setOverdrawViz(bool overdrawViz) {
371     if (fOverdrawViz == overdrawViz) {
372         return;
373     }
374 
375     fOverdrawViz = overdrawViz;
376     this->updatePaintFilterCanvas();
377 }
378 
overrideTexFiltering(bool overrideTexFiltering,SkFilterQuality quality)379 void 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 
onClipPath(const SkPath & path,SkRegion::Op op,ClipEdgeStyle edgeStyle)389 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
390     this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
391 }
392 
onClipRect(const SkRect & rect,SkRegion::Op op,ClipEdgeStyle edgeStyle)393 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
394     this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
395 }
396 
onClipRRect(const SkRRect & rrect,SkRegion::Op op,ClipEdgeStyle edgeStyle)397 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
398     this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
399 }
400 
onClipRegion(const SkRegion & region,SkRegion::Op op)401 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
402     this->addDrawCommand(new SkClipRegionCommand(region, op));
403 }
404 
didConcat(const SkMatrix & matrix)405 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
406     this->addDrawCommand(new SkConcatCommand(matrix));
407     this->INHERITED::didConcat(matrix);
408 }
409 
onDrawBitmap(const SkBitmap & bitmap,SkScalar left,SkScalar top,const SkPaint * paint)410 void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
411                                  SkScalar top, const SkPaint* paint) {
412     this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
413 }
414 
onDrawBitmapRect(const SkBitmap & bitmap,const SkRect * src,const SkRect & dst,const SkPaint * paint,DrawBitmapRectFlags flags)415 void 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 
onDrawBitmapNine(const SkBitmap & bitmap,const SkIRect & center,const SkRect & dst,const SkPaint * paint)420 void 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 
onDrawImage(const SkImage * image,SkScalar left,SkScalar top,const SkPaint * paint)425 void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
426                                 const SkPaint* paint) {
427     SkDebugf("SkDebugCanvas::onDrawImage unimplemented\n");
428 }
429 
onDrawImageRect(const SkImage * image,const SkRect * src,const SkRect & dst,const SkPaint * paint)430 void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
431                                     const SkPaint* paint) {
432     SkDebugf("SkDebugCanvas::onDrawImageRect unimplemented\n");
433 }
434 
beginCommentGroup(const char * description)435 void SkDebugCanvas::beginCommentGroup(const char* description) {
436     this->addDrawCommand(new SkBeginCommentGroupCommand(description));
437 }
438 
addComment(const char * kywd,const char * value)439 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
440     this->addDrawCommand(new SkCommentCommand(kywd, value));
441 }
442 
endCommentGroup()443 void SkDebugCanvas::endCommentGroup() {
444     this->addDrawCommand(new SkEndCommentGroupCommand());
445 }
446 
onDrawOval(const SkRect & oval,const SkPaint & paint)447 void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
448     this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
449 }
450 
onDrawPaint(const SkPaint & paint)451 void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
452     this->addDrawCommand(new SkDrawPaintCommand(paint));
453 }
454 
onDrawPath(const SkPath & path,const SkPaint & paint)455 void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
456     this->addDrawCommand(new SkDrawPathCommand(path, paint));
457 }
458 
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)459 void 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 
onDrawPoints(PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)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 
onDrawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)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 
onDrawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)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 
onDrawRect(const SkRect & rect,const SkPaint & paint)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 
onDrawRRect(const SkRRect & rrect,const SkPaint & paint)488 void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
489     this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
490 }
491 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)492 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
493                                  const SkPaint& paint) {
494     this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
495 }
496 
onDrawSprite(const SkBitmap & bitmap,int left,int top,const SkPaint * paint)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 
onDrawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)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 
onDrawTextOnPath(const void * text,size_t byteLength,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)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 
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)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 
onDrawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkXfermode * xmode,const SkPaint & paint)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 
onDrawVertices(VertexMode vmode,int vertexCount,const SkPoint vertices[],const SkPoint texs[],const SkColor colors[],SkXfermode *,const uint16_t indices[],int indexCount,const SkPaint & paint)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 
willRestore()531 void SkDebugCanvas::willRestore() {
532     this->addDrawCommand(new SkRestoreCommand());
533     this->INHERITED::willRestore();
534 }
535 
willSave()536 void SkDebugCanvas::willSave() {
537     this->addDrawCommand(new SkSaveCommand());
538     this->INHERITED::willSave();
539 }
540 
willSaveLayer(const SkRect * bounds,const SkPaint * paint,SaveFlags flags)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 
didSetMatrix(const SkMatrix & matrix)549 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
550     this->addDrawCommand(new SkSetMatrixCommand(matrix));
551     this->INHERITED::didSetMatrix(matrix);
552 }
553 
toggleCommand(int index,bool toggle)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 
outputScalar(SkScalar num)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 
outputPointsCommon(const SkPoint * pts,int count)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 
outputPoints(const SkPoint * pts,int count)603 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
604     this->outputPointsCommon(pts, count);
605     fClipStackData.appendf(");<br>");
606 }
607 
outputConicPoints(const SkPoint * pts,SkScalar weight)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 
addPathData(const SkPath & path,const char * pathName)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 
addClipStackData(const SkPath & devPath,const SkPath & operand,SkRegion::Op elementOp)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 
lastClipStackData(const SkPath & devPath)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 }
683