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 "SkDevice.h"
     14 #include "SkPaintFilterCanvas.h"
     15 #include "SkXfermode.h"
     16 
     17 namespace {
     18 
     19 class OverdrawXfermode : public SkXfermode {
     20 public:
     21     SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override {
     22         // This table encodes the color progression of the overdraw visualization
     23         static const SkPMColor gTable[] = {
     24             SkPackARGB32(0x00, 0x00, 0x00, 0x00),
     25             SkPackARGB32(0xFF, 128, 158, 255),
     26             SkPackARGB32(0xFF, 170, 185, 212),
     27             SkPackARGB32(0xFF, 213, 195, 170),
     28             SkPackARGB32(0xFF, 255, 192, 127),
     29             SkPackARGB32(0xFF, 255, 185, 85),
     30             SkPackARGB32(0xFF, 255, 165, 42),
     31             SkPackARGB32(0xFF, 255, 135, 0),
     32             SkPackARGB32(0xFF, 255,  95, 0),
     33             SkPackARGB32(0xFF, 255,  50, 0),
     34             SkPackARGB32(0xFF, 255,  0, 0)
     35         };
     36 
     37 
     38         int idx;
     39         if (SkColorGetR(dst) < 64) { // 0
     40             idx = 0;
     41         } else if (SkColorGetG(dst) < 25) { // 10
     42             idx = 9;  // cap at 9 for upcoming increment
     43         } else if ((SkColorGetB(dst)+21)/42 > 0) { // 1-6
     44             idx = 7 - (SkColorGetB(dst)+21)/42;
     45         } else { // 7-9
     46             idx = 10 - (SkColorGetG(dst)+22)/45;
     47         }
     48         ++idx;
     49         SkASSERT(idx < (int)SK_ARRAY_COUNT(gTable));
     50 
     51         return gTable[idx];
     52     }
     53 
     54     Factory getFactory() const override { return NULL; }
     55 #ifndef SK_IGNORE_TO_STRING
     56     void toString(SkString* str) const override { str->set("OverdrawXfermode"); }
     57 #endif
     58 };
     59 
     60 class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
     61 public:
     62     DebugPaintFilterCanvas(int width, int height, bool overdrawViz, bool overrideFilterQuality,
     63                            SkFilterQuality quality)
     64         : INHERITED(width, height)
     65         , fOverdrawXfermode(overdrawViz ? SkNEW(OverdrawXfermode) : NULL)
     66         , fOverrideFilterQuality(overrideFilterQuality)
     67         , fFilterQuality(quality) { }
     68 
     69 protected:
     70     void onFilterPaint(SkPaint* paint, Type) const override {
     71         if (NULL != fOverdrawXfermode.get()) {
     72             paint->setAntiAlias(false);
     73             paint->setXfermode(fOverdrawXfermode.get());
     74         }
     75 
     76         if (fOverrideFilterQuality) {
     77             paint->setFilterQuality(fFilterQuality);
     78         }
     79     }
     80 
     81     void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
     82         // We need to replay the picture onto this canvas in order to filter its internal paints.
     83         this->SkCanvas::onDrawPicture(picture, matrix, paint);
     84     }
     85 
     86 private:
     87     SkAutoTUnref<SkXfermode> fOverdrawXfermode;
     88 
     89     bool fOverrideFilterQuality;
     90     SkFilterQuality fFilterQuality;
     91 
     92     typedef SkPaintFilterCanvas INHERITED;
     93 };
     94 
     95 }
     96 
     97 SkDebugCanvas::SkDebugCanvas(int width, int height)
     98         : INHERITED(width, height)
     99         , fPicture(NULL)
    100         , fFilter(false)
    101         , fMegaVizMode(false)
    102         , fOverdrawViz(false)
    103         , fOverrideFilterQuality(false)
    104         , fFilterQuality(kNone_SkFilterQuality) {
    105     fUserMatrix.reset();
    106 
    107     // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
    108     // operations. This can lead to problems in the debugger which expects all
    109     // the operations in the captured skp to appear in the debug canvas. To
    110     // circumvent this we create a wide open clip here (an empty clip rect
    111     // is not sufficient).
    112     // Internally, the SkRect passed to clipRect is converted to an SkIRect and
    113     // rounded out. The following code creates a nearly maximal rect that will
    114     // not get collapsed by the coming conversions (Due to precision loss the
    115     // inset has to be surprisingly large).
    116     SkIRect largeIRect = SkIRect::MakeLargest();
    117     largeIRect.inset(1024, 1024);
    118     SkRect large = SkRect::Make(largeIRect);
    119 #ifdef SK_DEBUG
    120     SkASSERT(!large.roundOut().isEmpty());
    121 #endif
    122     // call the base class' version to avoid adding a draw command
    123     this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
    124 }
    125 
    126 SkDebugCanvas::~SkDebugCanvas() {
    127     fCommandVector.deleteAll();
    128 }
    129 
    130 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
    131     fCommandVector.push(command);
    132 }
    133 
    134 void SkDebugCanvas::draw(SkCanvas* canvas) {
    135     if (!fCommandVector.isEmpty()) {
    136         this->drawTo(canvas, fCommandVector.count() - 1);
    137     }
    138 }
    139 
    140 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
    141     canvas->concat(fUserMatrix);
    142 }
    143 
    144 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
    145     SkBitmap bitmap;
    146     bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
    147 
    148     SkCanvas canvas(bitmap);
    149     canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
    150     this->applyUserTransform(&canvas);
    151 
    152     int layer = 0;
    153     SkColor prev = bitmap.getColor(0,0);
    154     for (int i = 0; i < index; i++) {
    155         if (fCommandVector[i]->isVisible()) {
    156             fCommandVector[i]->setUserMatrix(fUserMatrix);
    157             fCommandVector[i]->execute(&canvas);
    158         }
    159         if (prev != bitmap.getColor(0,0)) {
    160             layer = i;
    161         }
    162         prev = bitmap.getColor(0,0);
    163     }
    164     return layer;
    165 }
    166 
    167 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
    168 public:
    169     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
    170 
    171     void clipRect(const SkRect& r, SkRegion::Op, bool doAA) override {
    172         SkPaint p;
    173         p.setColor(SK_ColorRED);
    174         p.setStyle(SkPaint::kStroke_Style);
    175         p.setAntiAlias(doAA);
    176         fCanvas->drawRect(r, p);
    177     }
    178     void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) override {
    179         SkPaint p;
    180         p.setColor(SK_ColorGREEN);
    181         p.setStyle(SkPaint::kStroke_Style);
    182         p.setAntiAlias(doAA);
    183         fCanvas->drawRRect(rr, p);
    184     }
    185     void clipPath(const SkPath& path, SkRegion::Op, bool doAA) override {
    186         SkPaint p;
    187         p.setColor(SK_ColorBLUE);
    188         p.setStyle(SkPaint::kStroke_Style);
    189         p.setAntiAlias(doAA);
    190         fCanvas->drawPath(path, p);
    191     }
    192 
    193 protected:
    194     SkCanvas* fCanvas;
    195 
    196 private:
    197     typedef SkCanvas::ClipVisitor INHERITED;
    198 };
    199 
    200 // set up the saveLayer commands so that the active ones
    201 // return true in their 'active' method
    202 void SkDebugCanvas::markActiveCommands(int index) {
    203     fActiveLayers.rewind();
    204 
    205     for (int i = 0; i < fCommandVector.count(); ++i) {
    206         fCommandVector[i]->setActive(false);
    207     }
    208 
    209     for (int i = 0; i < index; ++i) {
    210         SkDrawCommand::Action result = fCommandVector[i]->action();
    211         if (SkDrawCommand::kPushLayer_Action == result) {
    212             fActiveLayers.push(fCommandVector[i]);
    213         } else if (SkDrawCommand::kPopLayer_Action == result) {
    214             fActiveLayers.pop();
    215         }
    216     }
    217 
    218     for (int i = 0; i < fActiveLayers.count(); ++i) {
    219         fActiveLayers[i]->setActive(true);
    220     }
    221 
    222 }
    223 
    224 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
    225     SkASSERT(!fCommandVector.isEmpty());
    226     SkASSERT(index < fCommandVector.count());
    227 
    228     int saveCount = canvas->save();
    229 
    230     SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()),
    231                                        SkIntToScalar(canvas->getBaseLayerSize().height()));
    232 
    233     bool pathOpsMode = getAllowSimplifyClip();
    234     canvas->setAllowSimplifyClip(pathOpsMode);
    235     canvas->clear(SK_ColorTRANSPARENT);
    236     canvas->resetMatrix();
    237     if (!windowRect.isEmpty()) {
    238         canvas->clipRect(windowRect, SkRegion::kReplace_Op);
    239     }
    240     this->applyUserTransform(canvas);
    241 
    242     if (fPaintFilterCanvas) {
    243         fPaintFilterCanvas->addCanvas(canvas);
    244         canvas = fPaintFilterCanvas.get();
    245     }
    246 
    247     if (fMegaVizMode) {
    248         this->markActiveCommands(index);
    249     }
    250 
    251     for (int i = 0; i <= index; i++) {
    252         if (i == index && fFilter) {
    253             canvas->clear(0xAAFFFFFF);
    254         }
    255 
    256         if (fCommandVector[i]->isVisible()) {
    257             if (fMegaVizMode && fCommandVector[i]->active()) {
    258                 // "active" commands execute their visualization behaviors:
    259                 //     All active saveLayers get replaced with saves so all draws go to the
    260                 //     visible canvas.
    261                 //     All active culls draw their cull box
    262                 fCommandVector[i]->vizExecute(canvas);
    263             } else {
    264                 fCommandVector[i]->setUserMatrix(fUserMatrix);
    265                 fCommandVector[i]->execute(canvas);
    266             }
    267         }
    268     }
    269 
    270     if (fMegaVizMode) {
    271         canvas->save();
    272         // nuke the CTM
    273         canvas->resetMatrix();
    274         // turn off clipping
    275         if (!windowRect.isEmpty()) {
    276             SkRect r = windowRect;
    277             r.outset(SK_Scalar1, SK_Scalar1);
    278             canvas->clipRect(r, SkRegion::kReplace_Op);
    279         }
    280         // visualize existing clips
    281         SkDebugClipVisitor visitor(canvas);
    282 
    283         canvas->replayClips(&visitor);
    284 
    285         canvas->restore();
    286     }
    287     if (pathOpsMode) {
    288         this->resetClipStackData();
    289         const SkClipStack* clipStack = canvas->getClipStack();
    290         SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
    291         const SkClipStack::Element* element;
    292         SkPath devPath;
    293         while ((element = iter.next())) {
    294             SkClipStack::Element::Type type = element->getType();
    295             SkPath operand;
    296             if (type != SkClipStack::Element::kEmpty_Type) {
    297                element->asPath(&operand);
    298             }
    299             SkRegion::Op elementOp = element->getOp();
    300             this->addClipStackData(devPath, operand, elementOp);
    301             if (elementOp == SkRegion::kReplace_Op) {
    302                 devPath = operand;
    303             } else {
    304                 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
    305             }
    306         }
    307         this->lastClipStackData(devPath);
    308     }
    309     fMatrix = canvas->getTotalMatrix();
    310     if (!canvas->getClipDeviceBounds(&fClip)) {
    311         fClip.setEmpty();
    312     }
    313 
    314     canvas->restoreToCount(saveCount);
    315 
    316     if (fPaintFilterCanvas) {
    317         fPaintFilterCanvas->removeAll();
    318     }
    319 }
    320 
    321 void SkDebugCanvas::deleteDrawCommandAt(int index) {
    322     SkASSERT(index < fCommandVector.count());
    323     delete fCommandVector[index];
    324     fCommandVector.remove(index);
    325 }
    326 
    327 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
    328     SkASSERT(index < fCommandVector.count());
    329     return fCommandVector[index];
    330 }
    331 
    332 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
    333     SkASSERT(index < fCommandVector.count());
    334     delete fCommandVector[index];
    335     fCommandVector[index] = command;
    336 }
    337 
    338 const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
    339     SkASSERT(index < fCommandVector.count());
    340     return fCommandVector[index]->Info();
    341 }
    342 
    343 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
    344     SkASSERT(index < fCommandVector.count());
    345     return fCommandVector[index]->isVisible();
    346 }
    347 
    348 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
    349     return fCommandVector;
    350 }
    351 
    352 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
    353     return fCommandVector;
    354 }
    355 
    356 void SkDebugCanvas::updatePaintFilterCanvas() {
    357     if (!fOverdrawViz && !fOverrideFilterQuality) {
    358         fPaintFilterCanvas.reset(NULL);
    359         return;
    360     }
    361 
    362     const SkImageInfo info = this->imageInfo();
    363     fPaintFilterCanvas.reset(SkNEW_ARGS(DebugPaintFilterCanvas, (info.width(),
    364                                                                  info.height(),
    365                                                                  fOverdrawViz,
    366                                                                  fOverrideFilterQuality,
    367                                                                  fFilterQuality)));
    368 }
    369 
    370 void SkDebugCanvas::setOverdrawViz(bool overdrawViz) {
    371     if (fOverdrawViz == overdrawViz) {
    372         return;
    373     }
    374 
    375     fOverdrawViz = overdrawViz;
    376     this->updatePaintFilterCanvas();
    377 }
    378 
    379 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
    380     if (fOverrideFilterQuality == overrideTexFiltering && fFilterQuality == quality) {
    381         return;
    382     }
    383 
    384     fOverrideFilterQuality = overrideTexFiltering;
    385     fFilterQuality = quality;
    386     this->updatePaintFilterCanvas();
    387 }
    388 
    389 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    390     this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
    391 }
    392 
    393 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    394     this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
    395 }
    396 
    397 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
    398     this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
    399 }
    400 
    401 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
    402     this->addDrawCommand(new SkClipRegionCommand(region, op));
    403 }
    404 
    405 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
    406     this->addDrawCommand(new SkConcatCommand(matrix));
    407     this->INHERITED::didConcat(matrix);
    408 }
    409 
    410 void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
    411                                  SkScalar top, const SkPaint* paint) {
    412     this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
    413 }
    414 
    415 void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
    416                                      const SkPaint* paint, DrawBitmapRectFlags flags) {
    417     this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags));
    418 }
    419 
    420 void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
    421                                      const SkRect& dst, const SkPaint* paint) {
    422     this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
    423 }
    424 
    425 void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
    426                                 const SkPaint* paint) {
    427     SkDebugf("SkDebugCanvas::onDrawImage unimplemented\n");
    428 }
    429 
    430 void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
    431                                     const SkPaint* paint) {
    432     SkDebugf("SkDebugCanvas::onDrawImageRect unimplemented\n");
    433 }
    434 
    435 void SkDebugCanvas::beginCommentGroup(const char* description) {
    436     this->addDrawCommand(new SkBeginCommentGroupCommand(description));
    437 }
    438 
    439 void SkDebugCanvas::addComment(const char* kywd, const char* value) {
    440     this->addDrawCommand(new SkCommentCommand(kywd, value));
    441 }
    442 
    443 void SkDebugCanvas::endCommentGroup() {
    444     this->addDrawCommand(new SkEndCommentGroupCommand());
    445 }
    446 
    447 void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
    448     this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
    449 }
    450 
    451 void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
    452     this->addDrawCommand(new SkDrawPaintCommand(paint));
    453 }
    454 
    455 void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
    456     this->addDrawCommand(new SkDrawPathCommand(path, paint));
    457 }
    458 
    459 void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
    460                                   const SkMatrix* matrix,
    461                                   const SkPaint* paint) {
    462     this->addDrawCommand(new SkBeginDrawPictureCommand(picture, matrix, paint));
    463     this->INHERITED::onDrawPicture(picture, matrix, paint);
    464     this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
    465 }
    466 
    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));
    470 }
    471 
    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));
    475 }
    476 
    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));
    481 }
    482 
    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));
    486 }
    487 
    488 void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
    489     this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
    490 }
    491 
    492 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
    493                                  const SkPaint& paint) {
    494     this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
    495 }
    496 
    497 void SkDebugCanvas::onDrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
    498     this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint));
    499 }
    500 
    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));
    504 }
    505 
    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));
    510 }
    511 
    512 void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
    513                                    const SkPaint& paint) {
    514     this->addDrawCommand(new SkDrawTextBlobCommand(blob, x, y, paint));
    515 }
    516 
    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));
    521 }
    522 
    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));
    529 }
    530 
    531 void SkDebugCanvas::willRestore() {
    532     this->addDrawCommand(new SkRestoreCommand());
    533     this->INHERITED::willRestore();
    534 }
    535 
    536 void SkDebugCanvas::willSave() {
    537     this->addDrawCommand(new SkSaveCommand());
    538     this->INHERITED::willSave();
    539 }
    540 
    541 SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint,
    542                                                          SaveFlags flags) {
    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;
    547 }
    548 
    549 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
    550     this->addDrawCommand(new SkSetMatrixCommand(matrix));
    551     this->INHERITED::didSetMatrix(matrix);
    552 }
    553 
    554 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
    555     SkASSERT(index < fCommandVector.count());
    556     fCommandVector[index]->setVisible(toggle);
    557 }
    558 
    559 static const char* gFillTypeStrs[] = {
    560     "kWinding_FillType",
    561     "kEvenOdd_FillType",
    562     "kInverseWinding_FillType",
    563     "kInverseEvenOdd_FillType"
    564 };
    565 
    566 static const char* gOpStrs[] = {
    567     "kDifference_PathOp",
    568     "kIntersect_PathOp",
    569     "kUnion_PathOp",
    570     "kXor_PathOp",
    571     "kReverseDifference_PathOp",
    572 };
    573 
    574 static const char kHTML4SpaceIndent[] = "&nbsp;&nbsp;&nbsp;&nbsp;";
    575 
    576 void SkDebugCanvas::outputScalar(SkScalar num) {
    577     if (num == (int) num) {
    578         fClipStackData.appendf("%d", (int) num);
    579     } else {
    580         SkString str;
    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') {
    585             --width;
    586         }
    587         str.resize(width);
    588         fClipStackData.appendf("%sf", str.c_str());
    589     }
    590 }
    591 
    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(", ");
    599         }
    600     }
    601 }
    602 
    603 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
    604     this->outputPointsCommon(pts, count);
    605     fClipStackData.appendf(");<br>");
    606 }
    607 
    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>");
    613 }
    614 
    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]);
    621     iter.setPath(path);
    622     uint8_t verb;
    623     SkPoint pts[4];
    624     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    625         switch (verb) {
    626             case SkPath::kMove_Verb:
    627                 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
    628                 this->outputPoints(&pts[0], 1);
    629                 continue;
    630             case SkPath::kLine_Verb:
    631                 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
    632                 this->outputPoints(&pts[1], 1);
    633                 break;
    634             case SkPath::kQuad_Verb:
    635                 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
    636                 this->outputPoints(&pts[1], 2);
    637                 break;
    638             case SkPath::kConic_Verb:
    639                 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
    640                 this->outputConicPoints(&pts[1], iter.conicWeight());
    641                 break;
    642             case SkPath::kCubic_Verb:
    643                 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
    644                 this->outputPoints(&pts[1], 3);
    645                 break;
    646             case SkPath::kClose_Verb:
    647                 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
    648                 break;
    649             default:
    650                 SkDEBUGFAIL("bad verb");
    651                 return;
    652         }
    653     }
    654 }
    655 
    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;
    661         }
    662         fCalledAddStackData = false;
    663     } else {
    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;
    672     }
    673 }
    674 
    675 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
    676     if (fCalledAddStackData) {
    677         fClipStackData.appendf("<br>");
    678         addPathData(devPath, "pathOut");
    679         return true;
    680     }
    681     return false;
    682 }
    683