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[] = " ";
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