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