3 * Copyright 2012 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #include "SkColorPriv.h"
11 #include "SkDebugCanvas.h"
12 #include "SkDrawCommand.h"
13 #include "SkDrawFilter.h"
15 #include "SkXfermode.h"
17 SkDebugCanvas::SkDebugCanvas(int width, int height)
18 : INHERITED(width, height)
23 , fOverdrawFilter(NULL)
24 , fOverrideTexFiltering(false)
25 , fTexOverrideFilter(NULL) {
28 // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
29 // operations. This can lead to problems in the debugger which expects all
30 // the operations in the captured skp to appear in the debug canvas. To
31 // circumvent this we create a wide open clip here (an empty clip rect
32 // is not sufficient).
33 // Internally, the SkRect passed to clipRect is converted to an SkIRect and
34 // rounded out. The following code creates a nearly maximal rect that will
35 // not get collapsed by the coming conversions (Due to precision loss the
36 // inset has to be surprisingly large).
37 SkIRect largeIRect = SkIRect::MakeLargest();
38 largeIRect.inset(1024, 1024);
39 SkRect large = SkRect::Make(largeIRect);
41 SkASSERT(!large.roundOut().isEmpty());
43 // call the base class' version to avoid adding a draw command
44 this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
47 SkDebugCanvas::~SkDebugCanvas() {
48 fCommandVector.deleteAll();
49 SkSafeUnref(fOverdrawFilter);
50 SkSafeUnref(fTexOverrideFilter);
53 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
54 command->setOffset(this->getOpID());
55 fCommandVector.push(command);
58 void SkDebugCanvas::draw(SkCanvas* canvas) {
59 if (!fCommandVector.isEmpty()) {
60 this->drawTo(canvas, fCommandVector.count() - 1);
64 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
65 canvas->concat(fUserMatrix);
68 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
70 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
72 SkCanvas canvas(bitmap);
73 canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
74 this->applyUserTransform(&canvas);
77 SkColor prev = bitmap.getColor(0,0);
78 for (int i = 0; i < index; i++) {
79 if (fCommandVector[i]->isVisible()) {
80 fCommandVector[i]->setUserMatrix(fUserMatrix);
81 fCommandVector[i]->execute(&canvas);
83 if (prev != bitmap.getColor(0,0)) {
86 prev = bitmap.getColor(0,0);
91 class OverdrawXfermode : public SkXfermode {
93 SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE {
94 // This table encodes the color progression of the overdraw visualization
95 static const SkPMColor gTable[] = {
96 SkPackARGB32(0x00, 0x00, 0x00, 0x00),
97 SkPackARGB32(0xFF, 128, 158, 255),
98 SkPackARGB32(0xFF, 170, 185, 212),
99 SkPackARGB32(0xFF, 213, 195, 170),
100 SkPackARGB32(0xFF, 255, 192, 127),
101 SkPackARGB32(0xFF, 255, 185, 85),
102 SkPackARGB32(0xFF, 255, 165, 42),
103 SkPackARGB32(0xFF, 255, 135, 0),
104 SkPackARGB32(0xFF, 255, 95, 0),
105 SkPackARGB32(0xFF, 255, 50, 0),
106 SkPackARGB32(0xFF, 255, 0, 0)
111 if (SkColorGetR(dst) < 64) { // 0
113 } else if (SkColorGetG(dst) < 25) { // 10
114 idx = 9; // cap at 9 for upcoming increment
115 } else if ((SkColorGetB(dst)+21)/42 > 0) { // 1-6
116 idx = 7 - (SkColorGetB(dst)+21)/42;
118 idx = 10 - (SkColorGetG(dst)+22)/45;
121 SkASSERT(idx < (int)SK_ARRAY_COUNT(gTable));
126 Factory getFactory() const SK_OVERRIDE { return NULL; }
127 #ifndef SK_IGNORE_TO_STRING
128 virtual void toString(SkString* str) const SK_OVERRIDE { str->set("OverdrawXfermode"); }
132 class SkOverdrawFilter : public SkDrawFilter {
135 fXferMode = SkNEW(OverdrawXfermode);
138 virtual ~SkOverdrawFilter() {
142 bool filter(SkPaint* p, Type) SK_OVERRIDE {
143 p->setXfermode(fXferMode);
144 p->setAntiAlias(false);
149 SkXfermode* fXferMode;
152 typedef SkDrawFilter INHERITED;
155 // SkTexOverrideFilter modifies every paint to use the specified
156 // texture filtering mode
157 class SkTexOverrideFilter : public SkDrawFilter {
159 SkTexOverrideFilter() : fFilterQuality(kNone_SkFilterQuality) {
162 void setFilterQuality(SkFilterQuality filterQuality) {
163 fFilterQuality = filterQuality;
166 bool filter(SkPaint* p, Type) SK_OVERRIDE {
167 p->setFilterQuality(fFilterQuality);
172 SkFilterQuality fFilterQuality;
175 typedef SkDrawFilter INHERITED;
178 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
180 SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
182 void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE {
184 p.setColor(SK_ColorRED);
185 p.setStyle(SkPaint::kStroke_Style);
186 p.setAntiAlias(doAA);
187 fCanvas->drawRect(r, p);
189 void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE {
191 p.setColor(SK_ColorGREEN);
192 p.setStyle(SkPaint::kStroke_Style);
193 p.setAntiAlias(doAA);
194 fCanvas->drawRRect(rr, p);
196 void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE {
198 p.setColor(SK_ColorBLUE);
199 p.setStyle(SkPaint::kStroke_Style);
200 p.setAntiAlias(doAA);
201 fCanvas->drawPath(path, p);
208 typedef SkCanvas::ClipVisitor INHERITED;
211 // set up the saveLayer commands so that the active ones
212 // return true in their 'active' method
213 void SkDebugCanvas::markActiveCommands(int index) {
214 fActiveLayers.rewind();
216 for (int i = 0; i < fCommandVector.count(); ++i) {
217 fCommandVector[i]->setActive(false);
220 for (int i = 0; i < index; ++i) {
221 SkDrawCommand::Action result = fCommandVector[i]->action();
222 if (SkDrawCommand::kPushLayer_Action == result) {
223 fActiveLayers.push(fCommandVector[i]);
224 } else if (SkDrawCommand::kPopLayer_Action == result) {
229 for (int i = 0; i < fActiveLayers.count(); ++i) {
230 fActiveLayers[i]->setActive(true);
235 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
236 SkASSERT(!fCommandVector.isEmpty());
237 SkASSERT(index < fCommandVector.count());
239 int saveCount = canvas->save();
241 SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()),
242 SkIntToScalar(canvas->getBaseLayerSize().height()));
244 bool pathOpsMode = getAllowSimplifyClip();
245 canvas->setAllowSimplifyClip(pathOpsMode);
246 canvas->clear(SK_ColorTRANSPARENT);
247 canvas->resetMatrix();
248 if (!windowRect.isEmpty()) {
249 canvas->clipRect(windowRect, SkRegion::kReplace_Op);
251 this->applyUserTransform(canvas);
253 // The setting of the draw filter has to go here (rather than in
254 // SkRasterWidget) due to the canvas restores this class performs.
255 // Since the draw filter is stored in the layer stack if we
256 // call setDrawFilter on anything but the root layer odd things happen.
258 if (NULL == fOverdrawFilter) {
259 fOverdrawFilter = new SkOverdrawFilter;
262 if (fOverdrawFilter != canvas->getDrawFilter()) {
263 canvas->setDrawFilter(fOverdrawFilter);
265 } else if (fOverrideTexFiltering) {
266 if (NULL == fTexOverrideFilter) {
267 fTexOverrideFilter = new SkTexOverrideFilter;
270 if (fTexOverrideFilter != canvas->getDrawFilter()) {
271 canvas->setDrawFilter(fTexOverrideFilter);
274 canvas->setDrawFilter(NULL);
278 this->markActiveCommands(index);
281 for (int i = 0; i <= index; i++) {
282 if (i == index && fFilter) {
283 canvas->clear(0xAAFFFFFF);
286 if (fCommandVector[i]->isVisible()) {
287 if (fMegaVizMode && fCommandVector[i]->active()) {
288 // "active" commands execute their visualization behaviors:
289 // All active saveLayers get replaced with saves so all draws go to the
291 // All active culls draw their cull box
292 fCommandVector[i]->vizExecute(canvas);
294 fCommandVector[i]->setUserMatrix(fUserMatrix);
295 fCommandVector[i]->execute(canvas);
303 canvas->resetMatrix();
305 if (!windowRect.isEmpty()) {
306 SkRect r = windowRect;
307 r.outset(SK_Scalar1, SK_Scalar1);
308 canvas->clipRect(r, SkRegion::kReplace_Op);
310 // visualize existing clips
311 SkDebugClipVisitor visitor(canvas);
313 canvas->replayClips(&visitor);
318 this->resetClipStackData();
319 const SkClipStack* clipStack = canvas->getClipStack();
320 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
321 const SkClipStack::Element* element;
323 while ((element = iter.next())) {
324 SkClipStack::Element::Type type = element->getType();
326 if (type != SkClipStack::Element::kEmpty_Type) {
327 element->asPath(&operand);
329 SkRegion::Op elementOp = element->getOp();
330 this->addClipStackData(devPath, operand, elementOp);
331 if (elementOp == SkRegion::kReplace_Op) {
334 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
337 this->lastClipStackData(devPath);
339 fMatrix = canvas->getTotalMatrix();
340 if (!canvas->getClipDeviceBounds(&fClip)) {
344 canvas->restoreToCount(saveCount);
347 void SkDebugCanvas::deleteDrawCommandAt(int index) {
348 SkASSERT(index < fCommandVector.count());
349 delete fCommandVector[index];
350 fCommandVector.remove(index);
353 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
354 SkASSERT(index < fCommandVector.count());
355 return fCommandVector[index];
358 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
359 SkASSERT(index < fCommandVector.count());
360 delete fCommandVector[index];
361 fCommandVector[index] = command;
364 const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
365 SkASSERT(index < fCommandVector.count());
366 return fCommandVector[index]->Info();
369 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
370 SkASSERT(index < fCommandVector.count());
371 return fCommandVector[index]->isVisible();
374 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
375 return fCommandVector;
378 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
379 return fCommandVector;
382 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
383 if (NULL == fTexOverrideFilter) {
384 fTexOverrideFilter = new SkTexOverrideFilter;
387 fOverrideTexFiltering = overrideTexFiltering;
388 fTexOverrideFilter->setFilterQuality(quality);
391 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
392 this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
395 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
396 this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
399 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
400 this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
403 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
404 this->addDrawCommand(new SkClipRegionCommand(region, op));
407 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
408 this->addDrawCommand(new SkConcatCommand(matrix));
409 this->INHERITED::didConcat(matrix);
412 void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
413 SkScalar top, const SkPaint* paint) {
414 this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
417 void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
418 const SkPaint* paint, DrawBitmapRectFlags flags) {
419 this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
422 void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
423 const SkRect& dst, const SkPaint* paint) {
424 this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
427 void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
428 const SkPaint* paint) {
429 SkDebugf("SkDebugCanvas::onDrawImage unimplemented\n");
432 void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
433 const SkPaint* paint) {
434 SkDebugf("SkDebugCanvas::onDrawImageRect unimplemented\n");
437 void SkDebugCanvas::beginCommentGroup(const char* description) {
438 this->addDrawCommand(new SkBeginCommentGroupCommand(description));
441 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
442 this->addDrawCommand(new SkCommentCommand(kywd, value));
445 void SkDebugCanvas::endCommentGroup() {
446 this->addDrawCommand(new SkEndCommentGroupCommand());
449 void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
450 this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
453 void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
454 this->addDrawCommand(new SkDrawPaintCommand(paint));
457 void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
458 this->addDrawCommand(new SkDrawPathCommand(path, paint));
461 void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
462 const SkMatrix* matrix,
463 const SkPaint* paint) {
464 this->addDrawCommand(new SkDrawPictureCommand(picture, matrix, 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));
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));
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));
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));
488 void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
489 this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
492 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
493 const SkPaint& paint) {
494 this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
497 void SkDebugCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
498 this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, 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));
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));
512 void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
513 const SkPaint& paint) {
514 this->addDrawCommand(new SkDrawTextBlobCommand(blob, x, y, 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));
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));
531 void SkDebugCanvas::willRestore() {
532 this->addDrawCommand(new SkRestoreCommand());
533 this->INHERITED::willRestore();
536 void SkDebugCanvas::willSave() {
537 this->addDrawCommand(new SkSaveCommand());
538 this->INHERITED::willSave();
541 SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint,
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;
549 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
550 this->addDrawCommand(new SkSetMatrixCommand(matrix));
551 this->INHERITED::didSetMatrix(matrix);
554 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
555 SkASSERT(index < fCommandVector.count());
556 fCommandVector[index]->setVisible(toggle);
559 static const char* gFillTypeStrs[] = {
562 "kInverseWinding_FillType",
563 "kInverseEvenOdd_FillType"
566 static const char* gOpStrs[] = {
567 "kDifference_PathOp",
571 "kReverseDifference_PathOp",
574 static const char kHTML4SpaceIndent[] = " ";
576 void SkDebugCanvas::outputScalar(SkScalar num) {
577 if (num == (int) num) {
578 fClipStackData.appendf("%d", (int) num);
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') {
588 fClipStackData.appendf("%sf", str.c_str());
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(", ");
603 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
604 this->outputPointsCommon(pts, count);
605 fClipStackData.appendf(");<br>");
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>");
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]);
624 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
626 case SkPath::kMove_Verb:
627 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
628 this->outputPoints(&pts[0], 1);
630 case SkPath::kLine_Verb:
631 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
632 this->outputPoints(&pts[1], 1);
634 case SkPath::kQuad_Verb:
635 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
636 this->outputPoints(&pts[1], 2);
638 case SkPath::kConic_Verb:
639 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
640 this->outputConicPoints(&pts[1], iter.conicWeight());
642 case SkPath::kCubic_Verb:
643 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
644 this->outputPoints(&pts[1], 3);
646 case SkPath::kClose_Verb:
647 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
650 SkDEBUGFAIL("bad verb");
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;
662 fCalledAddStackData = false;
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;
675 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
676 if (fCalledAddStackData) {
677 fClipStackData.appendf("<br>");
678 addPathData(devPath, "pathOut");