Home | History | Annotate | Download | only in debugger
      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 
     17 SkDebugCanvas::SkDebugCanvas(int width, int height)
     18         : INHERITED(width, height)
     19         , fPicture(NULL)
     20         , fWidth(width)
     21         , fHeight(height)
     22         , fFilter(false)
     23         , fMegaVizMode(false)
     24         , fIndex(0)
     25         , fOverdrawViz(false)
     26         , fOverdrawFilter(NULL)
     27         , fOverrideTexFiltering(false)
     28         , fTexOverrideFilter(NULL)
     29         , fOutstandingSaveCount(0) {
     30     fUserMatrix.reset();
     31 
     32     // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
     33     // operations. This can lead to problems in the debugger which expects all
     34     // the operations in the captured skp to appear in the debug canvas. To
     35     // circumvent this we create a wide open clip here (an empty clip rect
     36     // is not sufficient).
     37     // Internally, the SkRect passed to clipRect is converted to an SkIRect and
     38     // rounded out. The following code creates a nearly maximal rect that will
     39     // not get collapsed by the coming conversions (Due to precision loss the
     40     // inset has to be surprisingly large).
     41     SkIRect largeIRect = SkIRect::MakeLargest();
     42     largeIRect.inset(1024, 1024);
     43     SkRect large = SkRect::Make(largeIRect);
     44 #ifdef SK_DEBUG
     45     large.roundOut(&largeIRect);
     46     SkASSERT(!largeIRect.isEmpty());
     47 #endif
     48     // call the base class' version to avoid adding a draw command
     49     this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
     50 }
     51 
     52 SkDebugCanvas::~SkDebugCanvas() {
     53     fCommandVector.deleteAll();
     54     SkSafeUnref(fOverdrawFilter);
     55     SkSafeUnref(fTexOverrideFilter);
     56 }
     57 
     58 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
     59     command->setOffset(this->getOpID());
     60     fCommandVector.push(command);
     61 }
     62 
     63 void SkDebugCanvas::draw(SkCanvas* canvas) {
     64     if (!fCommandVector.isEmpty()) {
     65         this->drawTo(canvas, fCommandVector.count() - 1);
     66     }
     67 }
     68 
     69 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
     70     canvas->concat(fUserMatrix);
     71 }
     72 
     73 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
     74     SkBitmap bitmap;
     75     bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
     76 
     77     SkCanvas canvas(bitmap);
     78     canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
     79     applyUserTransform(&canvas);
     80 
     81     int layer = 0;
     82     SkColor prev = bitmap.getColor(0,0);
     83     for (int i = 0; i < index; i++) {
     84         if (fCommandVector[i]->isVisible()) {
     85             fCommandVector[i]->execute(&canvas);
     86         }
     87         if (prev != bitmap.getColor(0,0)) {
     88             layer = i;
     89         }
     90         prev = bitmap.getColor(0,0);
     91     }
     92     return layer;
     93 }
     94 
     95 class OverdrawXfermode : public SkXfermode {
     96 public:
     97     virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE {
     98         // This table encodes the color progression of the overdraw visualization
     99         static const SkPMColor gTable[] = {
    100             SkPackARGB32(0x00, 0x00, 0x00, 0x00),
    101             SkPackARGB32(0xFF, 128, 158, 255),
    102             SkPackARGB32(0xFF, 170, 185, 212),
    103             SkPackARGB32(0xFF, 213, 195, 170),
    104             SkPackARGB32(0xFF, 255, 192, 127),
    105             SkPackARGB32(0xFF, 255, 185, 85),
    106             SkPackARGB32(0xFF, 255, 165, 42),
    107             SkPackARGB32(0xFF, 255, 135, 0),
    108             SkPackARGB32(0xFF, 255,  95, 0),
    109             SkPackARGB32(0xFF, 255,  50, 0),
    110             SkPackARGB32(0xFF, 255,  0, 0)
    111         };
    112 
    113         for (size_t i = 0; i < SK_ARRAY_COUNT(gTable)-1; ++i) {
    114             if (gTable[i] == dst) {
    115                 return gTable[i+1];
    116             }
    117         }
    118 
    119         return gTable[SK_ARRAY_COUNT(gTable)-1];
    120     }
    121 
    122     virtual Factory getFactory() const SK_OVERRIDE { return NULL; }
    123 #ifndef SK_IGNORE_TO_STRING
    124     virtual void toString(SkString* str) const { str->set("OverdrawXfermode"); }
    125 #endif
    126 };
    127 
    128 class SkOverdrawFilter : public SkDrawFilter {
    129 public:
    130     SkOverdrawFilter() {
    131         fXferMode = SkNEW(OverdrawXfermode);
    132     }
    133 
    134     virtual ~SkOverdrawFilter() {
    135         delete fXferMode;
    136     }
    137 
    138     virtual bool filter(SkPaint* p, Type) SK_OVERRIDE {
    139         p->setXfermode(fXferMode);
    140         return true;
    141     }
    142 
    143 protected:
    144     SkXfermode* fXferMode;
    145 
    146 private:
    147     typedef SkDrawFilter INHERITED;
    148 };
    149 
    150 // SkTexOverrideFilter modifies every paint to use the specified
    151 // texture filtering mode
    152 class SkTexOverrideFilter : public SkDrawFilter {
    153 public:
    154     SkTexOverrideFilter() : fFilterLevel(SkPaint::kNone_FilterLevel) {
    155     }
    156 
    157     void setFilterLevel(SkPaint::FilterLevel filterLevel) {
    158         fFilterLevel = filterLevel;
    159     }
    160 
    161     virtual bool filter(SkPaint* p, Type) SK_OVERRIDE {
    162         p->setFilterLevel(fFilterLevel);
    163         return true;
    164     }
    165 
    166 protected:
    167     SkPaint::FilterLevel fFilterLevel;
    168 
    169 private:
    170     typedef SkDrawFilter INHERITED;
    171 };
    172 
    173 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
    174 public:
    175     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
    176 
    177     virtual void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE {
    178         SkPaint p;
    179         p.setColor(SK_ColorRED);
    180         p.setStyle(SkPaint::kStroke_Style);
    181         p.setAntiAlias(doAA);
    182         fCanvas->drawRect(r, p);
    183     }
    184     virtual void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE {
    185         SkPaint p;
    186         p.setColor(SK_ColorGREEN);
    187         p.setStyle(SkPaint::kStroke_Style);
    188         p.setAntiAlias(doAA);
    189         fCanvas->drawRRect(rr, p);
    190     }
    191     virtual void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE {
    192         SkPaint p;
    193         p.setColor(SK_ColorBLUE);
    194         p.setStyle(SkPaint::kStroke_Style);
    195         p.setAntiAlias(doAA);
    196         fCanvas->drawPath(path, p);
    197     }
    198 
    199 protected:
    200     SkCanvas* fCanvas;
    201 
    202 private:
    203     typedef SkCanvas::ClipVisitor INHERITED;
    204 };
    205 
    206 // set up the saveLayer commands so that the active ones
    207 // return true in their 'active' method
    208 void SkDebugCanvas::markActiveCommands(int index) {
    209     fActiveLayers.rewind();
    210     fActiveCulls.rewind();
    211 
    212     for (int i = 0; i < fCommandVector.count(); ++i) {
    213         fCommandVector[i]->setActive(false);
    214     }
    215 
    216     for (int i = 0; i < index; ++i) {
    217         SkDrawCommand::Action result = fCommandVector[i]->action();
    218         if (SkDrawCommand::kPushLayer_Action == result) {
    219             fActiveLayers.push(fCommandVector[i]);
    220         } else if (SkDrawCommand::kPopLayer_Action == result) {
    221             fActiveLayers.pop();
    222         } else if (SkDrawCommand::kPushCull_Action == result) {
    223             fActiveCulls.push(fCommandVector[i]);
    224         } else if (SkDrawCommand::kPopCull_Action == result) {
    225             fActiveCulls.pop();
    226         }
    227     }
    228 
    229     for (int i = 0; i < fActiveLayers.count(); ++i) {
    230         fActiveLayers[i]->setActive(true);
    231     }
    232 
    233     for (int i = 0; i < fActiveCulls.count(); ++i) {
    234         fActiveCulls[i]->setActive(true);
    235     }
    236 }
    237 
    238 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
    239     SkASSERT(!fCommandVector.isEmpty());
    240     SkASSERT(index < fCommandVector.count());
    241     int i = 0;
    242 
    243     bool pathOpsMode = getAllowSimplifyClip();
    244     canvas->setAllowSimplifyClip(pathOpsMode);
    245     // This only works assuming the canvas and device are the same ones that
    246     // were previously drawn into because they need to preserve all saves
    247     // and restores.
    248     // The visibility filter also requires a full re-draw - otherwise we can
    249     // end up drawing the filter repeatedly.
    250     if (fIndex < index && !fFilter && !fMegaVizMode && !pathOpsMode) {
    251         i = fIndex + 1;
    252     } else {
    253         for (int j = 0; j < fOutstandingSaveCount; j++) {
    254             canvas->restore();
    255         }
    256         canvas->clear(SK_ColorTRANSPARENT);
    257         canvas->resetMatrix();
    258         SkRect rect = SkRect::MakeWH(SkIntToScalar(fWidth),
    259                                      SkIntToScalar(fHeight));
    260         canvas->clipRect(rect, SkRegion::kReplace_Op );
    261         applyUserTransform(canvas);
    262         fOutstandingSaveCount = 0;
    263     }
    264 
    265     // The setting of the draw filter has to go here (rather than in
    266     // SkRasterWidget) due to the canvas restores this class performs.
    267     // Since the draw filter is stored in the layer stack if we
    268     // call setDrawFilter on anything but the root layer odd things happen.
    269     if (fOverdrawViz) {
    270         if (NULL == fOverdrawFilter) {
    271             fOverdrawFilter = new SkOverdrawFilter;
    272         }
    273 
    274         if (fOverdrawFilter != canvas->getDrawFilter()) {
    275             canvas->setDrawFilter(fOverdrawFilter);
    276         }
    277     } else if (fOverrideTexFiltering) {
    278         if (NULL == fTexOverrideFilter) {
    279             fTexOverrideFilter = new SkTexOverrideFilter;
    280         }
    281 
    282         if (fTexOverrideFilter != canvas->getDrawFilter()) {
    283             canvas->setDrawFilter(fTexOverrideFilter);
    284         }
    285     } else {
    286         canvas->setDrawFilter(NULL);
    287     }
    288 
    289     if (fMegaVizMode) {
    290         this->markActiveCommands(index);
    291     }
    292 
    293     for (; i <= index; i++) {
    294         if (i == index && fFilter) {
    295             SkPaint p;
    296             p.setColor(0xAAFFFFFF);
    297             canvas->save();
    298             canvas->resetMatrix();
    299             SkRect mask;
    300             mask.set(SkIntToScalar(0), SkIntToScalar(0),
    301                     SkIntToScalar(fWidth), SkIntToScalar(fHeight));
    302             canvas->clipRect(mask, SkRegion::kReplace_Op, false);
    303             canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
    304                     SkIntToScalar(fWidth), SkIntToScalar(fHeight), p);
    305             canvas->restore();
    306         }
    307 
    308         if (fCommandVector[i]->isVisible()) {
    309             if (fMegaVizMode && fCommandVector[i]->active()) {
    310                 // "active" commands execute their visualization behaviors:
    311                 //     All active saveLayers get replaced with saves so all draws go to the
    312                 //     visible canvas.
    313                 //     All active culls draw their cull box
    314                 fCommandVector[i]->vizExecute(canvas);
    315             } else {
    316                 fCommandVector[i]->execute(canvas);
    317             }
    318 
    319             fCommandVector[i]->trackSaveState(&fOutstandingSaveCount);
    320         }
    321     }
    322 
    323     if (fMegaVizMode) {
    324         SkRect r = SkRect::MakeWH(SkIntToScalar(fWidth), SkIntToScalar(fHeight));
    325         r.outset(SK_Scalar1, SK_Scalar1);
    326 
    327         canvas->save();
    328         // nuke the CTM
    329         canvas->setMatrix(SkMatrix::I());
    330         // turn off clipping
    331         canvas->clipRect(r, SkRegion::kReplace_Op);
    332 
    333         // visualize existing clips
    334         SkDebugClipVisitor visitor(canvas);
    335 
    336         canvas->replayClips(&visitor);
    337 
    338         canvas->restore();
    339     }
    340     if (pathOpsMode) {
    341         this->resetClipStackData();
    342         const SkClipStack* clipStack = canvas->getClipStack();
    343         SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
    344         const SkClipStack::Element* element;
    345         SkPath devPath;
    346         while ((element = iter.next())) {
    347             SkClipStack::Element::Type type = element->getType();
    348             SkPath operand;
    349             if (type != SkClipStack::Element::kEmpty_Type) {
    350                element->asPath(&operand);
    351             }
    352             SkRegion::Op elementOp = element->getOp();
    353             this->addClipStackData(devPath, operand, elementOp);
    354             if (elementOp == SkRegion::kReplace_Op) {
    355                 devPath = operand;
    356             } else {
    357                 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
    358             }
    359         }
    360         this->lastClipStackData(devPath);
    361     }
    362     fMatrix = canvas->getTotalMatrix();
    363     if (!canvas->getClipDeviceBounds(&fClip)) {
    364         fClip.setEmpty();
    365     }
    366     fIndex = index;
    367 }
    368 
    369 void SkDebugCanvas::deleteDrawCommandAt(int index) {
    370     SkASSERT(index < fCommandVector.count());
    371     delete fCommandVector[index];
    372     fCommandVector.remove(index);
    373 }
    374 
    375 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
    376     SkASSERT(index < fCommandVector.count());
    377     return fCommandVector[index];
    378 }
    379 
    380 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
    381     SkASSERT(index < fCommandVector.count());
    382     delete fCommandVector[index];
    383     fCommandVector[index] = command;
    384 }
    385 
    386 SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) {
    387     SkASSERT(index < fCommandVector.count());
    388     return fCommandVector[index]->Info();
    389 }
    390 
    391 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
    392     SkASSERT(index < fCommandVector.count());
    393     return fCommandVector[index]->isVisible();
    394 }
    395 
    396 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
    397     return fCommandVector;
    398 }
    399 
    400 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
    401     return fCommandVector;
    402 }
    403 
    404 // TODO(chudy): Free command string memory.
    405 SkTArray<SkString>* SkDebugCanvas::getDrawCommandsAsStrings() const {
    406     SkTArray<SkString>* commandString = new SkTArray<SkString>(fCommandVector.count());
    407     if (!fCommandVector.isEmpty()) {
    408         for (int i = 0; i < fCommandVector.count(); i ++) {
    409             commandString->push_back() = fCommandVector[i]->toString();
    410         }
    411     }
    412     return commandString;
    413 }
    414 
    415 SkTDArray<size_t>* SkDebugCanvas::getDrawCommandOffsets() const {
    416     SkTDArray<size_t>* commandOffsets = new SkTDArray<size_t>;
    417     if (!fCommandVector.isEmpty()) {
    418         for (int i = 0; i < fCommandVector.count(); i ++) {
    419             *commandOffsets->push() = fCommandVector[i]->offset();
    420         }
    421     }
    422     return commandOffsets;
    423 }
    424 
    425 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkPaint::FilterLevel level) {
    426     if (NULL == fTexOverrideFilter) {
    427         fTexOverrideFilter = new SkTexOverrideFilter;
    428     }
    429 
    430     fOverrideTexFiltering = overrideTexFiltering;
    431     fTexOverrideFilter->setFilterLevel(level);
    432 }
    433 
    434 void SkDebugCanvas::clear(SkColor color) {
    435     this->addDrawCommand(new SkClearCommand(color));
    436 }
    437 
    438 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    439     this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
    440 }
    441 
    442 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    443     this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
    444 }
    445 
    446 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    447     this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
    448 }
    449 
    450 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
    451     this->addDrawCommand(new SkClipRegionCommand(region, op));
    452 }
    453 
    454 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
    455     switch (matrix.getType()) {
    456         case SkMatrix::kTranslate_Mask:
    457             this->addDrawCommand(new SkTranslateCommand(matrix.getTranslateX(),
    458                                                         matrix.getTranslateY()));
    459             break;
    460         case SkMatrix::kScale_Mask:
    461             this->addDrawCommand(new SkScaleCommand(matrix.getScaleX(),
    462                                                     matrix.getScaleY()));
    463             break;
    464         default:
    465             this->addDrawCommand(new SkConcatCommand(matrix));
    466             break;
    467     }
    468 
    469     this->INHERITED::didConcat(matrix);
    470 }
    471 
    472 void SkDebugCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
    473                                SkScalar top, const SkPaint* paint = NULL) {
    474     this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
    475 }
    476 
    477 void SkDebugCanvas::drawBitmapRectToRect(const SkBitmap& bitmap,
    478                                          const SkRect* src, const SkRect& dst,
    479                                          const SkPaint* paint,
    480                                          SkCanvas::DrawBitmapRectFlags flags) {
    481     this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
    482 }
    483 
    484 void SkDebugCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
    485                                      const SkMatrix& matrix, const SkPaint* paint) {
    486     this->addDrawCommand(new SkDrawBitmapMatrixCommand(bitmap, matrix, paint));
    487 }
    488 
    489 void SkDebugCanvas::drawBitmapNine(const SkBitmap& bitmap,
    490         const SkIRect& center, const SkRect& dst, const SkPaint* paint) {
    491     this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
    492 }
    493 
    494 void SkDebugCanvas::drawData(const void* data, size_t length) {
    495     this->addDrawCommand(new SkDrawDataCommand(data, length));
    496 }
    497 
    498 void SkDebugCanvas::beginCommentGroup(const char* description) {
    499     this->addDrawCommand(new SkBeginCommentGroupCommand(description));
    500 }
    501 
    502 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
    503     this->addDrawCommand(new SkCommentCommand(kywd, value));
    504 }
    505 
    506 void SkDebugCanvas::endCommentGroup() {
    507     this->addDrawCommand(new SkEndCommentGroupCommand());
    508 }
    509 
    510 void SkDebugCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
    511     this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
    512 }
    513 
    514 void SkDebugCanvas::drawPaint(const SkPaint& paint) {
    515     this->addDrawCommand(new SkDrawPaintCommand(paint));
    516 }
    517 
    518 void SkDebugCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
    519     this->addDrawCommand(new SkDrawPathCommand(path, paint));
    520 }
    521 
    522 void SkDebugCanvas::onDrawPicture(const SkPicture* picture) {
    523     this->addDrawCommand(new SkDrawPictureCommand(picture));
    524 }
    525 
    526 void SkDebugCanvas::drawPoints(PointMode mode, size_t count,
    527                                const SkPoint pts[], const SkPaint& paint) {
    528     this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
    529 }
    530 
    531 void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
    532                                   const SkPaint& paint) {
    533     this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint));
    534 }
    535 
    536 void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
    537                                    SkScalar constY, const SkPaint& paint) {
    538     this->addDrawCommand(
    539         new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint));
    540 }
    541 
    542 void SkDebugCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
    543     // NOTE(chudy): Messing up when renamed to DrawRect... Why?
    544     addDrawCommand(new SkDrawRectCommand(rect, paint));
    545 }
    546 
    547 void SkDebugCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
    548     this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
    549 }
    550 
    551 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
    552                                  const SkPaint& paint) {
    553     this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
    554 }
    555 
    556 void SkDebugCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
    557                                const SkPaint* paint = NULL) {
    558     this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint));
    559 }
    560 
    561 void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
    562                                const SkPaint& paint) {
    563     this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint));
    564 }
    565 
    566 void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
    567                                      const SkMatrix* matrix, const SkPaint& paint) {
    568     this->addDrawCommand(
    569         new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint));
    570 }
    571 
    572 void SkDebugCanvas::drawVertices(VertexMode vmode, int vertexCount,
    573         const SkPoint vertices[], const SkPoint texs[], const SkColor colors[],
    574         SkXfermode*, const uint16_t indices[], int indexCount,
    575         const SkPaint& paint) {
    576     this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices,
    577                          texs, colors, NULL, indices, indexCount, paint));
    578 }
    579 
    580 void SkDebugCanvas::onPushCull(const SkRect& cullRect) {
    581     this->addDrawCommand(new SkPushCullCommand(cullRect));
    582 }
    583 
    584 void SkDebugCanvas::onPopCull() {
    585     this->addDrawCommand(new SkPopCullCommand());
    586 }
    587 
    588 void SkDebugCanvas::willRestore() {
    589     this->addDrawCommand(new SkRestoreCommand());
    590     this->INHERITED::willRestore();
    591 }
    592 
    593 void SkDebugCanvas::willSave(SaveFlags flags) {
    594     this->addDrawCommand(new SkSaveCommand(flags));
    595     this->INHERITED::willSave(flags);
    596 }
    597 
    598 SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint,
    599                                                          SaveFlags flags) {
    600     this->addDrawCommand(new SkSaveLayerCommand(bounds, paint, flags));
    601     this->INHERITED::willSaveLayer(bounds, paint, flags);
    602     // No need for a full layer.
    603     return kNoLayer_SaveLayerStrategy;
    604 }
    605 
    606 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
    607     this->addDrawCommand(new SkSetMatrixCommand(matrix));
    608     this->INHERITED::didSetMatrix(matrix);
    609 }
    610 
    611 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
    612     SkASSERT(index < fCommandVector.count());
    613     fCommandVector[index]->setVisible(toggle);
    614 }
    615 
    616 static const char* gFillTypeStrs[] = {
    617     "kWinding_FillType",
    618     "kEvenOdd_FillType",
    619     "kInverseWinding_FillType",
    620     "kInverseEvenOdd_FillType"
    621 };
    622 
    623 static const char* gOpStrs[] = {
    624     "kDifference_PathOp",
    625     "kIntersect_PathOp",
    626     "kUnion_PathOp",
    627     "kXor_PathOp",
    628     "kReverseDifference_PathOp",
    629 };
    630 
    631 static const char kHTML4SpaceIndent[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
    632 
    633 void SkDebugCanvas::outputScalar(SkScalar num) {
    634     if (num == (int) num) {
    635         fClipStackData.appendf("%d", (int) num);
    636     } else {
    637         SkString str;
    638         str.printf("%1.9g", num);
    639         int width = (int) str.size();
    640         const char* cStr = str.c_str();
    641         while (cStr[width - 1] == '0') {
    642             --width;
    643         }
    644         str.resize(width);
    645         fClipStackData.appendf("%sf", str.c_str());
    646     }
    647 }
    648 
    649 void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) {
    650     for (int index = 0; index < count; ++index) {
    651         this->outputScalar(pts[index].fX);
    652         fClipStackData.appendf(", ");
    653         this->outputScalar(pts[index].fY);
    654         if (index + 1 < count) {
    655             fClipStackData.appendf(", ");
    656         }
    657     }
    658 }
    659 
    660 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
    661     this->outputPointsCommon(pts, count);
    662     fClipStackData.appendf(");<br>");
    663 }
    664 
    665 void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) {
    666     this->outputPointsCommon(pts, 2);
    667     fClipStackData.appendf(", ");
    668     this->outputScalar(weight);
    669     fClipStackData.appendf(");<br>");
    670 }
    671 
    672 void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) {
    673     SkPath::RawIter iter(path);
    674     SkPath::FillType fillType = path.getFillType();
    675     fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName);
    676     fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName,
    677             gFillTypeStrs[fillType]);
    678     iter.setPath(path);
    679     uint8_t verb;
    680     SkPoint pts[4];
    681     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    682         switch (verb) {
    683             case SkPath::kMove_Verb:
    684                 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
    685                 this->outputPoints(&pts[0], 1);
    686                 continue;
    687             case SkPath::kLine_Verb:
    688                 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
    689                 this->outputPoints(&pts[1], 1);
    690                 break;
    691             case SkPath::kQuad_Verb:
    692                 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
    693                 this->outputPoints(&pts[1], 2);
    694                 break;
    695             case SkPath::kConic_Verb:
    696                 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
    697                 this->outputConicPoints(&pts[1], iter.conicWeight());
    698                 break;
    699             case SkPath::kCubic_Verb:
    700                 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
    701                 this->outputPoints(&pts[1], 3);
    702                 break;
    703             case SkPath::kClose_Verb:
    704                 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
    705                 break;
    706             default:
    707                 SkDEBUGFAIL("bad verb");
    708                 return;
    709         }
    710     }
    711 }
    712 
    713 void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand,
    714                                      SkRegion::Op elementOp) {
    715     if (elementOp == SkRegion::kReplace_Op) {
    716         if (!lastClipStackData(devPath)) {
    717             fSaveDevPath = operand;
    718         }
    719         fCalledAddStackData = false;
    720     } else {
    721         fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter,"
    722             " const char* filename) {<br>");
    723         addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path");
    724         addPathData(operand, "pathB");
    725         fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>",
    726             kHTML4SpaceIndent, gOpStrs[elementOp]);
    727         fClipStackData.appendf("}<br>");
    728         fCalledAddStackData = true;
    729     }
    730 }
    731 
    732 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
    733     if (fCalledAddStackData) {
    734         fClipStackData.appendf("<br>");
    735         addPathData(devPath, "pathOut");
    736         return true;
    737     }
    738     return false;
    739 }
    740