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