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 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 
     51 SkDebugCanvas::~SkDebugCanvas() {
     52     fCommandVector.deleteAll();
     53     SkSafeUnref(fOverdrawFilter);
     54     SkSafeUnref(fTexOverrideFilter);
     55 }
     56 
     57 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
     58     command->setOffset(this->getOpID());
     59     fCommandVector.push(command);
     60 }
     61 
     62 void SkDebugCanvas::draw(SkCanvas* canvas) {
     63     if (!fCommandVector.isEmpty()) {
     64         this->drawTo(canvas, fCommandVector.count() - 1);
     65     }
     66 }
     67 
     68 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
     69     canvas->concat(fUserMatrix);
     70 }
     71 
     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:
     96     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 
    121     virtual Factory getFactory() const SK_OVERRIDE { return NULL; }
    122 #ifndef SK_IGNORE_TO_STRING
    123     virtual void toString(SkString* str) const { str->set("OverdrawXfermode"); }
    124 #endif
    125 };
    126 
    127 class SkOverdrawFilter : public SkDrawFilter {
    128 public:
    129     SkOverdrawFilter() {
    130         fXferMode = SkNEW(OverdrawXfermode);
    131     }
    132 
    133     virtual ~SkOverdrawFilter() {
    134         delete fXferMode;
    135     }
    136 
    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:
    153     SkTexOverrideFilter() : fFilterLevel(SkPaint::kNone_FilterLevel) {
    154     }
    155 
    156     void setFilterLevel(SkPaint::FilterLevel filterLevel) {
    157         fFilterLevel = filterLevel;
    158     }
    159 
    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:
    174     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
    175 
    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     }
    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     }
    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
    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 
    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 
    359 void SkDebugCanvas::deleteDrawCommandAt(int index) {
    360     SkASSERT(index < fCommandVector.count());
    361     delete fCommandVector[index];
    362     fCommandVector.remove(index);
    363 }
    364 
    365 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
    366     SkASSERT(index < fCommandVector.count());
    367     return fCommandVector[index];
    368 }
    369 
    370 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
    371     SkASSERT(index < fCommandVector.count());
    372     delete fCommandVector[index];
    373     fCommandVector[index] = command;
    374 }
    375 
    376 SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) {
    377     SkASSERT(index < fCommandVector.count());
    378     return fCommandVector[index]->Info();
    379 }
    380 
    381 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
    382     SkASSERT(index < fCommandVector.count());
    383     return fCommandVector[index]->isVisible();
    384 }
    385 
    386 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
    387     return fCommandVector;
    388 }
    389 
    390 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
    391     return fCommandVector;
    392 }
    393 
    394 // TODO(chudy): Free command string memory.
    395 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 
    405 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 
    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 
    424 void SkDebugCanvas::clear(SkColor color) {
    425     this->addDrawCommand(new SkClearCommand(color));
    426 }
    427 
    428 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    429     this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
    430 }
    431 
    432 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    433     this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
    434 }
    435 
    436 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    437     this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
    438 }
    439 
    440 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
    441     this->addDrawCommand(new SkClipRegionCommand(region, op));
    442 }
    443 
    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 
    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 
    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 
    474 void SkDebugCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
    475                                      const SkMatrix& matrix, const SkPaint* paint) {
    476     this->addDrawCommand(new SkDrawBitmapMatrixCommand(bitmap, matrix, paint));
    477 }
    478 
    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 
    484 void SkDebugCanvas::drawData(const void* data, size_t length) {
    485     this->addDrawCommand(new SkDrawDataCommand(data, length));
    486 }
    487 
    488 void SkDebugCanvas::beginCommentGroup(const char* description) {
    489     this->addDrawCommand(new SkBeginCommentGroupCommand(description));
    490 }
    491 
    492 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
    493     this->addDrawCommand(new SkCommentCommand(kywd, value));
    494 }
    495 
    496 void SkDebugCanvas::endCommentGroup() {
    497     this->addDrawCommand(new SkEndCommentGroupCommand());
    498 }
    499 
    500 void SkDebugCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
    501     this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
    502 }
    503 
    504 void SkDebugCanvas::drawPaint(const SkPaint& paint) {
    505     this->addDrawCommand(new SkDrawPaintCommand(paint));
    506 }
    507 
    508 void SkDebugCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
    509     this->addDrawCommand(new SkDrawPathCommand(path, paint));
    510 }
    511 
    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 
    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 
    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 
    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 
    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 
    539 void SkDebugCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
    540     this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
    541 }
    542 
    543 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
    544                                  const SkPaint& paint) {
    545     this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
    546 }
    547 
    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 
    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 
    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 
    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 
    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 
    577 void SkDebugCanvas::onPushCull(const SkRect& cullRect) {
    578     this->addDrawCommand(new SkPushCullCommand(cullRect));
    579 }
    580 
    581 void SkDebugCanvas::onPopCull() {
    582     this->addDrawCommand(new SkPopCullCommand());
    583 }
    584 
    585 void SkDebugCanvas::willRestore() {
    586     this->addDrawCommand(new SkRestoreCommand());
    587     this->INHERITED::willRestore();
    588 }
    589 
    590 void SkDebugCanvas::willSave() {
    591     this->addDrawCommand(new SkSaveCommand());
    592     this->INHERITED::willSave();
    593 }
    594 
    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 
    603 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
    604     this->addDrawCommand(new SkSetMatrixCommand(matrix));
    605     this->INHERITED::didSetMatrix(matrix);
    606 }
    607 
    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[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
    629 
    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 
    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 
    657 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
    658     this->outputPointsCommon(pts, count);
    659     fClipStackData.appendf(");<br>");
    660 }
    661 
    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 
    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 
    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 
    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