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